roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     
3044     
3045     var onStop = function(e){
3046         dragEl = null;
3047         clearProc();
3048     };
3049     
3050     var triggerRefresh = function(){
3051         if(ddm.dragCurrent){
3052              ddm.refreshCache(ddm.dragCurrent.groups);
3053         }
3054     };
3055     
3056     var doScroll = function(){
3057         if(ddm.dragCurrent){
3058             var dds = Roo.dd.ScrollManager;
3059             if(!dds.animate){
3060                 if(proc.el.scroll(proc.dir, dds.increment)){
3061                     triggerRefresh();
3062                 }
3063             }else{
3064                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3065             }
3066         }
3067     };
3068     
3069     var clearProc = function(){
3070         if(proc.id){
3071             clearInterval(proc.id);
3072         }
3073         proc.id = 0;
3074         proc.el = null;
3075         proc.dir = "";
3076     };
3077     
3078     var startProc = function(el, dir){
3079         clearProc();
3080         proc.el = el;
3081         proc.dir = dir;
3082         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3083     };
3084     
3085     var onFire = function(e, isDrop){
3086         Roo.log('scroll onfire');
3087         if(isDrop || !ddm.dragCurrent){ return; }
3088         var dds = Roo.dd.ScrollManager;
3089         if(!dragEl || dragEl != ddm.dragCurrent){
3090             dragEl = ddm.dragCurrent;
3091             // refresh regions on drag start
3092             dds.refreshCache();
3093         }
3094         
3095         var xy = Roo.lib.Event.getXY(e);
3096         var pt = new Roo.lib.Point(xy[0], xy[1]);
3097         for(var id in els){
3098             var el = els[id], r = el._region;
3099             if(r && r.contains(pt) && el.isScrollable()){
3100                 if(r.bottom - pt.y <= dds.thresh){
3101                     if(proc.el != el){
3102                         startProc(el, "down");
3103                     }
3104                     return;
3105                 }else if(r.right - pt.x <= dds.thresh){
3106                     if(proc.el != el){
3107                         startProc(el, "left");
3108                     }
3109                     return;
3110                 }else if(pt.y - r.top <= dds.thresh){
3111                     if(proc.el != el){
3112                         startProc(el, "up");
3113                     }
3114                     return;
3115                 }else if(pt.x - r.left <= dds.thresh){
3116                     if(proc.el != el){
3117                         startProc(el, "right");
3118                     }
3119                     return;
3120                 }
3121             }
3122         }
3123         clearProc();
3124     };
3125     
3126     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3127     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3128     
3129     return {
3130         /**
3131          * Registers new overflow element(s) to auto scroll
3132          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3133          */
3134         register : function(el){
3135             if(el instanceof Array){
3136                 for(var i = 0, len = el.length; i < len; i++) {
3137                         this.register(el[i]);
3138                 }
3139             }else{
3140                 el = Roo.get(el);
3141                 els[el.id] = el;
3142             }
3143             Roo.dd.ScrollManager.els = els;
3144         },
3145         
3146         /**
3147          * Unregisters overflow element(s) so they are no longer scrolled
3148          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3149          */
3150         unregister : function(el){
3151             if(el instanceof Array){
3152                 for(var i = 0, len = el.length; i < len; i++) {
3153                         this.unregister(el[i]);
3154                 }
3155             }else{
3156                 el = Roo.get(el);
3157                 delete els[el.id];
3158             }
3159         },
3160         
3161         /**
3162          * The number of pixels from the edge of a container the pointer needs to be to 
3163          * trigger scrolling (defaults to 25)
3164          * @type Number
3165          */
3166         thresh : 25,
3167         
3168         /**
3169          * The number of pixels to scroll in each scroll increment (defaults to 50)
3170          * @type Number
3171          */
3172         increment : 100,
3173         
3174         /**
3175          * The frequency of scrolls in milliseconds (defaults to 500)
3176          * @type Number
3177          */
3178         frequency : 500,
3179         
3180         /**
3181          * True to animate the scroll (defaults to true)
3182          * @type Boolean
3183          */
3184         animate: true,
3185         
3186         /**
3187          * The animation duration in seconds - 
3188          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3189          * @type Number
3190          */
3191         animDuration: .4,
3192         
3193         /**
3194          * Manually trigger a cache refresh.
3195          */
3196         refreshCache : function(){
3197             for(var id in els){
3198                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3199                     els[id]._region = els[id].getRegion();
3200                 }
3201             }
3202         }
3203     };
3204 }();/*
3205  * Based on:
3206  * Ext JS Library 1.1.1
3207  * Copyright(c) 2006-2007, Ext JS, LLC.
3208  *
3209  * Originally Released Under LGPL - original licence link has changed is not relivant.
3210  *
3211  * Fork - LGPL
3212  * <script type="text/javascript">
3213  */
3214  
3215
3216 /**
3217  * @class Roo.dd.Registry
3218  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3219  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3220  * @singleton
3221  */
3222 Roo.dd.Registry = function(){
3223     var elements = {}; 
3224     var handles = {}; 
3225     var autoIdSeed = 0;
3226
3227     var getId = function(el, autogen){
3228         if(typeof el == "string"){
3229             return el;
3230         }
3231         var id = el.id;
3232         if(!id && autogen !== false){
3233             id = "roodd-" + (++autoIdSeed);
3234             el.id = id;
3235         }
3236         return id;
3237     };
3238     
3239     return {
3240     /**
3241      * Register a drag drop element
3242      * @param {String|HTMLElement} element The id or DOM node to register
3243      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3244      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3245      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3246      * populated in the data object (if applicable):
3247      * <pre>
3248 Value      Description<br />
3249 ---------  ------------------------------------------<br />
3250 handles    Array of DOM nodes that trigger dragging<br />
3251            for the element being registered<br />
3252 isHandle   True if the element passed in triggers<br />
3253            dragging itself, else false
3254 </pre>
3255      */
3256         register : function(el, data){
3257             data = data || {};
3258             if(typeof el == "string"){
3259                 el = document.getElementById(el);
3260             }
3261             data.ddel = el;
3262             elements[getId(el)] = data;
3263             if(data.isHandle !== false){
3264                 handles[data.ddel.id] = data;
3265             }
3266             if(data.handles){
3267                 var hs = data.handles;
3268                 for(var i = 0, len = hs.length; i < len; i++){
3269                         handles[getId(hs[i])] = data;
3270                 }
3271             }
3272         },
3273
3274     /**
3275      * Unregister a drag drop element
3276      * @param {String|HTMLElement}  element The id or DOM node to unregister
3277      */
3278         unregister : function(el){
3279             var id = getId(el, false);
3280             var data = elements[id];
3281             if(data){
3282                 delete elements[id];
3283                 if(data.handles){
3284                     var hs = data.handles;
3285                     for(var i = 0, len = hs.length; i < len; i++){
3286                         delete handles[getId(hs[i], false)];
3287                     }
3288                 }
3289             }
3290         },
3291
3292     /**
3293      * Returns the handle registered for a DOM Node by id
3294      * @param {String|HTMLElement} id The DOM node or id to look up
3295      * @return {Object} handle The custom handle data
3296      */
3297         getHandle : function(id){
3298             if(typeof id != "string"){ // must be element?
3299                 id = id.id;
3300             }
3301             return handles[id];
3302         },
3303
3304     /**
3305      * Returns the handle that is registered for the DOM node that is the target of the event
3306      * @param {Event} e The event
3307      * @return {Object} handle The custom handle data
3308      */
3309         getHandleFromEvent : function(e){
3310             var t = Roo.lib.Event.getTarget(e);
3311             return t ? handles[t.id] : null;
3312         },
3313
3314     /**
3315      * Returns a custom data object that is registered for a DOM node by id
3316      * @param {String|HTMLElement} id The DOM node or id to look up
3317      * @return {Object} data The custom data
3318      */
3319         getTarget : function(id){
3320             if(typeof id != "string"){ // must be element?
3321                 id = id.id;
3322             }
3323             return elements[id];
3324         },
3325
3326     /**
3327      * Returns a custom data object that is registered for the DOM node that is the target of the event
3328      * @param {Event} e The event
3329      * @return {Object} data The custom data
3330      */
3331         getTargetFromEvent : function(e){
3332             var t = Roo.lib.Event.getTarget(e);
3333             return t ? elements[t.id] || handles[t.id] : null;
3334         }
3335     };
3336 }();/*
3337  * Based on:
3338  * Ext JS Library 1.1.1
3339  * Copyright(c) 2006-2007, Ext JS, LLC.
3340  *
3341  * Originally Released Under LGPL - original licence link has changed is not relivant.
3342  *
3343  * Fork - LGPL
3344  * <script type="text/javascript">
3345  */
3346  
3347
3348 /**
3349  * @class Roo.dd.StatusProxy
3350  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3351  * default drag proxy used by all Roo.dd components.
3352  * @constructor
3353  * @param {Object} config
3354  */
3355 Roo.dd.StatusProxy = function(config){
3356     Roo.apply(this, config);
3357     this.id = this.id || Roo.id();
3358     this.el = new Roo.Layer({
3359         dh: {
3360             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3361                 {tag: "div", cls: "x-dd-drop-icon"},
3362                 {tag: "div", cls: "x-dd-drag-ghost"}
3363             ]
3364         }, 
3365         shadow: !config || config.shadow !== false
3366     });
3367     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3368     this.dropStatus = this.dropNotAllowed;
3369 };
3370
3371 Roo.dd.StatusProxy.prototype = {
3372     /**
3373      * @cfg {String} dropAllowed
3374      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3375      */
3376     dropAllowed : "x-dd-drop-ok",
3377     /**
3378      * @cfg {String} dropNotAllowed
3379      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3380      */
3381     dropNotAllowed : "x-dd-drop-nodrop",
3382
3383     /**
3384      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3385      * over the current target element.
3386      * @param {String} cssClass The css class for the new drop status indicator image
3387      */
3388     setStatus : function(cssClass){
3389         cssClass = cssClass || this.dropNotAllowed;
3390         if(this.dropStatus != cssClass){
3391             this.el.replaceClass(this.dropStatus, cssClass);
3392             this.dropStatus = cssClass;
3393         }
3394     },
3395
3396     /**
3397      * Resets the status indicator to the default dropNotAllowed value
3398      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3399      */
3400     reset : function(clearGhost){
3401         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3402         this.dropStatus = this.dropNotAllowed;
3403         if(clearGhost){
3404             this.ghost.update("");
3405         }
3406     },
3407
3408     /**
3409      * Updates the contents of the ghost element
3410      * @param {String} html The html that will replace the current innerHTML of the ghost element
3411      */
3412     update : function(html){
3413         if(typeof html == "string"){
3414             this.ghost.update(html);
3415         }else{
3416             this.ghost.update("");
3417             html.style.margin = "0";
3418             this.ghost.dom.appendChild(html);
3419         }
3420         // ensure float = none set?? cant remember why though.
3421         var el = this.ghost.dom.firstChild;
3422                 if(el){
3423                         Roo.fly(el).setStyle('float', 'none');
3424                 }
3425     },
3426     
3427     /**
3428      * Returns the underlying proxy {@link Roo.Layer}
3429      * @return {Roo.Layer} el
3430     */
3431     getEl : function(){
3432         return this.el;
3433     },
3434
3435     /**
3436      * Returns the ghost element
3437      * @return {Roo.Element} el
3438      */
3439     getGhost : function(){
3440         return this.ghost;
3441     },
3442
3443     /**
3444      * Hides the proxy
3445      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3446      */
3447     hide : function(clear){
3448         this.el.hide();
3449         if(clear){
3450             this.reset(true);
3451         }
3452     },
3453
3454     /**
3455      * Stops the repair animation if it's currently running
3456      */
3457     stop : function(){
3458         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3459             this.anim.stop();
3460         }
3461     },
3462
3463     /**
3464      * Displays this proxy
3465      */
3466     show : function(){
3467         this.el.show();
3468     },
3469
3470     /**
3471      * Force the Layer to sync its shadow and shim positions to the element
3472      */
3473     sync : function(){
3474         this.el.sync();
3475     },
3476
3477     /**
3478      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3479      * invalid drop operation by the item being dragged.
3480      * @param {Array} xy The XY position of the element ([x, y])
3481      * @param {Function} callback The function to call after the repair is complete
3482      * @param {Object} scope The scope in which to execute the callback
3483      */
3484     repair : function(xy, callback, scope){
3485         this.callback = callback;
3486         this.scope = scope;
3487         if(xy && this.animRepair !== false){
3488             this.el.addClass("x-dd-drag-repair");
3489             this.el.hideUnders(true);
3490             this.anim = this.el.shift({
3491                 duration: this.repairDuration || .5,
3492                 easing: 'easeOut',
3493                 xy: xy,
3494                 stopFx: true,
3495                 callback: this.afterRepair,
3496                 scope: this
3497             });
3498         }else{
3499             this.afterRepair();
3500         }
3501     },
3502
3503     // private
3504     afterRepair : function(){
3505         this.hide(true);
3506         if(typeof this.callback == "function"){
3507             this.callback.call(this.scope || this);
3508         }
3509         this.callback = null;
3510         this.scope = null;
3511     }
3512 };/*
3513  * Based on:
3514  * Ext JS Library 1.1.1
3515  * Copyright(c) 2006-2007, Ext JS, LLC.
3516  *
3517  * Originally Released Under LGPL - original licence link has changed is not relivant.
3518  *
3519  * Fork - LGPL
3520  * <script type="text/javascript">
3521  */
3522
3523 /**
3524  * @class Roo.dd.DragSource
3525  * @extends Roo.dd.DDProxy
3526  * A simple class that provides the basic implementation needed to make any element draggable.
3527  * @constructor
3528  * @param {String/HTMLElement/Element} el The container element
3529  * @param {Object} config
3530  */
3531 Roo.dd.DragSource = function(el, config){
3532     this.el = Roo.get(el);
3533     this.dragData = {};
3534     
3535     Roo.apply(this, config);
3536     
3537     if(!this.proxy){
3538         this.proxy = new Roo.dd.StatusProxy();
3539     }
3540
3541     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3542           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3543     
3544     this.dragging = false;
3545 };
3546
3547 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3548     /**
3549      * @cfg {String} dropAllowed
3550      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3551      */
3552     dropAllowed : "x-dd-drop-ok",
3553     /**
3554      * @cfg {String} dropNotAllowed
3555      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3556      */
3557     dropNotAllowed : "x-dd-drop-nodrop",
3558
3559     /**
3560      * Returns the data object associated with this drag source
3561      * @return {Object} data An object containing arbitrary data
3562      */
3563     getDragData : function(e){
3564         return this.dragData;
3565     },
3566
3567     // private
3568     onDragEnter : function(e, id){
3569         var target = Roo.dd.DragDropMgr.getDDById(id);
3570         this.cachedTarget = target;
3571         if(this.beforeDragEnter(target, e, id) !== false){
3572             if(target.isNotifyTarget){
3573                 var status = target.notifyEnter(this, e, this.dragData);
3574                 this.proxy.setStatus(status);
3575             }else{
3576                 this.proxy.setStatus(this.dropAllowed);
3577             }
3578             
3579             if(this.afterDragEnter){
3580                 /**
3581                  * An empty function by default, but provided so that you can perform a custom action
3582                  * when the dragged item enters the drop target by providing an implementation.
3583                  * @param {Roo.dd.DragDrop} target The drop target
3584                  * @param {Event} e The event object
3585                  * @param {String} id The id of the dragged element
3586                  * @method afterDragEnter
3587                  */
3588                 this.afterDragEnter(target, e, id);
3589             }
3590         }
3591     },
3592
3593     /**
3594      * An empty function by default, but provided so that you can perform a custom action
3595      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3596      * @param {Roo.dd.DragDrop} target The drop target
3597      * @param {Event} e The event object
3598      * @param {String} id The id of the dragged element
3599      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3600      */
3601     beforeDragEnter : function(target, e, id){
3602         return true;
3603     },
3604
3605     // private
3606     alignElWithMouse: function() {
3607         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3608         this.proxy.sync();
3609     },
3610
3611     // private
3612     onDragOver : function(e, id){
3613         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3614         if(this.beforeDragOver(target, e, id) !== false){
3615             if(target.isNotifyTarget){
3616                 var status = target.notifyOver(this, e, this.dragData);
3617                 this.proxy.setStatus(status);
3618             }
3619
3620             if(this.afterDragOver){
3621                 /**
3622                  * An empty function by default, but provided so that you can perform a custom action
3623                  * while the dragged item is over the drop target by providing an implementation.
3624                  * @param {Roo.dd.DragDrop} target The drop target
3625                  * @param {Event} e The event object
3626                  * @param {String} id The id of the dragged element
3627                  * @method afterDragOver
3628                  */
3629                 this.afterDragOver(target, e, id);
3630             }
3631         }
3632     },
3633
3634     /**
3635      * An empty function by default, but provided so that you can perform a custom action
3636      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3637      * @param {Roo.dd.DragDrop} target The drop target
3638      * @param {Event} e The event object
3639      * @param {String} id The id of the dragged element
3640      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3641      */
3642     beforeDragOver : function(target, e, id){
3643         return true;
3644     },
3645
3646     // private
3647     onDragOut : function(e, id){
3648         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3649         if(this.beforeDragOut(target, e, id) !== false){
3650             if(target.isNotifyTarget){
3651                 target.notifyOut(this, e, this.dragData);
3652             }
3653             this.proxy.reset();
3654             if(this.afterDragOut){
3655                 /**
3656                  * An empty function by default, but provided so that you can perform a custom action
3657                  * after the dragged item is dragged out of the target without dropping.
3658                  * @param {Roo.dd.DragDrop} target The drop target
3659                  * @param {Event} e The event object
3660                  * @param {String} id The id of the dragged element
3661                  * @method afterDragOut
3662                  */
3663                 this.afterDragOut(target, e, id);
3664             }
3665         }
3666         this.cachedTarget = null;
3667     },
3668
3669     /**
3670      * An empty function by default, but provided so that you can perform a custom action before the dragged
3671      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3672      * @param {Roo.dd.DragDrop} target The drop target
3673      * @param {Event} e The event object
3674      * @param {String} id The id of the dragged element
3675      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3676      */
3677     beforeDragOut : function(target, e, id){
3678         return true;
3679     },
3680     
3681     // private
3682     onDragDrop : function(e, id){
3683         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3684         if(this.beforeDragDrop(target, e, id) !== false){
3685             if(target.isNotifyTarget){
3686                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3687                     this.onValidDrop(target, e, id);
3688                 }else{
3689                     this.onInvalidDrop(target, e, id);
3690                 }
3691             }else{
3692                 this.onValidDrop(target, e, id);
3693             }
3694             
3695             if(this.afterDragDrop){
3696                 /**
3697                  * An empty function by default, but provided so that you can perform a custom action
3698                  * after a valid drag drop has occurred by providing an implementation.
3699                  * @param {Roo.dd.DragDrop} target The drop target
3700                  * @param {Event} e The event object
3701                  * @param {String} id The id of the dropped element
3702                  * @method afterDragDrop
3703                  */
3704                 this.afterDragDrop(target, e, id);
3705             }
3706         }
3707         delete this.cachedTarget;
3708     },
3709
3710     /**
3711      * An empty function by default, but provided so that you can perform a custom action before the dragged
3712      * item is dropped onto the target and optionally cancel the onDragDrop.
3713      * @param {Roo.dd.DragDrop} target The drop target
3714      * @param {Event} e The event object
3715      * @param {String} id The id of the dragged element
3716      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3717      */
3718     beforeDragDrop : function(target, e, id){
3719         return true;
3720     },
3721
3722     // private
3723     onValidDrop : function(target, e, id){
3724         this.hideProxy();
3725         if(this.afterValidDrop){
3726             /**
3727              * An empty function by default, but provided so that you can perform a custom action
3728              * after a valid drop has occurred by providing an implementation.
3729              * @param {Object} target The target DD 
3730              * @param {Event} e The event object
3731              * @param {String} id The id of the dropped element
3732              * @method afterInvalidDrop
3733              */
3734             this.afterValidDrop(target, e, id);
3735         }
3736     },
3737
3738     // private
3739     getRepairXY : function(e, data){
3740         return this.el.getXY();  
3741     },
3742
3743     // private
3744     onInvalidDrop : function(target, e, id){
3745         this.beforeInvalidDrop(target, e, id);
3746         if(this.cachedTarget){
3747             if(this.cachedTarget.isNotifyTarget){
3748                 this.cachedTarget.notifyOut(this, e, this.dragData);
3749             }
3750             this.cacheTarget = null;
3751         }
3752         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3753
3754         if(this.afterInvalidDrop){
3755             /**
3756              * An empty function by default, but provided so that you can perform a custom action
3757              * after an invalid drop has occurred by providing an implementation.
3758              * @param {Event} e The event object
3759              * @param {String} id The id of the dropped element
3760              * @method afterInvalidDrop
3761              */
3762             this.afterInvalidDrop(e, id);
3763         }
3764     },
3765
3766     // private
3767     afterRepair : function(){
3768         if(Roo.enableFx){
3769             this.el.highlight(this.hlColor || "c3daf9");
3770         }
3771         this.dragging = false;
3772     },
3773
3774     /**
3775      * An empty function by default, but provided so that you can perform a custom action after an invalid
3776      * drop has occurred.
3777      * @param {Roo.dd.DragDrop} target The drop target
3778      * @param {Event} e The event object
3779      * @param {String} id The id of the dragged element
3780      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3781      */
3782     beforeInvalidDrop : function(target, e, id){
3783         return true;
3784     },
3785
3786     // private
3787     handleMouseDown : function(e){
3788         if(this.dragging) {
3789             return;
3790         }
3791         var data = this.getDragData(e);
3792         if(data && this.onBeforeDrag(data, e) !== false){
3793             this.dragData = data;
3794             this.proxy.stop();
3795             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3796         } 
3797     },
3798
3799     /**
3800      * An empty function by default, but provided so that you can perform a custom action before the initial
3801      * drag event begins and optionally cancel it.
3802      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3803      * @param {Event} e The event object
3804      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3805      */
3806     onBeforeDrag : function(data, e){
3807         return true;
3808     },
3809
3810     /**
3811      * An empty function by default, but provided so that you can perform a custom action once the initial
3812      * drag event has begun.  The drag cannot be canceled from this function.
3813      * @param {Number} x The x position of the click on the dragged object
3814      * @param {Number} y The y position of the click on the dragged object
3815      */
3816     onStartDrag : Roo.emptyFn,
3817
3818     // private - YUI override
3819     startDrag : function(x, y){
3820         this.proxy.reset();
3821         this.dragging = true;
3822         this.proxy.update("");
3823         this.onInitDrag(x, y);
3824         this.proxy.show();
3825     },
3826
3827     // private
3828     onInitDrag : function(x, y){
3829         var clone = this.el.dom.cloneNode(true);
3830         clone.id = Roo.id(); // prevent duplicate ids
3831         this.proxy.update(clone);
3832         this.onStartDrag(x, y);
3833         return true;
3834     },
3835
3836     /**
3837      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3838      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3839      */
3840     getProxy : function(){
3841         return this.proxy;  
3842     },
3843
3844     /**
3845      * Hides the drag source's {@link Roo.dd.StatusProxy}
3846      */
3847     hideProxy : function(){
3848         this.proxy.hide();  
3849         this.proxy.reset(true);
3850         this.dragging = false;
3851     },
3852
3853     // private
3854     triggerCacheRefresh : function(){
3855         Roo.dd.DDM.refreshCache(this.groups);
3856     },
3857
3858     // private - override to prevent hiding
3859     b4EndDrag: function(e) {
3860     },
3861
3862     // private - override to prevent moving
3863     endDrag : function(e){
3864         this.onEndDrag(this.dragData, e);
3865     },
3866
3867     // private
3868     onEndDrag : function(data, e){
3869     },
3870     
3871     // private - pin to cursor
3872     autoOffset : function(x, y) {
3873         this.setDelta(-12, -20);
3874     }    
3875 });/*
3876  * Based on:
3877  * Ext JS Library 1.1.1
3878  * Copyright(c) 2006-2007, Ext JS, LLC.
3879  *
3880  * Originally Released Under LGPL - original licence link has changed is not relivant.
3881  *
3882  * Fork - LGPL
3883  * <script type="text/javascript">
3884  */
3885
3886
3887 /**
3888  * @class Roo.dd.DropTarget
3889  * @extends Roo.dd.DDTarget
3890  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3891  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3892  * @constructor
3893  * @param {String/HTMLElement/Element} el The container element
3894  * @param {Object} config
3895  */
3896 Roo.dd.DropTarget = function(el, config){
3897     this.el = Roo.get(el);
3898     
3899     var listeners = false; ;
3900     if (config && config.listeners) {
3901         listeners= config.listeners;
3902         delete config.listeners;
3903     }
3904     Roo.apply(this, config);
3905     
3906     if(this.containerScroll){
3907         Roo.dd.ScrollManager.register(this.el);
3908     }
3909     this.addEvents( {
3910          /**
3911          * @scope Roo.dd.DropTarget
3912          */
3913          
3914          /**
3915          * @event enter
3916          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3917          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3918          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3919          * 
3920          * IMPORTANT : it should set this.overClass and this.dropAllowed
3921          * 
3922          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3923          * @param {Event} e The event
3924          * @param {Object} data An object containing arbitrary data supplied by the drag source
3925          */
3926         "enter" : true,
3927         
3928          /**
3929          * @event over
3930          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3931          * This method will be called on every mouse movement while the drag source is over the drop target.
3932          * This default implementation simply returns the dropAllowed config value.
3933          * 
3934          * IMPORTANT : it should set this.dropAllowed
3935          * 
3936          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3937          * @param {Event} e The event
3938          * @param {Object} data An object containing arbitrary data supplied by the drag source
3939          
3940          */
3941         "over" : true,
3942         /**
3943          * @event out
3944          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3945          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3946          * overClass (if any) from the drop element.
3947          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3948          * @param {Event} e The event
3949          * @param {Object} data An object containing arbitrary data supplied by the drag source
3950          */
3951          "out" : true,
3952          
3953         /**
3954          * @event drop
3955          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3956          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3957          * implementation that does something to process the drop event and returns true so that the drag source's
3958          * repair action does not run.
3959          * 
3960          * IMPORTANT : it should set this.success
3961          * 
3962          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3963          * @param {Event} e The event
3964          * @param {Object} data An object containing arbitrary data supplied by the drag source
3965         */
3966          "drop" : true
3967     });
3968             
3969      
3970     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3971         this.el.dom, 
3972         this.ddGroup || this.group,
3973         {
3974             isTarget: true,
3975             listeners : listeners || {} 
3976            
3977         
3978         }
3979     );
3980
3981 };
3982
3983 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3984     /**
3985      * @cfg {String} overClass
3986      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3987      */
3988      /**
3989      * @cfg {String} ddGroup
3990      * The drag drop group to handle drop events for
3991      */
3992      
3993     /**
3994      * @cfg {String} dropAllowed
3995      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3996      */
3997     dropAllowed : "x-dd-drop-ok",
3998     /**
3999      * @cfg {String} dropNotAllowed
4000      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4001      */
4002     dropNotAllowed : "x-dd-drop-nodrop",
4003     /**
4004      * @cfg {boolean} success
4005      * set this after drop listener.. 
4006      */
4007     success : false,
4008     /**
4009      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4010      * if the drop point is valid for over/enter..
4011      */
4012     valid : false,
4013     // private
4014     isTarget : true,
4015
4016     // private
4017     isNotifyTarget : true,
4018     
4019     /**
4020      * @hide
4021      */
4022     notifyEnter : function(dd, e, data)
4023     {
4024         this.valid = true;
4025         this.fireEvent('enter', dd, e, data);
4026         if(this.overClass){
4027             this.el.addClass(this.overClass);
4028         }
4029         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4030             this.valid ? this.dropAllowed : this.dropNotAllowed
4031         );
4032     },
4033
4034     /**
4035      * @hide
4036      */
4037     notifyOver : function(dd, e, data)
4038     {
4039         this.valid = true;
4040         this.fireEvent('over', dd, e, data);
4041         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4042             this.valid ? this.dropAllowed : this.dropNotAllowed
4043         );
4044     },
4045
4046     /**
4047      * @hide
4048      */
4049     notifyOut : function(dd, e, data)
4050     {
4051         this.fireEvent('out', dd, e, data);
4052         if(this.overClass){
4053             this.el.removeClass(this.overClass);
4054         }
4055     },
4056
4057     /**
4058      * @hide
4059      */
4060     notifyDrop : function(dd, e, data)
4061     {
4062         this.success = false;
4063         this.fireEvent('drop', dd, e, data);
4064         return this.success;
4065     }
4066 });/*
4067  * Based on:
4068  * Ext JS Library 1.1.1
4069  * Copyright(c) 2006-2007, Ext JS, LLC.
4070  *
4071  * Originally Released Under LGPL - original licence link has changed is not relivant.
4072  *
4073  * Fork - LGPL
4074  * <script type="text/javascript">
4075  */
4076
4077
4078 /**
4079  * @class Roo.dd.DragZone
4080  * @extends Roo.dd.DragSource
4081  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4082  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4083  * @constructor
4084  * @param {String/HTMLElement/Element} el The container element
4085  * @param {Object} config
4086  */
4087 Roo.dd.DragZone = function(el, config){
4088     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4089     if(this.containerScroll){
4090         Roo.dd.ScrollManager.register(this.el);
4091     }
4092 };
4093
4094 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4095     /**
4096      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4097      * for auto scrolling during drag operations.
4098      */
4099     /**
4100      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4101      * method after a failed drop (defaults to "c3daf9" - light blue)
4102      */
4103
4104     /**
4105      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4106      * for a valid target to drag based on the mouse down. Override this method
4107      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4108      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4109      * @param {EventObject} e The mouse down event
4110      * @return {Object} The dragData
4111      */
4112     getDragData : function(e){
4113         return Roo.dd.Registry.getHandleFromEvent(e);
4114     },
4115     
4116     /**
4117      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4118      * this.dragData.ddel
4119      * @param {Number} x The x position of the click on the dragged object
4120      * @param {Number} y The y position of the click on the dragged object
4121      * @return {Boolean} true to continue the drag, false to cancel
4122      */
4123     onInitDrag : function(x, y){
4124         this.proxy.update(this.dragData.ddel.cloneNode(true));
4125         this.onStartDrag(x, y);
4126         return true;
4127     },
4128     
4129     /**
4130      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4131      */
4132     afterRepair : function(){
4133         if(Roo.enableFx){
4134             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4135         }
4136         this.dragging = false;
4137     },
4138
4139     /**
4140      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4141      * the XY of this.dragData.ddel
4142      * @param {EventObject} e The mouse up event
4143      * @return {Array} The xy location (e.g. [100, 200])
4144      */
4145     getRepairXY : function(e){
4146         return Roo.Element.fly(this.dragData.ddel).getXY();  
4147     }
4148 });/*
4149  * Based on:
4150  * Ext JS Library 1.1.1
4151  * Copyright(c) 2006-2007, Ext JS, LLC.
4152  *
4153  * Originally Released Under LGPL - original licence link has changed is not relivant.
4154  *
4155  * Fork - LGPL
4156  * <script type="text/javascript">
4157  */
4158 /**
4159  * @class Roo.dd.DropZone
4160  * @extends Roo.dd.DropTarget
4161  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4162  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4163  * @constructor
4164  * @param {String/HTMLElement/Element} el The container element
4165  * @param {Object} config
4166  */
4167 Roo.dd.DropZone = function(el, config){
4168     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4169 };
4170
4171 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4172     /**
4173      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4174      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4175      * provide your own custom lookup.
4176      * @param {Event} e The event
4177      * @return {Object} data The custom data
4178      */
4179     getTargetFromEvent : function(e){
4180         return Roo.dd.Registry.getTargetFromEvent(e);
4181     },
4182
4183     /**
4184      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4185      * that it has registered.  This method has no default implementation and should be overridden to provide
4186      * node-specific processing if necessary.
4187      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4188      * {@link #getTargetFromEvent} for this node)
4189      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4190      * @param {Event} e The event
4191      * @param {Object} data An object containing arbitrary data supplied by the drag source
4192      */
4193     onNodeEnter : function(n, dd, e, data){
4194         
4195     },
4196
4197     /**
4198      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4199      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4200      * overridden to provide the proper feedback.
4201      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4202      * {@link #getTargetFromEvent} for this node)
4203      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4204      * @param {Event} e The event
4205      * @param {Object} data An object containing arbitrary data supplied by the drag source
4206      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4207      * underlying {@link Roo.dd.StatusProxy} can be updated
4208      */
4209     onNodeOver : function(n, dd, e, data){
4210         return this.dropAllowed;
4211     },
4212
4213     /**
4214      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4215      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4216      * node-specific processing if necessary.
4217      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4218      * {@link #getTargetFromEvent} for this node)
4219      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4220      * @param {Event} e The event
4221      * @param {Object} data An object containing arbitrary data supplied by the drag source
4222      */
4223     onNodeOut : function(n, dd, e, data){
4224         
4225     },
4226
4227     /**
4228      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4229      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4230      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4231      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4232      * {@link #getTargetFromEvent} for this node)
4233      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4234      * @param {Event} e The event
4235      * @param {Object} data An object containing arbitrary data supplied by the drag source
4236      * @return {Boolean} True if the drop was valid, else false
4237      */
4238     onNodeDrop : function(n, dd, e, data){
4239         return false;
4240     },
4241
4242     /**
4243      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4244      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4245      * it should be overridden to provide the proper feedback if necessary.
4246      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4247      * @param {Event} e The event
4248      * @param {Object} data An object containing arbitrary data supplied by the drag source
4249      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4250      * underlying {@link Roo.dd.StatusProxy} can be updated
4251      */
4252     onContainerOver : function(dd, e, data){
4253         return this.dropNotAllowed;
4254     },
4255
4256     /**
4257      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4258      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4259      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4260      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4261      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4262      * @param {Event} e The event
4263      * @param {Object} data An object containing arbitrary data supplied by the drag source
4264      * @return {Boolean} True if the drop was valid, else false
4265      */
4266     onContainerDrop : function(dd, e, data){
4267         return false;
4268     },
4269
4270     /**
4271      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4272      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4273      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4274      * you should override this method and provide a custom implementation.
4275      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4276      * @param {Event} e The event
4277      * @param {Object} data An object containing arbitrary data supplied by the drag source
4278      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4279      * underlying {@link Roo.dd.StatusProxy} can be updated
4280      */
4281     notifyEnter : function(dd, e, data){
4282         return this.dropNotAllowed;
4283     },
4284
4285     /**
4286      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4287      * This method will be called on every mouse movement while the drag source is over the drop zone.
4288      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4289      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4290      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4291      * registered node, it will call {@link #onContainerOver}.
4292      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4293      * @param {Event} e The event
4294      * @param {Object} data An object containing arbitrary data supplied by the drag source
4295      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4296      * underlying {@link Roo.dd.StatusProxy} can be updated
4297      */
4298     notifyOver : function(dd, e, data){
4299         var n = this.getTargetFromEvent(e);
4300         if(!n){ // not over valid drop target
4301             if(this.lastOverNode){
4302                 this.onNodeOut(this.lastOverNode, dd, e, data);
4303                 this.lastOverNode = null;
4304             }
4305             return this.onContainerOver(dd, e, data);
4306         }
4307         if(this.lastOverNode != n){
4308             if(this.lastOverNode){
4309                 this.onNodeOut(this.lastOverNode, dd, e, data);
4310             }
4311             this.onNodeEnter(n, dd, e, data);
4312             this.lastOverNode = n;
4313         }
4314         return this.onNodeOver(n, dd, e, data);
4315     },
4316
4317     /**
4318      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4319      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4320      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4321      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4322      * @param {Event} e The event
4323      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4324      */
4325     notifyOut : function(dd, e, data){
4326         if(this.lastOverNode){
4327             this.onNodeOut(this.lastOverNode, dd, e, data);
4328             this.lastOverNode = null;
4329         }
4330     },
4331
4332     /**
4333      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4334      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4335      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4336      * otherwise it will call {@link #onContainerDrop}.
4337      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4338      * @param {Event} e The event
4339      * @param {Object} data An object containing arbitrary data supplied by the drag source
4340      * @return {Boolean} True if the drop was valid, else false
4341      */
4342     notifyDrop : function(dd, e, data){
4343         if(this.lastOverNode){
4344             this.onNodeOut(this.lastOverNode, dd, e, data);
4345             this.lastOverNode = null;
4346         }
4347         var n = this.getTargetFromEvent(e);
4348         return n ?
4349             this.onNodeDrop(n, dd, e, data) :
4350             this.onContainerDrop(dd, e, data);
4351     },
4352
4353     // private
4354     triggerCacheRefresh : function(){
4355         Roo.dd.DDM.refreshCache(this.groups);
4356     }  
4357 });/*
4358  * Based on:
4359  * Ext JS Library 1.1.1
4360  * Copyright(c) 2006-2007, Ext JS, LLC.
4361  *
4362  * Originally Released Under LGPL - original licence link has changed is not relivant.
4363  *
4364  * Fork - LGPL
4365  * <script type="text/javascript">
4366  */
4367
4368
4369 /**
4370  * @class Roo.data.SortTypes
4371  * @singleton
4372  * Defines the default sorting (casting?) comparison functions used when sorting data.
4373  */
4374 Roo.data.SortTypes = {
4375     /**
4376      * Default sort that does nothing
4377      * @param {Mixed} s The value being converted
4378      * @return {Mixed} The comparison value
4379      */
4380     none : function(s){
4381         return s;
4382     },
4383     
4384     /**
4385      * The regular expression used to strip tags
4386      * @type {RegExp}
4387      * @property
4388      */
4389     stripTagsRE : /<\/?[^>]+>/gi,
4390     
4391     /**
4392      * Strips all HTML tags to sort on text only
4393      * @param {Mixed} s The value being converted
4394      * @return {String} The comparison value
4395      */
4396     asText : function(s){
4397         return String(s).replace(this.stripTagsRE, "");
4398     },
4399     
4400     /**
4401      * Strips all HTML tags to sort on text only - Case insensitive
4402      * @param {Mixed} s The value being converted
4403      * @return {String} The comparison value
4404      */
4405     asUCText : function(s){
4406         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4407     },
4408     
4409     /**
4410      * Case insensitive string
4411      * @param {Mixed} s The value being converted
4412      * @return {String} The comparison value
4413      */
4414     asUCString : function(s) {
4415         return String(s).toUpperCase();
4416     },
4417     
4418     /**
4419      * Date sorting
4420      * @param {Mixed} s The value being converted
4421      * @return {Number} The comparison value
4422      */
4423     asDate : function(s) {
4424         if(!s){
4425             return 0;
4426         }
4427         if(s instanceof Date){
4428             return s.getTime();
4429         }
4430         return Date.parse(String(s));
4431     },
4432     
4433     /**
4434      * Float sorting
4435      * @param {Mixed} s The value being converted
4436      * @return {Float} The comparison value
4437      */
4438     asFloat : function(s) {
4439         var val = parseFloat(String(s).replace(/,/g, ""));
4440         if(isNaN(val)) val = 0;
4441         return val;
4442     },
4443     
4444     /**
4445      * Integer sorting
4446      * @param {Mixed} s The value being converted
4447      * @return {Number} The comparison value
4448      */
4449     asInt : function(s) {
4450         var val = parseInt(String(s).replace(/,/g, ""));
4451         if(isNaN(val)) val = 0;
4452         return val;
4453     }
4454 };/*
4455  * Based on:
4456  * Ext JS Library 1.1.1
4457  * Copyright(c) 2006-2007, Ext JS, LLC.
4458  *
4459  * Originally Released Under LGPL - original licence link has changed is not relivant.
4460  *
4461  * Fork - LGPL
4462  * <script type="text/javascript">
4463  */
4464
4465 /**
4466 * @class Roo.data.Record
4467  * Instances of this class encapsulate both record <em>definition</em> information, and record
4468  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4469  * to access Records cached in an {@link Roo.data.Store} object.<br>
4470  * <p>
4471  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4472  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4473  * objects.<br>
4474  * <p>
4475  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4476  * @constructor
4477  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4478  * {@link #create}. The parameters are the same.
4479  * @param {Array} data An associative Array of data values keyed by the field name.
4480  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4481  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4482  * not specified an integer id is generated.
4483  */
4484 Roo.data.Record = function(data, id){
4485     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4486     this.data = data;
4487 };
4488
4489 /**
4490  * Generate a constructor for a specific record layout.
4491  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4492  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4493  * Each field definition object may contain the following properties: <ul>
4494  * <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,
4495  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4496  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4497  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4498  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4499  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4500  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4501  * this may be omitted.</p></li>
4502  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4503  * <ul><li>auto (Default, implies no conversion)</li>
4504  * <li>string</li>
4505  * <li>int</li>
4506  * <li>float</li>
4507  * <li>boolean</li>
4508  * <li>date</li></ul></p></li>
4509  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4510  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4511  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4512  * by the Reader into an object that will be stored in the Record. It is passed the
4513  * following parameters:<ul>
4514  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4515  * </ul></p></li>
4516  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4517  * </ul>
4518  * <br>usage:<br><pre><code>
4519 var TopicRecord = Roo.data.Record.create(
4520     {name: 'title', mapping: 'topic_title'},
4521     {name: 'author', mapping: 'username'},
4522     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4523     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4524     {name: 'lastPoster', mapping: 'user2'},
4525     {name: 'excerpt', mapping: 'post_text'}
4526 );
4527
4528 var myNewRecord = new TopicRecord({
4529     title: 'Do my job please',
4530     author: 'noobie',
4531     totalPosts: 1,
4532     lastPost: new Date(),
4533     lastPoster: 'Animal',
4534     excerpt: 'No way dude!'
4535 });
4536 myStore.add(myNewRecord);
4537 </code></pre>
4538  * @method create
4539  * @static
4540  */
4541 Roo.data.Record.create = function(o){
4542     var f = function(){
4543         f.superclass.constructor.apply(this, arguments);
4544     };
4545     Roo.extend(f, Roo.data.Record);
4546     var p = f.prototype;
4547     p.fields = new Roo.util.MixedCollection(false, function(field){
4548         return field.name;
4549     });
4550     for(var i = 0, len = o.length; i < len; i++){
4551         p.fields.add(new Roo.data.Field(o[i]));
4552     }
4553     f.getField = function(name){
4554         return p.fields.get(name);  
4555     };
4556     return f;
4557 };
4558
4559 Roo.data.Record.AUTO_ID = 1000;
4560 Roo.data.Record.EDIT = 'edit';
4561 Roo.data.Record.REJECT = 'reject';
4562 Roo.data.Record.COMMIT = 'commit';
4563
4564 Roo.data.Record.prototype = {
4565     /**
4566      * Readonly flag - true if this record has been modified.
4567      * @type Boolean
4568      */
4569     dirty : false,
4570     editing : false,
4571     error: null,
4572     modified: null,
4573
4574     // private
4575     join : function(store){
4576         this.store = store;
4577     },
4578
4579     /**
4580      * Set the named field to the specified value.
4581      * @param {String} name The name of the field to set.
4582      * @param {Object} value The value to set the field to.
4583      */
4584     set : function(name, value){
4585         if(this.data[name] == value){
4586             return;
4587         }
4588         this.dirty = true;
4589         if(!this.modified){
4590             this.modified = {};
4591         }
4592         if(typeof this.modified[name] == 'undefined'){
4593             this.modified[name] = this.data[name];
4594         }
4595         this.data[name] = value;
4596         if(!this.editing && this.store){
4597             this.store.afterEdit(this);
4598         }       
4599     },
4600
4601     /**
4602      * Get the value of the named field.
4603      * @param {String} name The name of the field to get the value of.
4604      * @return {Object} The value of the field.
4605      */
4606     get : function(name){
4607         return this.data[name]; 
4608     },
4609
4610     // private
4611     beginEdit : function(){
4612         this.editing = true;
4613         this.modified = {}; 
4614     },
4615
4616     // private
4617     cancelEdit : function(){
4618         this.editing = false;
4619         delete this.modified;
4620     },
4621
4622     // private
4623     endEdit : function(){
4624         this.editing = false;
4625         if(this.dirty && this.store){
4626             this.store.afterEdit(this);
4627         }
4628     },
4629
4630     /**
4631      * Usually called by the {@link Roo.data.Store} which owns the Record.
4632      * Rejects all changes made to the Record since either creation, or the last commit operation.
4633      * Modified fields are reverted to their original values.
4634      * <p>
4635      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4636      * of reject operations.
4637      */
4638     reject : function(){
4639         var m = this.modified;
4640         for(var n in m){
4641             if(typeof m[n] != "function"){
4642                 this.data[n] = m[n];
4643             }
4644         }
4645         this.dirty = false;
4646         delete this.modified;
4647         this.editing = false;
4648         if(this.store){
4649             this.store.afterReject(this);
4650         }
4651     },
4652
4653     /**
4654      * Usually called by the {@link Roo.data.Store} which owns the Record.
4655      * Commits all changes made to the Record since either creation, or the last commit operation.
4656      * <p>
4657      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4658      * of commit operations.
4659      */
4660     commit : function(){
4661         this.dirty = false;
4662         delete this.modified;
4663         this.editing = false;
4664         if(this.store){
4665             this.store.afterCommit(this);
4666         }
4667     },
4668
4669     // private
4670     hasError : function(){
4671         return this.error != null;
4672     },
4673
4674     // private
4675     clearError : function(){
4676         this.error = null;
4677     },
4678
4679     /**
4680      * Creates a copy of this record.
4681      * @param {String} id (optional) A new record id if you don't want to use this record's id
4682      * @return {Record}
4683      */
4684     copy : function(newId) {
4685         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4686     }
4687 };/*
4688  * Based on:
4689  * Ext JS Library 1.1.1
4690  * Copyright(c) 2006-2007, Ext JS, LLC.
4691  *
4692  * Originally Released Under LGPL - original licence link has changed is not relivant.
4693  *
4694  * Fork - LGPL
4695  * <script type="text/javascript">
4696  */
4697
4698
4699
4700 /**
4701  * @class Roo.data.Store
4702  * @extends Roo.util.Observable
4703  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4704  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4705  * <p>
4706  * 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
4707  * has no knowledge of the format of the data returned by the Proxy.<br>
4708  * <p>
4709  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4710  * instances from the data object. These records are cached and made available through accessor functions.
4711  * @constructor
4712  * Creates a new Store.
4713  * @param {Object} config A config object containing the objects needed for the Store to access data,
4714  * and read the data into Records.
4715  */
4716 Roo.data.Store = function(config){
4717     this.data = new Roo.util.MixedCollection(false);
4718     this.data.getKey = function(o){
4719         return o.id;
4720     };
4721     this.baseParams = {};
4722     // private
4723     this.paramNames = {
4724         "start" : "start",
4725         "limit" : "limit",
4726         "sort" : "sort",
4727         "dir" : "dir",
4728         "multisort" : "_multisort"
4729     };
4730
4731     if(config && config.data){
4732         this.inlineData = config.data;
4733         delete config.data;
4734     }
4735
4736     Roo.apply(this, config);
4737     
4738     if(this.reader){ // reader passed
4739         this.reader = Roo.factory(this.reader, Roo.data);
4740         this.reader.xmodule = this.xmodule || false;
4741         if(!this.recordType){
4742             this.recordType = this.reader.recordType;
4743         }
4744         if(this.reader.onMetaChange){
4745             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4746         }
4747     }
4748
4749     if(this.recordType){
4750         this.fields = this.recordType.prototype.fields;
4751     }
4752     this.modified = [];
4753
4754     this.addEvents({
4755         /**
4756          * @event datachanged
4757          * Fires when the data cache has changed, and a widget which is using this Store
4758          * as a Record cache should refresh its view.
4759          * @param {Store} this
4760          */
4761         datachanged : true,
4762         /**
4763          * @event metachange
4764          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4765          * @param {Store} this
4766          * @param {Object} meta The JSON metadata
4767          */
4768         metachange : true,
4769         /**
4770          * @event add
4771          * Fires when Records have been added to the Store
4772          * @param {Store} this
4773          * @param {Roo.data.Record[]} records The array of Records added
4774          * @param {Number} index The index at which the record(s) were added
4775          */
4776         add : true,
4777         /**
4778          * @event remove
4779          * Fires when a Record has been removed from the Store
4780          * @param {Store} this
4781          * @param {Roo.data.Record} record The Record that was removed
4782          * @param {Number} index The index at which the record was removed
4783          */
4784         remove : true,
4785         /**
4786          * @event update
4787          * Fires when a Record has been updated
4788          * @param {Store} this
4789          * @param {Roo.data.Record} record The Record that was updated
4790          * @param {String} operation The update operation being performed.  Value may be one of:
4791          * <pre><code>
4792  Roo.data.Record.EDIT
4793  Roo.data.Record.REJECT
4794  Roo.data.Record.COMMIT
4795          * </code></pre>
4796          */
4797         update : true,
4798         /**
4799          * @event clear
4800          * Fires when the data cache has been cleared.
4801          * @param {Store} this
4802          */
4803         clear : true,
4804         /**
4805          * @event beforeload
4806          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4807          * the load action will be canceled.
4808          * @param {Store} this
4809          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4810          */
4811         beforeload : true,
4812         /**
4813          * @event load
4814          * Fires after a new set of Records has been loaded.
4815          * @param {Store} this
4816          * @param {Roo.data.Record[]} records The Records that were loaded
4817          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4818          */
4819         load : true,
4820         /**
4821          * @event loadexception
4822          * Fires if an exception occurs in the Proxy during loading.
4823          * Called with the signature of the Proxy's "loadexception" event.
4824          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4825          * 
4826          * @param {Proxy} 
4827          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4828          * @param {Object} load options 
4829          * @param {Object} jsonData from your request (normally this contains the Exception)
4830          */
4831         loadexception : true
4832     });
4833     
4834     if(this.proxy){
4835         this.proxy = Roo.factory(this.proxy, Roo.data);
4836         this.proxy.xmodule = this.xmodule || false;
4837         this.relayEvents(this.proxy,  ["loadexception"]);
4838     }
4839     this.sortToggle = {};
4840     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4841
4842     Roo.data.Store.superclass.constructor.call(this);
4843
4844     if(this.inlineData){
4845         this.loadData(this.inlineData);
4846         delete this.inlineData;
4847     }
4848 };
4849 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4850      /**
4851     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4852     * without a remote query - used by combo/forms at present.
4853     */
4854     
4855     /**
4856     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4857     */
4858     /**
4859     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4860     */
4861     /**
4862     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4863     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4864     */
4865     /**
4866     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4867     * on any HTTP request
4868     */
4869     /**
4870     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4871     */
4872     /**
4873     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4874     */
4875     multiSort: false,
4876     /**
4877     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4878     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4879     */
4880     remoteSort : false,
4881
4882     /**
4883     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4884      * loaded or when a record is removed. (defaults to false).
4885     */
4886     pruneModifiedRecords : false,
4887
4888     // private
4889     lastOptions : null,
4890
4891     /**
4892      * Add Records to the Store and fires the add event.
4893      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4894      */
4895     add : function(records){
4896         records = [].concat(records);
4897         for(var i = 0, len = records.length; i < len; i++){
4898             records[i].join(this);
4899         }
4900         var index = this.data.length;
4901         this.data.addAll(records);
4902         this.fireEvent("add", this, records, index);
4903     },
4904
4905     /**
4906      * Remove a Record from the Store and fires the remove event.
4907      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4908      */
4909     remove : function(record){
4910         var index = this.data.indexOf(record);
4911         this.data.removeAt(index);
4912         if(this.pruneModifiedRecords){
4913             this.modified.remove(record);
4914         }
4915         this.fireEvent("remove", this, record, index);
4916     },
4917
4918     /**
4919      * Remove all Records from the Store and fires the clear event.
4920      */
4921     removeAll : function(){
4922         this.data.clear();
4923         if(this.pruneModifiedRecords){
4924             this.modified = [];
4925         }
4926         this.fireEvent("clear", this);
4927     },
4928
4929     /**
4930      * Inserts Records to the Store at the given index and fires the add event.
4931      * @param {Number} index The start index at which to insert the passed Records.
4932      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4933      */
4934     insert : function(index, records){
4935         records = [].concat(records);
4936         for(var i = 0, len = records.length; i < len; i++){
4937             this.data.insert(index, records[i]);
4938             records[i].join(this);
4939         }
4940         this.fireEvent("add", this, records, index);
4941     },
4942
4943     /**
4944      * Get the index within the cache of the passed Record.
4945      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4946      * @return {Number} The index of the passed Record. Returns -1 if not found.
4947      */
4948     indexOf : function(record){
4949         return this.data.indexOf(record);
4950     },
4951
4952     /**
4953      * Get the index within the cache of the Record with the passed id.
4954      * @param {String} id The id of the Record to find.
4955      * @return {Number} The index of the Record. Returns -1 if not found.
4956      */
4957     indexOfId : function(id){
4958         return this.data.indexOfKey(id);
4959     },
4960
4961     /**
4962      * Get the Record with the specified id.
4963      * @param {String} id The id of the Record to find.
4964      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4965      */
4966     getById : function(id){
4967         return this.data.key(id);
4968     },
4969
4970     /**
4971      * Get the Record at the specified index.
4972      * @param {Number} index The index of the Record to find.
4973      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4974      */
4975     getAt : function(index){
4976         return this.data.itemAt(index);
4977     },
4978
4979     /**
4980      * Returns a range of Records between specified indices.
4981      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4982      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4983      * @return {Roo.data.Record[]} An array of Records
4984      */
4985     getRange : function(start, end){
4986         return this.data.getRange(start, end);
4987     },
4988
4989     // private
4990     storeOptions : function(o){
4991         o = Roo.apply({}, o);
4992         delete o.callback;
4993         delete o.scope;
4994         this.lastOptions = o;
4995     },
4996
4997     /**
4998      * Loads the Record cache from the configured Proxy using the configured Reader.
4999      * <p>
5000      * If using remote paging, then the first load call must specify the <em>start</em>
5001      * and <em>limit</em> properties in the options.params property to establish the initial
5002      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5003      * <p>
5004      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5005      * and this call will return before the new data has been loaded. Perform any post-processing
5006      * in a callback function, or in a "load" event handler.</strong>
5007      * <p>
5008      * @param {Object} options An object containing properties which control loading options:<ul>
5009      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5010      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5011      * passed the following arguments:<ul>
5012      * <li>r : Roo.data.Record[]</li>
5013      * <li>options: Options object from the load call</li>
5014      * <li>success: Boolean success indicator</li></ul></li>
5015      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5016      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5017      * </ul>
5018      */
5019     load : function(options){
5020         options = options || {};
5021         if(this.fireEvent("beforeload", this, options) !== false){
5022             this.storeOptions(options);
5023             var p = Roo.apply(options.params || {}, this.baseParams);
5024             // if meta was not loaded from remote source.. try requesting it.
5025             if (!this.reader.metaFromRemote) {
5026                 p._requestMeta = 1;
5027             }
5028             if(this.sortInfo && this.remoteSort){
5029                 var pn = this.paramNames;
5030                 p[pn["sort"]] = this.sortInfo.field;
5031                 p[pn["dir"]] = this.sortInfo.direction;
5032             }
5033             if (this.multiSort) {
5034                 var pn = this.paramNames;
5035                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5036             }
5037             
5038             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5039         }
5040     },
5041
5042     /**
5043      * Reloads the Record cache from the configured Proxy using the configured Reader and
5044      * the options from the last load operation performed.
5045      * @param {Object} options (optional) An object containing properties which may override the options
5046      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5047      * the most recently used options are reused).
5048      */
5049     reload : function(options){
5050         this.load(Roo.applyIf(options||{}, this.lastOptions));
5051     },
5052
5053     // private
5054     // Called as a callback by the Reader during a load operation.
5055     loadRecords : function(o, options, success){
5056         if(!o || success === false){
5057             if(success !== false){
5058                 this.fireEvent("load", this, [], options);
5059             }
5060             if(options.callback){
5061                 options.callback.call(options.scope || this, [], options, false);
5062             }
5063             return;
5064         }
5065         // if data returned failure - throw an exception.
5066         if (o.success === false) {
5067             // show a message if no listener is registered.
5068             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5069                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5070             }
5071             // loadmask wil be hooked into this..
5072             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5073             return;
5074         }
5075         var r = o.records, t = o.totalRecords || r.length;
5076         if(!options || options.add !== true){
5077             if(this.pruneModifiedRecords){
5078                 this.modified = [];
5079             }
5080             for(var i = 0, len = r.length; i < len; i++){
5081                 r[i].join(this);
5082             }
5083             if(this.snapshot){
5084                 this.data = this.snapshot;
5085                 delete this.snapshot;
5086             }
5087             this.data.clear();
5088             this.data.addAll(r);
5089             this.totalLength = t;
5090             this.applySort();
5091             this.fireEvent("datachanged", this);
5092         }else{
5093             this.totalLength = Math.max(t, this.data.length+r.length);
5094             this.add(r);
5095         }
5096         this.fireEvent("load", this, r, options);
5097         if(options.callback){
5098             options.callback.call(options.scope || this, r, options, true);
5099         }
5100     },
5101
5102
5103     /**
5104      * Loads data from a passed data block. A Reader which understands the format of the data
5105      * must have been configured in the constructor.
5106      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5107      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5108      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5109      */
5110     loadData : function(o, append){
5111         var r = this.reader.readRecords(o);
5112         this.loadRecords(r, {add: append}, true);
5113     },
5114
5115     /**
5116      * Gets the number of cached records.
5117      * <p>
5118      * <em>If using paging, this may not be the total size of the dataset. If the data object
5119      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5120      * the data set size</em>
5121      */
5122     getCount : function(){
5123         return this.data.length || 0;
5124     },
5125
5126     /**
5127      * Gets the total number of records in the dataset as returned by the server.
5128      * <p>
5129      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5130      * the dataset size</em>
5131      */
5132     getTotalCount : function(){
5133         return this.totalLength || 0;
5134     },
5135
5136     /**
5137      * Returns the sort state of the Store as an object with two properties:
5138      * <pre><code>
5139  field {String} The name of the field by which the Records are sorted
5140  direction {String} The sort order, "ASC" or "DESC"
5141      * </code></pre>
5142      */
5143     getSortState : function(){
5144         return this.sortInfo;
5145     },
5146
5147     // private
5148     applySort : function(){
5149         if(this.sortInfo && !this.remoteSort){
5150             var s = this.sortInfo, f = s.field;
5151             var st = this.fields.get(f).sortType;
5152             var fn = function(r1, r2){
5153                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5154                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5155             };
5156             this.data.sort(s.direction, fn);
5157             if(this.snapshot && this.snapshot != this.data){
5158                 this.snapshot.sort(s.direction, fn);
5159             }
5160         }
5161     },
5162
5163     /**
5164      * Sets the default sort column and order to be used by the next load operation.
5165      * @param {String} fieldName The name of the field to sort by.
5166      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5167      */
5168     setDefaultSort : function(field, dir){
5169         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5170     },
5171
5172     /**
5173      * Sort the Records.
5174      * If remote sorting is used, the sort is performed on the server, and the cache is
5175      * reloaded. If local sorting is used, the cache is sorted internally.
5176      * @param {String} fieldName The name of the field to sort by.
5177      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5178      */
5179     sort : function(fieldName, dir){
5180         var f = this.fields.get(fieldName);
5181         if(!dir){
5182             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5183             
5184             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5185                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5186             }else{
5187                 dir = f.sortDir;
5188             }
5189         }
5190         this.sortToggle[f.name] = dir;
5191         this.sortInfo = {field: f.name, direction: dir};
5192         if(!this.remoteSort){
5193             this.applySort();
5194             this.fireEvent("datachanged", this);
5195         }else{
5196             this.load(this.lastOptions);
5197         }
5198     },
5199
5200     /**
5201      * Calls the specified function for each of the Records in the cache.
5202      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5203      * Returning <em>false</em> aborts and exits the iteration.
5204      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5205      */
5206     each : function(fn, scope){
5207         this.data.each(fn, scope);
5208     },
5209
5210     /**
5211      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5212      * (e.g., during paging).
5213      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5214      */
5215     getModifiedRecords : function(){
5216         return this.modified;
5217     },
5218
5219     // private
5220     createFilterFn : function(property, value, anyMatch){
5221         if(!value.exec){ // not a regex
5222             value = String(value);
5223             if(value.length == 0){
5224                 return false;
5225             }
5226             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5227         }
5228         return function(r){
5229             return value.test(r.data[property]);
5230         };
5231     },
5232
5233     /**
5234      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5235      * @param {String} property A field on your records
5236      * @param {Number} start The record index to start at (defaults to 0)
5237      * @param {Number} end The last record index to include (defaults to length - 1)
5238      * @return {Number} The sum
5239      */
5240     sum : function(property, start, end){
5241         var rs = this.data.items, v = 0;
5242         start = start || 0;
5243         end = (end || end === 0) ? end : rs.length-1;
5244
5245         for(var i = start; i <= end; i++){
5246             v += (rs[i].data[property] || 0);
5247         }
5248         return v;
5249     },
5250
5251     /**
5252      * Filter the records by a specified property.
5253      * @param {String} field A field on your records
5254      * @param {String/RegExp} value Either a string that the field
5255      * should start with or a RegExp to test against the field
5256      * @param {Boolean} anyMatch True to match any part not just the beginning
5257      */
5258     filter : function(property, value, anyMatch){
5259         var fn = this.createFilterFn(property, value, anyMatch);
5260         return fn ? this.filterBy(fn) : this.clearFilter();
5261     },
5262
5263     /**
5264      * Filter by a function. The specified function will be called with each
5265      * record in this data source. If the function returns true the record is included,
5266      * otherwise it is filtered.
5267      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5268      * @param {Object} scope (optional) The scope of the function (defaults to this)
5269      */
5270     filterBy : function(fn, scope){
5271         this.snapshot = this.snapshot || this.data;
5272         this.data = this.queryBy(fn, scope||this);
5273         this.fireEvent("datachanged", this);
5274     },
5275
5276     /**
5277      * Query the records by a specified property.
5278      * @param {String} field A field on your records
5279      * @param {String/RegExp} value Either a string that the field
5280      * should start with or a RegExp to test against the field
5281      * @param {Boolean} anyMatch True to match any part not just the beginning
5282      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5283      */
5284     query : function(property, value, anyMatch){
5285         var fn = this.createFilterFn(property, value, anyMatch);
5286         return fn ? this.queryBy(fn) : this.data.clone();
5287     },
5288
5289     /**
5290      * Query by a function. The specified function will be called with each
5291      * record in this data source. If the function returns true the record is included
5292      * in the results.
5293      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5294      * @param {Object} scope (optional) The scope of the function (defaults to this)
5295       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5296      **/
5297     queryBy : function(fn, scope){
5298         var data = this.snapshot || this.data;
5299         return data.filterBy(fn, scope||this);
5300     },
5301
5302     /**
5303      * Collects unique values for a particular dataIndex from this store.
5304      * @param {String} dataIndex The property to collect
5305      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5306      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5307      * @return {Array} An array of the unique values
5308      **/
5309     collect : function(dataIndex, allowNull, bypassFilter){
5310         var d = (bypassFilter === true && this.snapshot) ?
5311                 this.snapshot.items : this.data.items;
5312         var v, sv, r = [], l = {};
5313         for(var i = 0, len = d.length; i < len; i++){
5314             v = d[i].data[dataIndex];
5315             sv = String(v);
5316             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5317                 l[sv] = true;
5318                 r[r.length] = v;
5319             }
5320         }
5321         return r;
5322     },
5323
5324     /**
5325      * Revert to a view of the Record cache with no filtering applied.
5326      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5327      */
5328     clearFilter : function(suppressEvent){
5329         if(this.snapshot && this.snapshot != this.data){
5330             this.data = this.snapshot;
5331             delete this.snapshot;
5332             if(suppressEvent !== true){
5333                 this.fireEvent("datachanged", this);
5334             }
5335         }
5336     },
5337
5338     // private
5339     afterEdit : function(record){
5340         if(this.modified.indexOf(record) == -1){
5341             this.modified.push(record);
5342         }
5343         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5344     },
5345     
5346     // private
5347     afterReject : function(record){
5348         this.modified.remove(record);
5349         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5350     },
5351
5352     // private
5353     afterCommit : function(record){
5354         this.modified.remove(record);
5355         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5356     },
5357
5358     /**
5359      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5360      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5361      */
5362     commitChanges : function(){
5363         var m = this.modified.slice(0);
5364         this.modified = [];
5365         for(var i = 0, len = m.length; i < len; i++){
5366             m[i].commit();
5367         }
5368     },
5369
5370     /**
5371      * Cancel outstanding changes on all changed records.
5372      */
5373     rejectChanges : function(){
5374         var m = this.modified.slice(0);
5375         this.modified = [];
5376         for(var i = 0, len = m.length; i < len; i++){
5377             m[i].reject();
5378         }
5379     },
5380
5381     onMetaChange : function(meta, rtype, o){
5382         this.recordType = rtype;
5383         this.fields = rtype.prototype.fields;
5384         delete this.snapshot;
5385         this.sortInfo = meta.sortInfo || this.sortInfo;
5386         this.modified = [];
5387         this.fireEvent('metachange', this, this.reader.meta);
5388     }
5389 });/*
5390  * Based on:
5391  * Ext JS Library 1.1.1
5392  * Copyright(c) 2006-2007, Ext JS, LLC.
5393  *
5394  * Originally Released Under LGPL - original licence link has changed is not relivant.
5395  *
5396  * Fork - LGPL
5397  * <script type="text/javascript">
5398  */
5399
5400 /**
5401  * @class Roo.data.SimpleStore
5402  * @extends Roo.data.Store
5403  * Small helper class to make creating Stores from Array data easier.
5404  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5405  * @cfg {Array} fields An array of field definition objects, or field name strings.
5406  * @cfg {Array} data The multi-dimensional array of data
5407  * @constructor
5408  * @param {Object} config
5409  */
5410 Roo.data.SimpleStore = function(config){
5411     Roo.data.SimpleStore.superclass.constructor.call(this, {
5412         isLocal : true,
5413         reader: new Roo.data.ArrayReader({
5414                 id: config.id
5415             },
5416             Roo.data.Record.create(config.fields)
5417         ),
5418         proxy : new Roo.data.MemoryProxy(config.data)
5419     });
5420     this.load();
5421 };
5422 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5423  * Based on:
5424  * Ext JS Library 1.1.1
5425  * Copyright(c) 2006-2007, Ext JS, LLC.
5426  *
5427  * Originally Released Under LGPL - original licence link has changed is not relivant.
5428  *
5429  * Fork - LGPL
5430  * <script type="text/javascript">
5431  */
5432
5433 /**
5434 /**
5435  * @extends Roo.data.Store
5436  * @class Roo.data.JsonStore
5437  * Small helper class to make creating Stores for JSON data easier. <br/>
5438 <pre><code>
5439 var store = new Roo.data.JsonStore({
5440     url: 'get-images.php',
5441     root: 'images',
5442     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5443 });
5444 </code></pre>
5445  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5446  * JsonReader and HttpProxy (unless inline data is provided).</b>
5447  * @cfg {Array} fields An array of field definition objects, or field name strings.
5448  * @constructor
5449  * @param {Object} config
5450  */
5451 Roo.data.JsonStore = function(c){
5452     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5453         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5454         reader: new Roo.data.JsonReader(c, c.fields)
5455     }));
5456 };
5457 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5458  * Based on:
5459  * Ext JS Library 1.1.1
5460  * Copyright(c) 2006-2007, Ext JS, LLC.
5461  *
5462  * Originally Released Under LGPL - original licence link has changed is not relivant.
5463  *
5464  * Fork - LGPL
5465  * <script type="text/javascript">
5466  */
5467
5468  
5469 Roo.data.Field = function(config){
5470     if(typeof config == "string"){
5471         config = {name: config};
5472     }
5473     Roo.apply(this, config);
5474     
5475     if(!this.type){
5476         this.type = "auto";
5477     }
5478     
5479     var st = Roo.data.SortTypes;
5480     // named sortTypes are supported, here we look them up
5481     if(typeof this.sortType == "string"){
5482         this.sortType = st[this.sortType];
5483     }
5484     
5485     // set default sortType for strings and dates
5486     if(!this.sortType){
5487         switch(this.type){
5488             case "string":
5489                 this.sortType = st.asUCString;
5490                 break;
5491             case "date":
5492                 this.sortType = st.asDate;
5493                 break;
5494             default:
5495                 this.sortType = st.none;
5496         }
5497     }
5498
5499     // define once
5500     var stripRe = /[\$,%]/g;
5501
5502     // prebuilt conversion function for this field, instead of
5503     // switching every time we're reading a value
5504     if(!this.convert){
5505         var cv, dateFormat = this.dateFormat;
5506         switch(this.type){
5507             case "":
5508             case "auto":
5509             case undefined:
5510                 cv = function(v){ return v; };
5511                 break;
5512             case "string":
5513                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5514                 break;
5515             case "int":
5516                 cv = function(v){
5517                     return v !== undefined && v !== null && v !== '' ?
5518                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5519                     };
5520                 break;
5521             case "float":
5522                 cv = function(v){
5523                     return v !== undefined && v !== null && v !== '' ?
5524                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5525                     };
5526                 break;
5527             case "bool":
5528             case "boolean":
5529                 cv = function(v){ return v === true || v === "true" || v == 1; };
5530                 break;
5531             case "date":
5532                 cv = function(v){
5533                     if(!v){
5534                         return '';
5535                     }
5536                     if(v instanceof Date){
5537                         return v;
5538                     }
5539                     if(dateFormat){
5540                         if(dateFormat == "timestamp"){
5541                             return new Date(v*1000);
5542                         }
5543                         return Date.parseDate(v, dateFormat);
5544                     }
5545                     var parsed = Date.parse(v);
5546                     return parsed ? new Date(parsed) : null;
5547                 };
5548              break;
5549             
5550         }
5551         this.convert = cv;
5552     }
5553 };
5554
5555 Roo.data.Field.prototype = {
5556     dateFormat: null,
5557     defaultValue: "",
5558     mapping: null,
5559     sortType : null,
5560     sortDir : "ASC"
5561 };/*
5562  * Based on:
5563  * Ext JS Library 1.1.1
5564  * Copyright(c) 2006-2007, Ext JS, LLC.
5565  *
5566  * Originally Released Under LGPL - original licence link has changed is not relivant.
5567  *
5568  * Fork - LGPL
5569  * <script type="text/javascript">
5570  */
5571  
5572 // Base class for reading structured data from a data source.  This class is intended to be
5573 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5574
5575 /**
5576  * @class Roo.data.DataReader
5577  * Base class for reading structured data from a data source.  This class is intended to be
5578  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5579  */
5580
5581 Roo.data.DataReader = function(meta, recordType){
5582     
5583     this.meta = meta;
5584     
5585     this.recordType = recordType instanceof Array ? 
5586         Roo.data.Record.create(recordType) : recordType;
5587 };
5588
5589 Roo.data.DataReader.prototype = {
5590      /**
5591      * Create an empty record
5592      * @param {Object} data (optional) - overlay some values
5593      * @return {Roo.data.Record} record created.
5594      */
5595     newRow :  function(d) {
5596         var da =  {};
5597         this.recordType.prototype.fields.each(function(c) {
5598             switch( c.type) {
5599                 case 'int' : da[c.name] = 0; break;
5600                 case 'date' : da[c.name] = new Date(); break;
5601                 case 'float' : da[c.name] = 0.0; break;
5602                 case 'boolean' : da[c.name] = false; break;
5603                 default : da[c.name] = ""; break;
5604             }
5605             
5606         });
5607         return new this.recordType(Roo.apply(da, d));
5608     }
5609     
5610 };/*
5611  * Based on:
5612  * Ext JS Library 1.1.1
5613  * Copyright(c) 2006-2007, Ext JS, LLC.
5614  *
5615  * Originally Released Under LGPL - original licence link has changed is not relivant.
5616  *
5617  * Fork - LGPL
5618  * <script type="text/javascript">
5619  */
5620
5621 /**
5622  * @class Roo.data.DataProxy
5623  * @extends Roo.data.Observable
5624  * This class is an abstract base class for implementations which provide retrieval of
5625  * unformatted data objects.<br>
5626  * <p>
5627  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5628  * (of the appropriate type which knows how to parse the data object) to provide a block of
5629  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5630  * <p>
5631  * Custom implementations must implement the load method as described in
5632  * {@link Roo.data.HttpProxy#load}.
5633  */
5634 Roo.data.DataProxy = function(){
5635     this.addEvents({
5636         /**
5637          * @event beforeload
5638          * Fires before a network request is made to retrieve a data object.
5639          * @param {Object} This DataProxy object.
5640          * @param {Object} params The params parameter to the load function.
5641          */
5642         beforeload : true,
5643         /**
5644          * @event load
5645          * Fires before the load method's callback is called.
5646          * @param {Object} This DataProxy object.
5647          * @param {Object} o The data object.
5648          * @param {Object} arg The callback argument object passed to the load function.
5649          */
5650         load : true,
5651         /**
5652          * @event loadexception
5653          * Fires if an Exception occurs during data retrieval.
5654          * @param {Object} This DataProxy object.
5655          * @param {Object} o The data object.
5656          * @param {Object} arg The callback argument object passed to the load function.
5657          * @param {Object} e The Exception.
5658          */
5659         loadexception : true
5660     });
5661     Roo.data.DataProxy.superclass.constructor.call(this);
5662 };
5663
5664 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5665
5666     /**
5667      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5668      */
5669 /*
5670  * Based on:
5671  * Ext JS Library 1.1.1
5672  * Copyright(c) 2006-2007, Ext JS, LLC.
5673  *
5674  * Originally Released Under LGPL - original licence link has changed is not relivant.
5675  *
5676  * Fork - LGPL
5677  * <script type="text/javascript">
5678  */
5679 /**
5680  * @class Roo.data.MemoryProxy
5681  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5682  * to the Reader when its load method is called.
5683  * @constructor
5684  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5685  */
5686 Roo.data.MemoryProxy = function(data){
5687     if (data.data) {
5688         data = data.data;
5689     }
5690     Roo.data.MemoryProxy.superclass.constructor.call(this);
5691     this.data = data;
5692 };
5693
5694 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5695     /**
5696      * Load data from the requested source (in this case an in-memory
5697      * data object passed to the constructor), read the data object into
5698      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5699      * process that block using the passed callback.
5700      * @param {Object} params This parameter is not used by the MemoryProxy class.
5701      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5702      * object into a block of Roo.data.Records.
5703      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5704      * The function must be passed <ul>
5705      * <li>The Record block object</li>
5706      * <li>The "arg" argument from the load function</li>
5707      * <li>A boolean success indicator</li>
5708      * </ul>
5709      * @param {Object} scope The scope in which to call the callback
5710      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5711      */
5712     load : function(params, reader, callback, scope, arg){
5713         params = params || {};
5714         var result;
5715         try {
5716             result = reader.readRecords(this.data);
5717         }catch(e){
5718             this.fireEvent("loadexception", this, arg, null, e);
5719             callback.call(scope, null, arg, false);
5720             return;
5721         }
5722         callback.call(scope, result, arg, true);
5723     },
5724     
5725     // private
5726     update : function(params, records){
5727         
5728     }
5729 });/*
5730  * Based on:
5731  * Ext JS Library 1.1.1
5732  * Copyright(c) 2006-2007, Ext JS, LLC.
5733  *
5734  * Originally Released Under LGPL - original licence link has changed is not relivant.
5735  *
5736  * Fork - LGPL
5737  * <script type="text/javascript">
5738  */
5739 /**
5740  * @class Roo.data.HttpProxy
5741  * @extends Roo.data.DataProxy
5742  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5743  * configured to reference a certain URL.<br><br>
5744  * <p>
5745  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5746  * from which the running page was served.<br><br>
5747  * <p>
5748  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5749  * <p>
5750  * Be aware that to enable the browser to parse an XML document, the server must set
5751  * the Content-Type header in the HTTP response to "text/xml".
5752  * @constructor
5753  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5754  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5755  * will be used to make the request.
5756  */
5757 Roo.data.HttpProxy = function(conn){
5758     Roo.data.HttpProxy.superclass.constructor.call(this);
5759     // is conn a conn config or a real conn?
5760     this.conn = conn;
5761     this.useAjax = !conn || !conn.events;
5762   
5763 };
5764
5765 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5766     // thse are take from connection...
5767     
5768     /**
5769      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5770      */
5771     /**
5772      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5773      * extra parameters to each request made by this object. (defaults to undefined)
5774      */
5775     /**
5776      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5777      *  to each request made by this object. (defaults to undefined)
5778      */
5779     /**
5780      * @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)
5781      */
5782     /**
5783      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5784      */
5785      /**
5786      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5787      * @type Boolean
5788      */
5789   
5790
5791     /**
5792      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5793      * @type Boolean
5794      */
5795     /**
5796      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5797      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5798      * a finer-grained basis than the DataProxy events.
5799      */
5800     getConnection : function(){
5801         return this.useAjax ? Roo.Ajax : this.conn;
5802     },
5803
5804     /**
5805      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5806      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5807      * process that block using the passed callback.
5808      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5809      * for the request to the remote server.
5810      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5811      * object into a block of Roo.data.Records.
5812      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5813      * The function must be passed <ul>
5814      * <li>The Record block object</li>
5815      * <li>The "arg" argument from the load function</li>
5816      * <li>A boolean success indicator</li>
5817      * </ul>
5818      * @param {Object} scope The scope in which to call the callback
5819      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5820      */
5821     load : function(params, reader, callback, scope, arg){
5822         if(this.fireEvent("beforeload", this, params) !== false){
5823             var  o = {
5824                 params : params || {},
5825                 request: {
5826                     callback : callback,
5827                     scope : scope,
5828                     arg : arg
5829                 },
5830                 reader: reader,
5831                 callback : this.loadResponse,
5832                 scope: this
5833             };
5834             if(this.useAjax){
5835                 Roo.applyIf(o, this.conn);
5836                 if(this.activeRequest){
5837                     Roo.Ajax.abort(this.activeRequest);
5838                 }
5839                 this.activeRequest = Roo.Ajax.request(o);
5840             }else{
5841                 this.conn.request(o);
5842             }
5843         }else{
5844             callback.call(scope||this, null, arg, false);
5845         }
5846     },
5847
5848     // private
5849     loadResponse : function(o, success, response){
5850         delete this.activeRequest;
5851         if(!success){
5852             this.fireEvent("loadexception", this, o, response);
5853             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5854             return;
5855         }
5856         var result;
5857         try {
5858             result = o.reader.read(response);
5859         }catch(e){
5860             this.fireEvent("loadexception", this, o, response, e);
5861             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5862             return;
5863         }
5864         
5865         this.fireEvent("load", this, o, o.request.arg);
5866         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5867     },
5868
5869     // private
5870     update : function(dataSet){
5871
5872     },
5873
5874     // private
5875     updateResponse : function(dataSet){
5876
5877     }
5878 });/*
5879  * Based on:
5880  * Ext JS Library 1.1.1
5881  * Copyright(c) 2006-2007, Ext JS, LLC.
5882  *
5883  * Originally Released Under LGPL - original licence link has changed is not relivant.
5884  *
5885  * Fork - LGPL
5886  * <script type="text/javascript">
5887  */
5888
5889 /**
5890  * @class Roo.data.ScriptTagProxy
5891  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5892  * other than the originating domain of the running page.<br><br>
5893  * <p>
5894  * <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
5895  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5896  * <p>
5897  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5898  * source code that is used as the source inside a &lt;script> tag.<br><br>
5899  * <p>
5900  * In order for the browser to process the returned data, the server must wrap the data object
5901  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5902  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5903  * depending on whether the callback name was passed:
5904  * <p>
5905  * <pre><code>
5906 boolean scriptTag = false;
5907 String cb = request.getParameter("callback");
5908 if (cb != null) {
5909     scriptTag = true;
5910     response.setContentType("text/javascript");
5911 } else {
5912     response.setContentType("application/x-json");
5913 }
5914 Writer out = response.getWriter();
5915 if (scriptTag) {
5916     out.write(cb + "(");
5917 }
5918 out.print(dataBlock.toJsonString());
5919 if (scriptTag) {
5920     out.write(");");
5921 }
5922 </pre></code>
5923  *
5924  * @constructor
5925  * @param {Object} config A configuration object.
5926  */
5927 Roo.data.ScriptTagProxy = function(config){
5928     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5929     Roo.apply(this, config);
5930     this.head = document.getElementsByTagName("head")[0];
5931 };
5932
5933 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5934
5935 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5936     /**
5937      * @cfg {String} url The URL from which to request the data object.
5938      */
5939     /**
5940      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5941      */
5942     timeout : 30000,
5943     /**
5944      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5945      * the server the name of the callback function set up by the load call to process the returned data object.
5946      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5947      * javascript output which calls this named function passing the data object as its only parameter.
5948      */
5949     callbackParam : "callback",
5950     /**
5951      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5952      * name to the request.
5953      */
5954     nocache : true,
5955
5956     /**
5957      * Load data from the configured URL, read the data object into
5958      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5959      * process that block using the passed callback.
5960      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5961      * for the request to the remote server.
5962      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5963      * object into a block of Roo.data.Records.
5964      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5965      * The function must be passed <ul>
5966      * <li>The Record block object</li>
5967      * <li>The "arg" argument from the load function</li>
5968      * <li>A boolean success indicator</li>
5969      * </ul>
5970      * @param {Object} scope The scope in which to call the callback
5971      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5972      */
5973     load : function(params, reader, callback, scope, arg){
5974         if(this.fireEvent("beforeload", this, params) !== false){
5975
5976             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5977
5978             var url = this.url;
5979             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5980             if(this.nocache){
5981                 url += "&_dc=" + (new Date().getTime());
5982             }
5983             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5984             var trans = {
5985                 id : transId,
5986                 cb : "stcCallback"+transId,
5987                 scriptId : "stcScript"+transId,
5988                 params : params,
5989                 arg : arg,
5990                 url : url,
5991                 callback : callback,
5992                 scope : scope,
5993                 reader : reader
5994             };
5995             var conn = this;
5996
5997             window[trans.cb] = function(o){
5998                 conn.handleResponse(o, trans);
5999             };
6000
6001             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6002
6003             if(this.autoAbort !== false){
6004                 this.abort();
6005             }
6006
6007             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6008
6009             var script = document.createElement("script");
6010             script.setAttribute("src", url);
6011             script.setAttribute("type", "text/javascript");
6012             script.setAttribute("id", trans.scriptId);
6013             this.head.appendChild(script);
6014
6015             this.trans = trans;
6016         }else{
6017             callback.call(scope||this, null, arg, false);
6018         }
6019     },
6020
6021     // private
6022     isLoading : function(){
6023         return this.trans ? true : false;
6024     },
6025
6026     /**
6027      * Abort the current server request.
6028      */
6029     abort : function(){
6030         if(this.isLoading()){
6031             this.destroyTrans(this.trans);
6032         }
6033     },
6034
6035     // private
6036     destroyTrans : function(trans, isLoaded){
6037         this.head.removeChild(document.getElementById(trans.scriptId));
6038         clearTimeout(trans.timeoutId);
6039         if(isLoaded){
6040             window[trans.cb] = undefined;
6041             try{
6042                 delete window[trans.cb];
6043             }catch(e){}
6044         }else{
6045             // if hasn't been loaded, wait for load to remove it to prevent script error
6046             window[trans.cb] = function(){
6047                 window[trans.cb] = undefined;
6048                 try{
6049                     delete window[trans.cb];
6050                 }catch(e){}
6051             };
6052         }
6053     },
6054
6055     // private
6056     handleResponse : function(o, trans){
6057         this.trans = false;
6058         this.destroyTrans(trans, true);
6059         var result;
6060         try {
6061             result = trans.reader.readRecords(o);
6062         }catch(e){
6063             this.fireEvent("loadexception", this, o, trans.arg, e);
6064             trans.callback.call(trans.scope||window, null, trans.arg, false);
6065             return;
6066         }
6067         this.fireEvent("load", this, o, trans.arg);
6068         trans.callback.call(trans.scope||window, result, trans.arg, true);
6069     },
6070
6071     // private
6072     handleFailure : function(trans){
6073         this.trans = false;
6074         this.destroyTrans(trans, false);
6075         this.fireEvent("loadexception", this, null, trans.arg);
6076         trans.callback.call(trans.scope||window, null, trans.arg, false);
6077     }
6078 });/*
6079  * Based on:
6080  * Ext JS Library 1.1.1
6081  * Copyright(c) 2006-2007, Ext JS, LLC.
6082  *
6083  * Originally Released Under LGPL - original licence link has changed is not relivant.
6084  *
6085  * Fork - LGPL
6086  * <script type="text/javascript">
6087  */
6088
6089 /**
6090  * @class Roo.data.JsonReader
6091  * @extends Roo.data.DataReader
6092  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6093  * based on mappings in a provided Roo.data.Record constructor.
6094  * 
6095  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6096  * in the reply previously. 
6097  * 
6098  * <p>
6099  * Example code:
6100  * <pre><code>
6101 var RecordDef = Roo.data.Record.create([
6102     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6103     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6104 ]);
6105 var myReader = new Roo.data.JsonReader({
6106     totalProperty: "results",    // The property which contains the total dataset size (optional)
6107     root: "rows",                // The property which contains an Array of row objects
6108     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6109 }, RecordDef);
6110 </code></pre>
6111  * <p>
6112  * This would consume a JSON file like this:
6113  * <pre><code>
6114 { 'results': 2, 'rows': [
6115     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6116     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6117 }
6118 </code></pre>
6119  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6120  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6121  * paged from the remote server.
6122  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6123  * @cfg {String} root name of the property which contains the Array of row objects.
6124  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6125  * @constructor
6126  * Create a new JsonReader
6127  * @param {Object} meta Metadata configuration options
6128  * @param {Object} recordType Either an Array of field definition objects,
6129  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6130  */
6131 Roo.data.JsonReader = function(meta, recordType){
6132     
6133     meta = meta || {};
6134     // set some defaults:
6135     Roo.applyIf(meta, {
6136         totalProperty: 'total',
6137         successProperty : 'success',
6138         root : 'data',
6139         id : 'id'
6140     });
6141     
6142     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6143 };
6144 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6145     
6146     /**
6147      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6148      * Used by Store query builder to append _requestMeta to params.
6149      * 
6150      */
6151     metaFromRemote : false,
6152     /**
6153      * This method is only used by a DataProxy which has retrieved data from a remote server.
6154      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6155      * @return {Object} data A data block which is used by an Roo.data.Store object as
6156      * a cache of Roo.data.Records.
6157      */
6158     read : function(response){
6159         var json = response.responseText;
6160        
6161         var o = /* eval:var:o */ eval("("+json+")");
6162         if(!o) {
6163             throw {message: "JsonReader.read: Json object not found"};
6164         }
6165         
6166         if(o.metaData){
6167             
6168             delete this.ef;
6169             this.metaFromRemote = true;
6170             this.meta = o.metaData;
6171             this.recordType = Roo.data.Record.create(o.metaData.fields);
6172             this.onMetaChange(this.meta, this.recordType, o);
6173         }
6174         return this.readRecords(o);
6175     },
6176
6177     // private function a store will implement
6178     onMetaChange : function(meta, recordType, o){
6179
6180     },
6181
6182     /**
6183          * @ignore
6184          */
6185     simpleAccess: function(obj, subsc) {
6186         return obj[subsc];
6187     },
6188
6189         /**
6190          * @ignore
6191          */
6192     getJsonAccessor: function(){
6193         var re = /[\[\.]/;
6194         return function(expr) {
6195             try {
6196                 return(re.test(expr))
6197                     ? new Function("obj", "return obj." + expr)
6198                     : function(obj){
6199                         return obj[expr];
6200                     };
6201             } catch(e){}
6202             return Roo.emptyFn;
6203         };
6204     }(),
6205
6206     /**
6207      * Create a data block containing Roo.data.Records from an XML document.
6208      * @param {Object} o An object which contains an Array of row objects in the property specified
6209      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6210      * which contains the total size of the dataset.
6211      * @return {Object} data A data block which is used by an Roo.data.Store object as
6212      * a cache of Roo.data.Records.
6213      */
6214     readRecords : function(o){
6215         /**
6216          * After any data loads, the raw JSON data is available for further custom processing.
6217          * @type Object
6218          */
6219         this.jsonData = o;
6220         var s = this.meta, Record = this.recordType,
6221             f = Record.prototype.fields, fi = f.items, fl = f.length;
6222
6223 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6224         if (!this.ef) {
6225             if(s.totalProperty) {
6226                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6227                 }
6228                 if(s.successProperty) {
6229                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6230                 }
6231                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6232                 if (s.id) {
6233                         var g = this.getJsonAccessor(s.id);
6234                         this.getId = function(rec) {
6235                                 var r = g(rec);
6236                                 return (r === undefined || r === "") ? null : r;
6237                         };
6238                 } else {
6239                         this.getId = function(){return null;};
6240                 }
6241             this.ef = [];
6242             for(var jj = 0; jj < fl; jj++){
6243                 f = fi[jj];
6244                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6245                 this.ef[jj] = this.getJsonAccessor(map);
6246             }
6247         }
6248
6249         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6250         if(s.totalProperty){
6251             var vt = parseInt(this.getTotal(o), 10);
6252             if(!isNaN(vt)){
6253                 totalRecords = vt;
6254             }
6255         }
6256         if(s.successProperty){
6257             var vs = this.getSuccess(o);
6258             if(vs === false || vs === 'false'){
6259                 success = false;
6260             }
6261         }
6262         var records = [];
6263             for(var i = 0; i < c; i++){
6264                     var n = root[i];
6265                 var values = {};
6266                 var id = this.getId(n);
6267                 for(var j = 0; j < fl; j++){
6268                     f = fi[j];
6269                 var v = this.ef[j](n);
6270                 if (!f.convert) {
6271                     Roo.log('missing convert for ' + f.name);
6272                     Roo.log(f);
6273                     continue;
6274                 }
6275                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6276                 }
6277                 var record = new Record(values, id);
6278                 record.json = n;
6279                 records[i] = record;
6280             }
6281             return {
6282                 success : success,
6283                 records : records,
6284                 totalRecords : totalRecords
6285             };
6286     }
6287 });/*
6288  * Based on:
6289  * Ext JS Library 1.1.1
6290  * Copyright(c) 2006-2007, Ext JS, LLC.
6291  *
6292  * Originally Released Under LGPL - original licence link has changed is not relivant.
6293  *
6294  * Fork - LGPL
6295  * <script type="text/javascript">
6296  */
6297
6298 /**
6299  * @class Roo.data.XmlReader
6300  * @extends Roo.data.DataReader
6301  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6302  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6303  * <p>
6304  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6305  * header in the HTTP response must be set to "text/xml".</em>
6306  * <p>
6307  * Example code:
6308  * <pre><code>
6309 var RecordDef = Roo.data.Record.create([
6310    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6311    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6312 ]);
6313 var myReader = new Roo.data.XmlReader({
6314    totalRecords: "results", // The element which contains the total dataset size (optional)
6315    record: "row",           // The repeated element which contains row information
6316    id: "id"                 // The element within the row that provides an ID for the record (optional)
6317 }, RecordDef);
6318 </code></pre>
6319  * <p>
6320  * This would consume an XML file like this:
6321  * <pre><code>
6322 &lt;?xml?>
6323 &lt;dataset>
6324  &lt;results>2&lt;/results>
6325  &lt;row>
6326    &lt;id>1&lt;/id>
6327    &lt;name>Bill&lt;/name>
6328    &lt;occupation>Gardener&lt;/occupation>
6329  &lt;/row>
6330  &lt;row>
6331    &lt;id>2&lt;/id>
6332    &lt;name>Ben&lt;/name>
6333    &lt;occupation>Horticulturalist&lt;/occupation>
6334  &lt;/row>
6335 &lt;/dataset>
6336 </code></pre>
6337  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6338  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6339  * paged from the remote server.
6340  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6341  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6342  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6343  * a record identifier value.
6344  * @constructor
6345  * Create a new XmlReader
6346  * @param {Object} meta Metadata configuration options
6347  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6348  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6349  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6350  */
6351 Roo.data.XmlReader = function(meta, recordType){
6352     meta = meta || {};
6353     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6354 };
6355 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6356     /**
6357      * This method is only used by a DataProxy which has retrieved data from a remote server.
6358          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6359          * to contain a method called 'responseXML' that returns an XML document object.
6360      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6361      * a cache of Roo.data.Records.
6362      */
6363     read : function(response){
6364         var doc = response.responseXML;
6365         if(!doc) {
6366             throw {message: "XmlReader.read: XML Document not available"};
6367         }
6368         return this.readRecords(doc);
6369     },
6370
6371     /**
6372      * Create a data block containing Roo.data.Records from an XML document.
6373          * @param {Object} doc A parsed XML document.
6374      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6375      * a cache of Roo.data.Records.
6376      */
6377     readRecords : function(doc){
6378         /**
6379          * After any data loads/reads, the raw XML Document is available for further custom processing.
6380          * @type XMLDocument
6381          */
6382         this.xmlData = doc;
6383         var root = doc.documentElement || doc;
6384         var q = Roo.DomQuery;
6385         var recordType = this.recordType, fields = recordType.prototype.fields;
6386         var sid = this.meta.id;
6387         var totalRecords = 0, success = true;
6388         if(this.meta.totalRecords){
6389             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6390         }
6391         
6392         if(this.meta.success){
6393             var sv = q.selectValue(this.meta.success, root, true);
6394             success = sv !== false && sv !== 'false';
6395         }
6396         var records = [];
6397         var ns = q.select(this.meta.record, root);
6398         for(var i = 0, len = ns.length; i < len; i++) {
6399                 var n = ns[i];
6400                 var values = {};
6401                 var id = sid ? q.selectValue(sid, n) : undefined;
6402                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6403                     var f = fields.items[j];
6404                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6405                     v = f.convert(v);
6406                     values[f.name] = v;
6407                 }
6408                 var record = new recordType(values, id);
6409                 record.node = n;
6410                 records[records.length] = record;
6411             }
6412
6413             return {
6414                 success : success,
6415                 records : records,
6416                 totalRecords : totalRecords || records.length
6417             };
6418     }
6419 });/*
6420  * Based on:
6421  * Ext JS Library 1.1.1
6422  * Copyright(c) 2006-2007, Ext JS, LLC.
6423  *
6424  * Originally Released Under LGPL - original licence link has changed is not relivant.
6425  *
6426  * Fork - LGPL
6427  * <script type="text/javascript">
6428  */
6429
6430 /**
6431  * @class Roo.data.ArrayReader
6432  * @extends Roo.data.DataReader
6433  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6434  * Each element of that Array represents a row of data fields. The
6435  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6436  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6437  * <p>
6438  * Example code:.
6439  * <pre><code>
6440 var RecordDef = Roo.data.Record.create([
6441     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6442     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6443 ]);
6444 var myReader = new Roo.data.ArrayReader({
6445     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6446 }, RecordDef);
6447 </code></pre>
6448  * <p>
6449  * This would consume an Array like this:
6450  * <pre><code>
6451 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6452   </code></pre>
6453  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6454  * @constructor
6455  * Create a new JsonReader
6456  * @param {Object} meta Metadata configuration options.
6457  * @param {Object} recordType Either an Array of field definition objects
6458  * as specified to {@link Roo.data.Record#create},
6459  * or an {@link Roo.data.Record} object
6460  * created using {@link Roo.data.Record#create}.
6461  */
6462 Roo.data.ArrayReader = function(meta, recordType){
6463     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6464 };
6465
6466 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6467     /**
6468      * Create a data block containing Roo.data.Records from an XML document.
6469      * @param {Object} o An Array of row objects which represents the dataset.
6470      * @return {Object} data A data block which is used by an Roo.data.Store object as
6471      * a cache of Roo.data.Records.
6472      */
6473     readRecords : function(o){
6474         var sid = this.meta ? this.meta.id : null;
6475         var recordType = this.recordType, fields = recordType.prototype.fields;
6476         var records = [];
6477         var root = o;
6478             for(var i = 0; i < root.length; i++){
6479                     var n = root[i];
6480                 var values = {};
6481                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6482                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6483                 var f = fields.items[j];
6484                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6485                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6486                 v = f.convert(v);
6487                 values[f.name] = v;
6488             }
6489                 var record = new recordType(values, id);
6490                 record.json = n;
6491                 records[records.length] = record;
6492             }
6493             return {
6494                 records : records,
6495                 totalRecords : records.length
6496             };
6497     }
6498 });/*
6499  * Based on:
6500  * Ext JS Library 1.1.1
6501  * Copyright(c) 2006-2007, Ext JS, LLC.
6502  *
6503  * Originally Released Under LGPL - original licence link has changed is not relivant.
6504  *
6505  * Fork - LGPL
6506  * <script type="text/javascript">
6507  */
6508
6509
6510 /**
6511  * @class Roo.data.Tree
6512  * @extends Roo.util.Observable
6513  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6514  * in the tree have most standard DOM functionality.
6515  * @constructor
6516  * @param {Node} root (optional) The root node
6517  */
6518 Roo.data.Tree = function(root){
6519    this.nodeHash = {};
6520    /**
6521     * The root node for this tree
6522     * @type Node
6523     */
6524    this.root = null;
6525    if(root){
6526        this.setRootNode(root);
6527    }
6528    this.addEvents({
6529        /**
6530         * @event append
6531         * Fires when a new child node is appended to a node in this tree.
6532         * @param {Tree} tree The owner tree
6533         * @param {Node} parent The parent node
6534         * @param {Node} node The newly appended node
6535         * @param {Number} index The index of the newly appended node
6536         */
6537        "append" : true,
6538        /**
6539         * @event remove
6540         * Fires when a child node is removed from a node in this tree.
6541         * @param {Tree} tree The owner tree
6542         * @param {Node} parent The parent node
6543         * @param {Node} node The child node removed
6544         */
6545        "remove" : true,
6546        /**
6547         * @event move
6548         * Fires when a node is moved to a new location in the tree
6549         * @param {Tree} tree The owner tree
6550         * @param {Node} node The node moved
6551         * @param {Node} oldParent The old parent of this node
6552         * @param {Node} newParent The new parent of this node
6553         * @param {Number} index The index it was moved to
6554         */
6555        "move" : true,
6556        /**
6557         * @event insert
6558         * Fires when a new child node is inserted in a node in this tree.
6559         * @param {Tree} tree The owner tree
6560         * @param {Node} parent The parent node
6561         * @param {Node} node The child node inserted
6562         * @param {Node} refNode The child node the node was inserted before
6563         */
6564        "insert" : true,
6565        /**
6566         * @event beforeappend
6567         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6568         * @param {Tree} tree The owner tree
6569         * @param {Node} parent The parent node
6570         * @param {Node} node The child node to be appended
6571         */
6572        "beforeappend" : true,
6573        /**
6574         * @event beforeremove
6575         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6576         * @param {Tree} tree The owner tree
6577         * @param {Node} parent The parent node
6578         * @param {Node} node The child node to be removed
6579         */
6580        "beforeremove" : true,
6581        /**
6582         * @event beforemove
6583         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} node The node being moved
6586         * @param {Node} oldParent The parent of the node
6587         * @param {Node} newParent The new parent the node is moving to
6588         * @param {Number} index The index it is being moved to
6589         */
6590        "beforemove" : true,
6591        /**
6592         * @event beforeinsert
6593         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6594         * @param {Tree} tree The owner tree
6595         * @param {Node} parent The parent node
6596         * @param {Node} node The child node to be inserted
6597         * @param {Node} refNode The child node the node is being inserted before
6598         */
6599        "beforeinsert" : true
6600    });
6601
6602     Roo.data.Tree.superclass.constructor.call(this);
6603 };
6604
6605 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6606     pathSeparator: "/",
6607
6608     proxyNodeEvent : function(){
6609         return this.fireEvent.apply(this, arguments);
6610     },
6611
6612     /**
6613      * Returns the root node for this tree.
6614      * @return {Node}
6615      */
6616     getRootNode : function(){
6617         return this.root;
6618     },
6619
6620     /**
6621      * Sets the root node for this tree.
6622      * @param {Node} node
6623      * @return {Node}
6624      */
6625     setRootNode : function(node){
6626         this.root = node;
6627         node.ownerTree = this;
6628         node.isRoot = true;
6629         this.registerNode(node);
6630         return node;
6631     },
6632
6633     /**
6634      * Gets a node in this tree by its id.
6635      * @param {String} id
6636      * @return {Node}
6637      */
6638     getNodeById : function(id){
6639         return this.nodeHash[id];
6640     },
6641
6642     registerNode : function(node){
6643         this.nodeHash[node.id] = node;
6644     },
6645
6646     unregisterNode : function(node){
6647         delete this.nodeHash[node.id];
6648     },
6649
6650     toString : function(){
6651         return "[Tree"+(this.id?" "+this.id:"")+"]";
6652     }
6653 });
6654
6655 /**
6656  * @class Roo.data.Node
6657  * @extends Roo.util.Observable
6658  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6659  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6660  * @constructor
6661  * @param {Object} attributes The attributes/config for the node
6662  */
6663 Roo.data.Node = function(attributes){
6664     /**
6665      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6666      * @type {Object}
6667      */
6668     this.attributes = attributes || {};
6669     this.leaf = this.attributes.leaf;
6670     /**
6671      * The node id. @type String
6672      */
6673     this.id = this.attributes.id;
6674     if(!this.id){
6675         this.id = Roo.id(null, "ynode-");
6676         this.attributes.id = this.id;
6677     }
6678      
6679     
6680     /**
6681      * All child nodes of this node. @type Array
6682      */
6683     this.childNodes = [];
6684     if(!this.childNodes.indexOf){ // indexOf is a must
6685         this.childNodes.indexOf = function(o){
6686             for(var i = 0, len = this.length; i < len; i++){
6687                 if(this[i] == o) {
6688                     return i;
6689                 }
6690             }
6691             return -1;
6692         };
6693     }
6694     /**
6695      * The parent node for this node. @type Node
6696      */
6697     this.parentNode = null;
6698     /**
6699      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6700      */
6701     this.firstChild = null;
6702     /**
6703      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6704      */
6705     this.lastChild = null;
6706     /**
6707      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6708      */
6709     this.previousSibling = null;
6710     /**
6711      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6712      */
6713     this.nextSibling = null;
6714
6715     this.addEvents({
6716        /**
6717         * @event append
6718         * Fires when a new child node is appended
6719         * @param {Tree} tree The owner tree
6720         * @param {Node} this This node
6721         * @param {Node} node The newly appended node
6722         * @param {Number} index The index of the newly appended node
6723         */
6724        "append" : true,
6725        /**
6726         * @event remove
6727         * Fires when a child node is removed
6728         * @param {Tree} tree The owner tree
6729         * @param {Node} this This node
6730         * @param {Node} node The removed node
6731         */
6732        "remove" : true,
6733        /**
6734         * @event move
6735         * Fires when this node is moved to a new location in the tree
6736         * @param {Tree} tree The owner tree
6737         * @param {Node} this This node
6738         * @param {Node} oldParent The old parent of this node
6739         * @param {Node} newParent The new parent of this node
6740         * @param {Number} index The index it was moved to
6741         */
6742        "move" : true,
6743        /**
6744         * @event insert
6745         * Fires when a new child node is inserted.
6746         * @param {Tree} tree The owner tree
6747         * @param {Node} this This node
6748         * @param {Node} node The child node inserted
6749         * @param {Node} refNode The child node the node was inserted before
6750         */
6751        "insert" : true,
6752        /**
6753         * @event beforeappend
6754         * Fires before a new child is appended, return false to cancel the append.
6755         * @param {Tree} tree The owner tree
6756         * @param {Node} this This node
6757         * @param {Node} node The child node to be appended
6758         */
6759        "beforeappend" : true,
6760        /**
6761         * @event beforeremove
6762         * Fires before a child is removed, return false to cancel the remove.
6763         * @param {Tree} tree The owner tree
6764         * @param {Node} this This node
6765         * @param {Node} node The child node to be removed
6766         */
6767        "beforeremove" : true,
6768        /**
6769         * @event beforemove
6770         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6771         * @param {Tree} tree The owner tree
6772         * @param {Node} this This node
6773         * @param {Node} oldParent The parent of this node
6774         * @param {Node} newParent The new parent this node is moving to
6775         * @param {Number} index The index it is being moved to
6776         */
6777        "beforemove" : true,
6778        /**
6779         * @event beforeinsert
6780         * Fires before a new child is inserted, return false to cancel the insert.
6781         * @param {Tree} tree The owner tree
6782         * @param {Node} this This node
6783         * @param {Node} node The child node to be inserted
6784         * @param {Node} refNode The child node the node is being inserted before
6785         */
6786        "beforeinsert" : true
6787    });
6788     this.listeners = this.attributes.listeners;
6789     Roo.data.Node.superclass.constructor.call(this);
6790 };
6791
6792 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6793     fireEvent : function(evtName){
6794         // first do standard event for this node
6795         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6796             return false;
6797         }
6798         // then bubble it up to the tree if the event wasn't cancelled
6799         var ot = this.getOwnerTree();
6800         if(ot){
6801             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6802                 return false;
6803             }
6804         }
6805         return true;
6806     },
6807
6808     /**
6809      * Returns true if this node is a leaf
6810      * @return {Boolean}
6811      */
6812     isLeaf : function(){
6813         return this.leaf === true;
6814     },
6815
6816     // private
6817     setFirstChild : function(node){
6818         this.firstChild = node;
6819     },
6820
6821     //private
6822     setLastChild : function(node){
6823         this.lastChild = node;
6824     },
6825
6826
6827     /**
6828      * Returns true if this node is the last child of its parent
6829      * @return {Boolean}
6830      */
6831     isLast : function(){
6832        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6833     },
6834
6835     /**
6836      * Returns true if this node is the first child of its parent
6837      * @return {Boolean}
6838      */
6839     isFirst : function(){
6840        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6841     },
6842
6843     hasChildNodes : function(){
6844         return !this.isLeaf() && this.childNodes.length > 0;
6845     },
6846
6847     /**
6848      * Insert node(s) as the last child node of this node.
6849      * @param {Node/Array} node The node or Array of nodes to append
6850      * @return {Node} The appended node if single append, or null if an array was passed
6851      */
6852     appendChild : function(node){
6853         var multi = false;
6854         if(node instanceof Array){
6855             multi = node;
6856         }else if(arguments.length > 1){
6857             multi = arguments;
6858         }
6859         // if passed an array or multiple args do them one by one
6860         if(multi){
6861             for(var i = 0, len = multi.length; i < len; i++) {
6862                 this.appendChild(multi[i]);
6863             }
6864         }else{
6865             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6866                 return false;
6867             }
6868             var index = this.childNodes.length;
6869             var oldParent = node.parentNode;
6870             // it's a move, make sure we move it cleanly
6871             if(oldParent){
6872                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6873                     return false;
6874                 }
6875                 oldParent.removeChild(node);
6876             }
6877             index = this.childNodes.length;
6878             if(index == 0){
6879                 this.setFirstChild(node);
6880             }
6881             this.childNodes.push(node);
6882             node.parentNode = this;
6883             var ps = this.childNodes[index-1];
6884             if(ps){
6885                 node.previousSibling = ps;
6886                 ps.nextSibling = node;
6887             }else{
6888                 node.previousSibling = null;
6889             }
6890             node.nextSibling = null;
6891             this.setLastChild(node);
6892             node.setOwnerTree(this.getOwnerTree());
6893             this.fireEvent("append", this.ownerTree, this, node, index);
6894             if(oldParent){
6895                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6896             }
6897             return node;
6898         }
6899     },
6900
6901     /**
6902      * Removes a child node from this node.
6903      * @param {Node} node The node to remove
6904      * @return {Node} The removed node
6905      */
6906     removeChild : function(node){
6907         var index = this.childNodes.indexOf(node);
6908         if(index == -1){
6909             return false;
6910         }
6911         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6912             return false;
6913         }
6914
6915         // remove it from childNodes collection
6916         this.childNodes.splice(index, 1);
6917
6918         // update siblings
6919         if(node.previousSibling){
6920             node.previousSibling.nextSibling = node.nextSibling;
6921         }
6922         if(node.nextSibling){
6923             node.nextSibling.previousSibling = node.previousSibling;
6924         }
6925
6926         // update child refs
6927         if(this.firstChild == node){
6928             this.setFirstChild(node.nextSibling);
6929         }
6930         if(this.lastChild == node){
6931             this.setLastChild(node.previousSibling);
6932         }
6933
6934         node.setOwnerTree(null);
6935         // clear any references from the node
6936         node.parentNode = null;
6937         node.previousSibling = null;
6938         node.nextSibling = null;
6939         this.fireEvent("remove", this.ownerTree, this, node);
6940         return node;
6941     },
6942
6943     /**
6944      * Inserts the first node before the second node in this nodes childNodes collection.
6945      * @param {Node} node The node to insert
6946      * @param {Node} refNode The node to insert before (if null the node is appended)
6947      * @return {Node} The inserted node
6948      */
6949     insertBefore : function(node, refNode){
6950         if(!refNode){ // like standard Dom, refNode can be null for append
6951             return this.appendChild(node);
6952         }
6953         // nothing to do
6954         if(node == refNode){
6955             return false;
6956         }
6957
6958         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6959             return false;
6960         }
6961         var index = this.childNodes.indexOf(refNode);
6962         var oldParent = node.parentNode;
6963         var refIndex = index;
6964
6965         // when moving internally, indexes will change after remove
6966         if(oldParent == this && this.childNodes.indexOf(node) < index){
6967             refIndex--;
6968         }
6969
6970         // it's a move, make sure we move it cleanly
6971         if(oldParent){
6972             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6973                 return false;
6974             }
6975             oldParent.removeChild(node);
6976         }
6977         if(refIndex == 0){
6978             this.setFirstChild(node);
6979         }
6980         this.childNodes.splice(refIndex, 0, node);
6981         node.parentNode = this;
6982         var ps = this.childNodes[refIndex-1];
6983         if(ps){
6984             node.previousSibling = ps;
6985             ps.nextSibling = node;
6986         }else{
6987             node.previousSibling = null;
6988         }
6989         node.nextSibling = refNode;
6990         refNode.previousSibling = node;
6991         node.setOwnerTree(this.getOwnerTree());
6992         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6993         if(oldParent){
6994             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6995         }
6996         return node;
6997     },
6998
6999     /**
7000      * Returns the child node at the specified index.
7001      * @param {Number} index
7002      * @return {Node}
7003      */
7004     item : function(index){
7005         return this.childNodes[index];
7006     },
7007
7008     /**
7009      * Replaces one child node in this node with another.
7010      * @param {Node} newChild The replacement node
7011      * @param {Node} oldChild The node to replace
7012      * @return {Node} The replaced node
7013      */
7014     replaceChild : function(newChild, oldChild){
7015         this.insertBefore(newChild, oldChild);
7016         this.removeChild(oldChild);
7017         return oldChild;
7018     },
7019
7020     /**
7021      * Returns the index of a child node
7022      * @param {Node} node
7023      * @return {Number} The index of the node or -1 if it was not found
7024      */
7025     indexOf : function(child){
7026         return this.childNodes.indexOf(child);
7027     },
7028
7029     /**
7030      * Returns the tree this node is in.
7031      * @return {Tree}
7032      */
7033     getOwnerTree : function(){
7034         // if it doesn't have one, look for one
7035         if(!this.ownerTree){
7036             var p = this;
7037             while(p){
7038                 if(p.ownerTree){
7039                     this.ownerTree = p.ownerTree;
7040                     break;
7041                 }
7042                 p = p.parentNode;
7043             }
7044         }
7045         return this.ownerTree;
7046     },
7047
7048     /**
7049      * Returns depth of this node (the root node has a depth of 0)
7050      * @return {Number}
7051      */
7052     getDepth : function(){
7053         var depth = 0;
7054         var p = this;
7055         while(p.parentNode){
7056             ++depth;
7057             p = p.parentNode;
7058         }
7059         return depth;
7060     },
7061
7062     // private
7063     setOwnerTree : function(tree){
7064         // if it's move, we need to update everyone
7065         if(tree != this.ownerTree){
7066             if(this.ownerTree){
7067                 this.ownerTree.unregisterNode(this);
7068             }
7069             this.ownerTree = tree;
7070             var cs = this.childNodes;
7071             for(var i = 0, len = cs.length; i < len; i++) {
7072                 cs[i].setOwnerTree(tree);
7073             }
7074             if(tree){
7075                 tree.registerNode(this);
7076             }
7077         }
7078     },
7079
7080     /**
7081      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7082      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7083      * @return {String} The path
7084      */
7085     getPath : function(attr){
7086         attr = attr || "id";
7087         var p = this.parentNode;
7088         var b = [this.attributes[attr]];
7089         while(p){
7090             b.unshift(p.attributes[attr]);
7091             p = p.parentNode;
7092         }
7093         var sep = this.getOwnerTree().pathSeparator;
7094         return sep + b.join(sep);
7095     },
7096
7097     /**
7098      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7099      * function call will be the scope provided or the current node. The arguments to the function
7100      * will be the args provided or the current node. If the function returns false at any point,
7101      * the bubble is stopped.
7102      * @param {Function} fn The function to call
7103      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7104      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7105      */
7106     bubble : function(fn, scope, args){
7107         var p = this;
7108         while(p){
7109             if(fn.call(scope || p, args || p) === false){
7110                 break;
7111             }
7112             p = p.parentNode;
7113         }
7114     },
7115
7116     /**
7117      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7118      * function call will be the scope provided or the current node. The arguments to the function
7119      * will be the args provided or the current node. If the function returns false at any point,
7120      * the cascade is stopped on that branch.
7121      * @param {Function} fn The function to call
7122      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7123      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7124      */
7125     cascade : function(fn, scope, args){
7126         if(fn.call(scope || this, args || this) !== false){
7127             var cs = this.childNodes;
7128             for(var i = 0, len = cs.length; i < len; i++) {
7129                 cs[i].cascade(fn, scope, args);
7130             }
7131         }
7132     },
7133
7134     /**
7135      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7136      * function call will be the scope provided or the current node. The arguments to the function
7137      * will be the args provided or the current node. If the function returns false at any point,
7138      * the iteration stops.
7139      * @param {Function} fn The function to call
7140      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7141      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7142      */
7143     eachChild : function(fn, scope, args){
7144         var cs = this.childNodes;
7145         for(var i = 0, len = cs.length; i < len; i++) {
7146                 if(fn.call(scope || this, args || cs[i]) === false){
7147                     break;
7148                 }
7149         }
7150     },
7151
7152     /**
7153      * Finds the first child that has the attribute with the specified value.
7154      * @param {String} attribute The attribute name
7155      * @param {Mixed} value The value to search for
7156      * @return {Node} The found child or null if none was found
7157      */
7158     findChild : function(attribute, value){
7159         var cs = this.childNodes;
7160         for(var i = 0, len = cs.length; i < len; i++) {
7161                 if(cs[i].attributes[attribute] == value){
7162                     return cs[i];
7163                 }
7164         }
7165         return null;
7166     },
7167
7168     /**
7169      * Finds the first child by a custom function. The child matches if the function passed
7170      * returns true.
7171      * @param {Function} fn
7172      * @param {Object} scope (optional)
7173      * @return {Node} The found child or null if none was found
7174      */
7175     findChildBy : function(fn, scope){
7176         var cs = this.childNodes;
7177         for(var i = 0, len = cs.length; i < len; i++) {
7178                 if(fn.call(scope||cs[i], cs[i]) === true){
7179                     return cs[i];
7180                 }
7181         }
7182         return null;
7183     },
7184
7185     /**
7186      * Sorts this nodes children using the supplied sort function
7187      * @param {Function} fn
7188      * @param {Object} scope (optional)
7189      */
7190     sort : function(fn, scope){
7191         var cs = this.childNodes;
7192         var len = cs.length;
7193         if(len > 0){
7194             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7195             cs.sort(sortFn);
7196             for(var i = 0; i < len; i++){
7197                 var n = cs[i];
7198                 n.previousSibling = cs[i-1];
7199                 n.nextSibling = cs[i+1];
7200                 if(i == 0){
7201                     this.setFirstChild(n);
7202                 }
7203                 if(i == len-1){
7204                     this.setLastChild(n);
7205                 }
7206             }
7207         }
7208     },
7209
7210     /**
7211      * Returns true if this node is an ancestor (at any point) of the passed node.
7212      * @param {Node} node
7213      * @return {Boolean}
7214      */
7215     contains : function(node){
7216         return node.isAncestor(this);
7217     },
7218
7219     /**
7220      * Returns true if the passed node is an ancestor (at any point) of this node.
7221      * @param {Node} node
7222      * @return {Boolean}
7223      */
7224     isAncestor : function(node){
7225         var p = this.parentNode;
7226         while(p){
7227             if(p == node){
7228                 return true;
7229             }
7230             p = p.parentNode;
7231         }
7232         return false;
7233     },
7234
7235     toString : function(){
7236         return "[Node"+(this.id?" "+this.id:"")+"]";
7237     }
7238 });/*
7239  * Based on:
7240  * Ext JS Library 1.1.1
7241  * Copyright(c) 2006-2007, Ext JS, LLC.
7242  *
7243  * Originally Released Under LGPL - original licence link has changed is not relivant.
7244  *
7245  * Fork - LGPL
7246  * <script type="text/javascript">
7247  */
7248  
7249
7250 /**
7251  * @class Roo.ComponentMgr
7252  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7253  * @singleton
7254  */
7255 Roo.ComponentMgr = function(){
7256     var all = new Roo.util.MixedCollection();
7257
7258     return {
7259         /**
7260          * Registers a component.
7261          * @param {Roo.Component} c The component
7262          */
7263         register : function(c){
7264             all.add(c);
7265         },
7266
7267         /**
7268          * Unregisters a component.
7269          * @param {Roo.Component} c The component
7270          */
7271         unregister : function(c){
7272             all.remove(c);
7273         },
7274
7275         /**
7276          * Returns a component by id
7277          * @param {String} id The component id
7278          */
7279         get : function(id){
7280             return all.get(id);
7281         },
7282
7283         /**
7284          * Registers a function that will be called when a specified component is added to ComponentMgr
7285          * @param {String} id The component id
7286          * @param {Funtction} fn The callback function
7287          * @param {Object} scope The scope of the callback
7288          */
7289         onAvailable : function(id, fn, scope){
7290             all.on("add", function(index, o){
7291                 if(o.id == id){
7292                     fn.call(scope || o, o);
7293                     all.un("add", fn, scope);
7294                 }
7295             });
7296         }
7297     };
7298 }();/*
7299  * Based on:
7300  * Ext JS Library 1.1.1
7301  * Copyright(c) 2006-2007, Ext JS, LLC.
7302  *
7303  * Originally Released Under LGPL - original licence link has changed is not relivant.
7304  *
7305  * Fork - LGPL
7306  * <script type="text/javascript">
7307  */
7308  
7309 /**
7310  * @class Roo.Component
7311  * @extends Roo.util.Observable
7312  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7313  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7314  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7315  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7316  * All visual components (widgets) that require rendering into a layout should subclass Component.
7317  * @constructor
7318  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7319  * 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
7320  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7321  */
7322 Roo.Component = function(config){
7323     config = config || {};
7324     if(config.tagName || config.dom || typeof config == "string"){ // element object
7325         config = {el: config, id: config.id || config};
7326     }
7327     this.initialConfig = config;
7328
7329     Roo.apply(this, config);
7330     this.addEvents({
7331         /**
7332          * @event disable
7333          * Fires after the component is disabled.
7334              * @param {Roo.Component} this
7335              */
7336         disable : true,
7337         /**
7338          * @event enable
7339          * Fires after the component is enabled.
7340              * @param {Roo.Component} this
7341              */
7342         enable : true,
7343         /**
7344          * @event beforeshow
7345          * Fires before the component is shown.  Return false to stop the show.
7346              * @param {Roo.Component} this
7347              */
7348         beforeshow : true,
7349         /**
7350          * @event show
7351          * Fires after the component is shown.
7352              * @param {Roo.Component} this
7353              */
7354         show : true,
7355         /**
7356          * @event beforehide
7357          * Fires before the component is hidden. Return false to stop the hide.
7358              * @param {Roo.Component} this
7359              */
7360         beforehide : true,
7361         /**
7362          * @event hide
7363          * Fires after the component is hidden.
7364              * @param {Roo.Component} this
7365              */
7366         hide : true,
7367         /**
7368          * @event beforerender
7369          * Fires before the component is rendered. Return false to stop the render.
7370              * @param {Roo.Component} this
7371              */
7372         beforerender : true,
7373         /**
7374          * @event render
7375          * Fires after the component is rendered.
7376              * @param {Roo.Component} this
7377              */
7378         render : true,
7379         /**
7380          * @event beforedestroy
7381          * Fires before the component is destroyed. Return false to stop the destroy.
7382              * @param {Roo.Component} this
7383              */
7384         beforedestroy : true,
7385         /**
7386          * @event destroy
7387          * Fires after the component is destroyed.
7388              * @param {Roo.Component} this
7389              */
7390         destroy : true
7391     });
7392     if(!this.id){
7393         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7394     }
7395     Roo.ComponentMgr.register(this);
7396     Roo.Component.superclass.constructor.call(this);
7397     this.initComponent();
7398     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7399         this.render(this.renderTo);
7400         delete this.renderTo;
7401     }
7402 };
7403
7404 /** @private */
7405 Roo.Component.AUTO_ID = 1000;
7406
7407 Roo.extend(Roo.Component, Roo.util.Observable, {
7408     /**
7409      * @scope Roo.Component.prototype
7410      * @type {Boolean}
7411      * true if this component is hidden. Read-only.
7412      */
7413     hidden : false,
7414     /**
7415      * @type {Boolean}
7416      * true if this component is disabled. Read-only.
7417      */
7418     disabled : false,
7419     /**
7420      * @type {Boolean}
7421      * true if this component has been rendered. Read-only.
7422      */
7423     rendered : false,
7424     
7425     /** @cfg {String} disableClass
7426      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7427      */
7428     disabledClass : "x-item-disabled",
7429         /** @cfg {Boolean} allowDomMove
7430          * Whether the component can move the Dom node when rendering (defaults to true).
7431          */
7432     allowDomMove : true,
7433     /** @cfg {String} hideMode
7434      * How this component should hidden. Supported values are
7435      * "visibility" (css visibility), "offsets" (negative offset position) and
7436      * "display" (css display) - defaults to "display".
7437      */
7438     hideMode: 'display',
7439
7440     /** @private */
7441     ctype : "Roo.Component",
7442
7443     /**
7444      * @cfg {String} actionMode 
7445      * which property holds the element that used for  hide() / show() / disable() / enable()
7446      * default is 'el' 
7447      */
7448     actionMode : "el",
7449
7450     /** @private */
7451     getActionEl : function(){
7452         return this[this.actionMode];
7453     },
7454
7455     initComponent : Roo.emptyFn,
7456     /**
7457      * If this is a lazy rendering component, render it to its container element.
7458      * @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.
7459      */
7460     render : function(container, position){
7461         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7462             if(!container && this.el){
7463                 this.el = Roo.get(this.el);
7464                 container = this.el.dom.parentNode;
7465                 this.allowDomMove = false;
7466             }
7467             this.container = Roo.get(container);
7468             this.rendered = true;
7469             if(position !== undefined){
7470                 if(typeof position == 'number'){
7471                     position = this.container.dom.childNodes[position];
7472                 }else{
7473                     position = Roo.getDom(position);
7474                 }
7475             }
7476             this.onRender(this.container, position || null);
7477             if(this.cls){
7478                 this.el.addClass(this.cls);
7479                 delete this.cls;
7480             }
7481             if(this.style){
7482                 this.el.applyStyles(this.style);
7483                 delete this.style;
7484             }
7485             this.fireEvent("render", this);
7486             this.afterRender(this.container);
7487             if(this.hidden){
7488                 this.hide();
7489             }
7490             if(this.disabled){
7491                 this.disable();
7492             }
7493         }
7494         return this;
7495     },
7496
7497     /** @private */
7498     // default function is not really useful
7499     onRender : function(ct, position){
7500         if(this.el){
7501             this.el = Roo.get(this.el);
7502             if(this.allowDomMove !== false){
7503                 ct.dom.insertBefore(this.el.dom, position);
7504             }
7505         }
7506     },
7507
7508     /** @private */
7509     getAutoCreate : function(){
7510         var cfg = typeof this.autoCreate == "object" ?
7511                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7512         if(this.id && !cfg.id){
7513             cfg.id = this.id;
7514         }
7515         return cfg;
7516     },
7517
7518     /** @private */
7519     afterRender : Roo.emptyFn,
7520
7521     /**
7522      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7523      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7524      */
7525     destroy : function(){
7526         if(this.fireEvent("beforedestroy", this) !== false){
7527             this.purgeListeners();
7528             this.beforeDestroy();
7529             if(this.rendered){
7530                 this.el.removeAllListeners();
7531                 this.el.remove();
7532                 if(this.actionMode == "container"){
7533                     this.container.remove();
7534                 }
7535             }
7536             this.onDestroy();
7537             Roo.ComponentMgr.unregister(this);
7538             this.fireEvent("destroy", this);
7539         }
7540     },
7541
7542         /** @private */
7543     beforeDestroy : function(){
7544
7545     },
7546
7547         /** @private */
7548         onDestroy : function(){
7549
7550     },
7551
7552     /**
7553      * Returns the underlying {@link Roo.Element}.
7554      * @return {Roo.Element} The element
7555      */
7556     getEl : function(){
7557         return this.el;
7558     },
7559
7560     /**
7561      * Returns the id of this component.
7562      * @return {String}
7563      */
7564     getId : function(){
7565         return this.id;
7566     },
7567
7568     /**
7569      * Try to focus this component.
7570      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7571      * @return {Roo.Component} this
7572      */
7573     focus : function(selectText){
7574         if(this.rendered){
7575             this.el.focus();
7576             if(selectText === true){
7577                 this.el.dom.select();
7578             }
7579         }
7580         return this;
7581     },
7582
7583     /** @private */
7584     blur : function(){
7585         if(this.rendered){
7586             this.el.blur();
7587         }
7588         return this;
7589     },
7590
7591     /**
7592      * Disable this component.
7593      * @return {Roo.Component} this
7594      */
7595     disable : function(){
7596         if(this.rendered){
7597             this.onDisable();
7598         }
7599         this.disabled = true;
7600         this.fireEvent("disable", this);
7601         return this;
7602     },
7603
7604         // private
7605     onDisable : function(){
7606         this.getActionEl().addClass(this.disabledClass);
7607         this.el.dom.disabled = true;
7608     },
7609
7610     /**
7611      * Enable this component.
7612      * @return {Roo.Component} this
7613      */
7614     enable : function(){
7615         if(this.rendered){
7616             this.onEnable();
7617         }
7618         this.disabled = false;
7619         this.fireEvent("enable", this);
7620         return this;
7621     },
7622
7623         // private
7624     onEnable : function(){
7625         this.getActionEl().removeClass(this.disabledClass);
7626         this.el.dom.disabled = false;
7627     },
7628
7629     /**
7630      * Convenience function for setting disabled/enabled by boolean.
7631      * @param {Boolean} disabled
7632      */
7633     setDisabled : function(disabled){
7634         this[disabled ? "disable" : "enable"]();
7635     },
7636
7637     /**
7638      * Show this component.
7639      * @return {Roo.Component} this
7640      */
7641     show: function(){
7642         if(this.fireEvent("beforeshow", this) !== false){
7643             this.hidden = false;
7644             if(this.rendered){
7645                 this.onShow();
7646             }
7647             this.fireEvent("show", this);
7648         }
7649         return this;
7650     },
7651
7652     // private
7653     onShow : function(){
7654         var ae = this.getActionEl();
7655         if(this.hideMode == 'visibility'){
7656             ae.dom.style.visibility = "visible";
7657         }else if(this.hideMode == 'offsets'){
7658             ae.removeClass('x-hidden');
7659         }else{
7660             ae.dom.style.display = "";
7661         }
7662     },
7663
7664     /**
7665      * Hide this component.
7666      * @return {Roo.Component} this
7667      */
7668     hide: function(){
7669         if(this.fireEvent("beforehide", this) !== false){
7670             this.hidden = true;
7671             if(this.rendered){
7672                 this.onHide();
7673             }
7674             this.fireEvent("hide", this);
7675         }
7676         return this;
7677     },
7678
7679     // private
7680     onHide : function(){
7681         var ae = this.getActionEl();
7682         if(this.hideMode == 'visibility'){
7683             ae.dom.style.visibility = "hidden";
7684         }else if(this.hideMode == 'offsets'){
7685             ae.addClass('x-hidden');
7686         }else{
7687             ae.dom.style.display = "none";
7688         }
7689     },
7690
7691     /**
7692      * Convenience function to hide or show this component by boolean.
7693      * @param {Boolean} visible True to show, false to hide
7694      * @return {Roo.Component} this
7695      */
7696     setVisible: function(visible){
7697         if(visible) {
7698             this.show();
7699         }else{
7700             this.hide();
7701         }
7702         return this;
7703     },
7704
7705     /**
7706      * Returns true if this component is visible.
7707      */
7708     isVisible : function(){
7709         return this.getActionEl().isVisible();
7710     },
7711
7712     cloneConfig : function(overrides){
7713         overrides = overrides || {};
7714         var id = overrides.id || Roo.id();
7715         var cfg = Roo.applyIf(overrides, this.initialConfig);
7716         cfg.id = id; // prevent dup id
7717         return new this.constructor(cfg);
7718     }
7719 });/*
7720  * Based on:
7721  * Ext JS Library 1.1.1
7722  * Copyright(c) 2006-2007, Ext JS, LLC.
7723  *
7724  * Originally Released Under LGPL - original licence link has changed is not relivant.
7725  *
7726  * Fork - LGPL
7727  * <script type="text/javascript">
7728  */
7729  (function(){ 
7730 /**
7731  * @class Roo.Layer
7732  * @extends Roo.Element
7733  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7734  * automatic maintaining of shadow/shim positions.
7735  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7736  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7737  * you can pass a string with a CSS class name. False turns off the shadow.
7738  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7739  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7740  * @cfg {String} cls CSS class to add to the element
7741  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7742  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7743  * @constructor
7744  * @param {Object} config An object with config options.
7745  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7746  */
7747
7748 Roo.Layer = function(config, existingEl){
7749     config = config || {};
7750     var dh = Roo.DomHelper;
7751     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7752     if(existingEl){
7753         this.dom = Roo.getDom(existingEl);
7754     }
7755     if(!this.dom){
7756         var o = config.dh || {tag: "div", cls: "x-layer"};
7757         this.dom = dh.append(pel, o);
7758     }
7759     if(config.cls){
7760         this.addClass(config.cls);
7761     }
7762     this.constrain = config.constrain !== false;
7763     this.visibilityMode = Roo.Element.VISIBILITY;
7764     if(config.id){
7765         this.id = this.dom.id = config.id;
7766     }else{
7767         this.id = Roo.id(this.dom);
7768     }
7769     this.zindex = config.zindex || this.getZIndex();
7770     this.position("absolute", this.zindex);
7771     if(config.shadow){
7772         this.shadowOffset = config.shadowOffset || 4;
7773         this.shadow = new Roo.Shadow({
7774             offset : this.shadowOffset,
7775             mode : config.shadow
7776         });
7777     }else{
7778         this.shadowOffset = 0;
7779     }
7780     this.useShim = config.shim !== false && Roo.useShims;
7781     this.useDisplay = config.useDisplay;
7782     this.hide();
7783 };
7784
7785 var supr = Roo.Element.prototype;
7786
7787 // shims are shared among layer to keep from having 100 iframes
7788 var shims = [];
7789
7790 Roo.extend(Roo.Layer, Roo.Element, {
7791
7792     getZIndex : function(){
7793         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7794     },
7795
7796     getShim : function(){
7797         if(!this.useShim){
7798             return null;
7799         }
7800         if(this.shim){
7801             return this.shim;
7802         }
7803         var shim = shims.shift();
7804         if(!shim){
7805             shim = this.createShim();
7806             shim.enableDisplayMode('block');
7807             shim.dom.style.display = 'none';
7808             shim.dom.style.visibility = 'visible';
7809         }
7810         var pn = this.dom.parentNode;
7811         if(shim.dom.parentNode != pn){
7812             pn.insertBefore(shim.dom, this.dom);
7813         }
7814         shim.setStyle('z-index', this.getZIndex()-2);
7815         this.shim = shim;
7816         return shim;
7817     },
7818
7819     hideShim : function(){
7820         if(this.shim){
7821             this.shim.setDisplayed(false);
7822             shims.push(this.shim);
7823             delete this.shim;
7824         }
7825     },
7826
7827     disableShadow : function(){
7828         if(this.shadow){
7829             this.shadowDisabled = true;
7830             this.shadow.hide();
7831             this.lastShadowOffset = this.shadowOffset;
7832             this.shadowOffset = 0;
7833         }
7834     },
7835
7836     enableShadow : function(show){
7837         if(this.shadow){
7838             this.shadowDisabled = false;
7839             this.shadowOffset = this.lastShadowOffset;
7840             delete this.lastShadowOffset;
7841             if(show){
7842                 this.sync(true);
7843             }
7844         }
7845     },
7846
7847     // private
7848     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7849     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7850     sync : function(doShow){
7851         var sw = this.shadow;
7852         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7853             var sh = this.getShim();
7854
7855             var w = this.getWidth(),
7856                 h = this.getHeight();
7857
7858             var l = this.getLeft(true),
7859                 t = this.getTop(true);
7860
7861             if(sw && !this.shadowDisabled){
7862                 if(doShow && !sw.isVisible()){
7863                     sw.show(this);
7864                 }else{
7865                     sw.realign(l, t, w, h);
7866                 }
7867                 if(sh){
7868                     if(doShow){
7869                        sh.show();
7870                     }
7871                     // fit the shim behind the shadow, so it is shimmed too
7872                     var a = sw.adjusts, s = sh.dom.style;
7873                     s.left = (Math.min(l, l+a.l))+"px";
7874                     s.top = (Math.min(t, t+a.t))+"px";
7875                     s.width = (w+a.w)+"px";
7876                     s.height = (h+a.h)+"px";
7877                 }
7878             }else if(sh){
7879                 if(doShow){
7880                    sh.show();
7881                 }
7882                 sh.setSize(w, h);
7883                 sh.setLeftTop(l, t);
7884             }
7885             
7886         }
7887     },
7888
7889     // private
7890     destroy : function(){
7891         this.hideShim();
7892         if(this.shadow){
7893             this.shadow.hide();
7894         }
7895         this.removeAllListeners();
7896         var pn = this.dom.parentNode;
7897         if(pn){
7898             pn.removeChild(this.dom);
7899         }
7900         Roo.Element.uncache(this.id);
7901     },
7902
7903     remove : function(){
7904         this.destroy();
7905     },
7906
7907     // private
7908     beginUpdate : function(){
7909         this.updating = true;
7910     },
7911
7912     // private
7913     endUpdate : function(){
7914         this.updating = false;
7915         this.sync(true);
7916     },
7917
7918     // private
7919     hideUnders : function(negOffset){
7920         if(this.shadow){
7921             this.shadow.hide();
7922         }
7923         this.hideShim();
7924     },
7925
7926     // private
7927     constrainXY : function(){
7928         if(this.constrain){
7929             var vw = Roo.lib.Dom.getViewWidth(),
7930                 vh = Roo.lib.Dom.getViewHeight();
7931             var s = Roo.get(document).getScroll();
7932
7933             var xy = this.getXY();
7934             var x = xy[0], y = xy[1];   
7935             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7936             // only move it if it needs it
7937             var moved = false;
7938             // first validate right/bottom
7939             if((x + w) > vw+s.left){
7940                 x = vw - w - this.shadowOffset;
7941                 moved = true;
7942             }
7943             if((y + h) > vh+s.top){
7944                 y = vh - h - this.shadowOffset;
7945                 moved = true;
7946             }
7947             // then make sure top/left isn't negative
7948             if(x < s.left){
7949                 x = s.left;
7950                 moved = true;
7951             }
7952             if(y < s.top){
7953                 y = s.top;
7954                 moved = true;
7955             }
7956             if(moved){
7957                 if(this.avoidY){
7958                     var ay = this.avoidY;
7959                     if(y <= ay && (y+h) >= ay){
7960                         y = ay-h-5;   
7961                     }
7962                 }
7963                 xy = [x, y];
7964                 this.storeXY(xy);
7965                 supr.setXY.call(this, xy);
7966                 this.sync();
7967             }
7968         }
7969     },
7970
7971     isVisible : function(){
7972         return this.visible;    
7973     },
7974
7975     // private
7976     showAction : function(){
7977         this.visible = true; // track visibility to prevent getStyle calls
7978         if(this.useDisplay === true){
7979             this.setDisplayed("");
7980         }else if(this.lastXY){
7981             supr.setXY.call(this, this.lastXY);
7982         }else if(this.lastLT){
7983             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7984         }
7985     },
7986
7987     // private
7988     hideAction : function(){
7989         this.visible = false;
7990         if(this.useDisplay === true){
7991             this.setDisplayed(false);
7992         }else{
7993             this.setLeftTop(-10000,-10000);
7994         }
7995     },
7996
7997     // overridden Element method
7998     setVisible : function(v, a, d, c, e){
7999         if(v){
8000             this.showAction();
8001         }
8002         if(a && v){
8003             var cb = function(){
8004                 this.sync(true);
8005                 if(c){
8006                     c();
8007                 }
8008             }.createDelegate(this);
8009             supr.setVisible.call(this, true, true, d, cb, e);
8010         }else{
8011             if(!v){
8012                 this.hideUnders(true);
8013             }
8014             var cb = c;
8015             if(a){
8016                 cb = function(){
8017                     this.hideAction();
8018                     if(c){
8019                         c();
8020                     }
8021                 }.createDelegate(this);
8022             }
8023             supr.setVisible.call(this, v, a, d, cb, e);
8024             if(v){
8025                 this.sync(true);
8026             }else if(!a){
8027                 this.hideAction();
8028             }
8029         }
8030     },
8031
8032     storeXY : function(xy){
8033         delete this.lastLT;
8034         this.lastXY = xy;
8035     },
8036
8037     storeLeftTop : function(left, top){
8038         delete this.lastXY;
8039         this.lastLT = [left, top];
8040     },
8041
8042     // private
8043     beforeFx : function(){
8044         this.beforeAction();
8045         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8046     },
8047
8048     // private
8049     afterFx : function(){
8050         Roo.Layer.superclass.afterFx.apply(this, arguments);
8051         this.sync(this.isVisible());
8052     },
8053
8054     // private
8055     beforeAction : function(){
8056         if(!this.updating && this.shadow){
8057             this.shadow.hide();
8058         }
8059     },
8060
8061     // overridden Element method
8062     setLeft : function(left){
8063         this.storeLeftTop(left, this.getTop(true));
8064         supr.setLeft.apply(this, arguments);
8065         this.sync();
8066     },
8067
8068     setTop : function(top){
8069         this.storeLeftTop(this.getLeft(true), top);
8070         supr.setTop.apply(this, arguments);
8071         this.sync();
8072     },
8073
8074     setLeftTop : function(left, top){
8075         this.storeLeftTop(left, top);
8076         supr.setLeftTop.apply(this, arguments);
8077         this.sync();
8078     },
8079
8080     setXY : function(xy, a, d, c, e){
8081         this.fixDisplay();
8082         this.beforeAction();
8083         this.storeXY(xy);
8084         var cb = this.createCB(c);
8085         supr.setXY.call(this, xy, a, d, cb, e);
8086         if(!a){
8087             cb();
8088         }
8089     },
8090
8091     // private
8092     createCB : function(c){
8093         var el = this;
8094         return function(){
8095             el.constrainXY();
8096             el.sync(true);
8097             if(c){
8098                 c();
8099             }
8100         };
8101     },
8102
8103     // overridden Element method
8104     setX : function(x, a, d, c, e){
8105         this.setXY([x, this.getY()], a, d, c, e);
8106     },
8107
8108     // overridden Element method
8109     setY : function(y, a, d, c, e){
8110         this.setXY([this.getX(), y], a, d, c, e);
8111     },
8112
8113     // overridden Element method
8114     setSize : function(w, h, a, d, c, e){
8115         this.beforeAction();
8116         var cb = this.createCB(c);
8117         supr.setSize.call(this, w, h, a, d, cb, e);
8118         if(!a){
8119             cb();
8120         }
8121     },
8122
8123     // overridden Element method
8124     setWidth : function(w, a, d, c, e){
8125         this.beforeAction();
8126         var cb = this.createCB(c);
8127         supr.setWidth.call(this, w, a, d, cb, e);
8128         if(!a){
8129             cb();
8130         }
8131     },
8132
8133     // overridden Element method
8134     setHeight : function(h, a, d, c, e){
8135         this.beforeAction();
8136         var cb = this.createCB(c);
8137         supr.setHeight.call(this, h, a, d, cb, e);
8138         if(!a){
8139             cb();
8140         }
8141     },
8142
8143     // overridden Element method
8144     setBounds : function(x, y, w, h, a, d, c, e){
8145         this.beforeAction();
8146         var cb = this.createCB(c);
8147         if(!a){
8148             this.storeXY([x, y]);
8149             supr.setXY.call(this, [x, y]);
8150             supr.setSize.call(this, w, h, a, d, cb, e);
8151             cb();
8152         }else{
8153             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8154         }
8155         return this;
8156     },
8157     
8158     /**
8159      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8160      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8161      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8162      * @param {Number} zindex The new z-index to set
8163      * @return {this} The Layer
8164      */
8165     setZIndex : function(zindex){
8166         this.zindex = zindex;
8167         this.setStyle("z-index", zindex + 2);
8168         if(this.shadow){
8169             this.shadow.setZIndex(zindex + 1);
8170         }
8171         if(this.shim){
8172             this.shim.setStyle("z-index", zindex);
8173         }
8174     }
8175 });
8176 })();/*
8177  * Based on:
8178  * Ext JS Library 1.1.1
8179  * Copyright(c) 2006-2007, Ext JS, LLC.
8180  *
8181  * Originally Released Under LGPL - original licence link has changed is not relivant.
8182  *
8183  * Fork - LGPL
8184  * <script type="text/javascript">
8185  */
8186
8187
8188 /**
8189  * @class Roo.Shadow
8190  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8191  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8192  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8193  * @constructor
8194  * Create a new Shadow
8195  * @param {Object} config The config object
8196  */
8197 Roo.Shadow = function(config){
8198     Roo.apply(this, config);
8199     if(typeof this.mode != "string"){
8200         this.mode = this.defaultMode;
8201     }
8202     var o = this.offset, a = {h: 0};
8203     var rad = Math.floor(this.offset/2);
8204     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8205         case "drop":
8206             a.w = 0;
8207             a.l = a.t = o;
8208             a.t -= 1;
8209             if(Roo.isIE){
8210                 a.l -= this.offset + rad;
8211                 a.t -= this.offset + rad;
8212                 a.w -= rad;
8213                 a.h -= rad;
8214                 a.t += 1;
8215             }
8216         break;
8217         case "sides":
8218             a.w = (o*2);
8219             a.l = -o;
8220             a.t = o-1;
8221             if(Roo.isIE){
8222                 a.l -= (this.offset - rad);
8223                 a.t -= this.offset + rad;
8224                 a.l += 1;
8225                 a.w -= (this.offset - rad)*2;
8226                 a.w -= rad + 1;
8227                 a.h -= 1;
8228             }
8229         break;
8230         case "frame":
8231             a.w = a.h = (o*2);
8232             a.l = a.t = -o;
8233             a.t += 1;
8234             a.h -= 2;
8235             if(Roo.isIE){
8236                 a.l -= (this.offset - rad);
8237                 a.t -= (this.offset - rad);
8238                 a.l += 1;
8239                 a.w -= (this.offset + rad + 1);
8240                 a.h -= (this.offset + rad);
8241                 a.h += 1;
8242             }
8243         break;
8244     };
8245
8246     this.adjusts = a;
8247 };
8248
8249 Roo.Shadow.prototype = {
8250     /**
8251      * @cfg {String} mode
8252      * The shadow display mode.  Supports the following options:<br />
8253      * sides: Shadow displays on both sides and bottom only<br />
8254      * frame: Shadow displays equally on all four sides<br />
8255      * drop: Traditional bottom-right drop shadow (default)
8256      */
8257     /**
8258      * @cfg {String} offset
8259      * The number of pixels to offset the shadow from the element (defaults to 4)
8260      */
8261     offset: 4,
8262
8263     // private
8264     defaultMode: "drop",
8265
8266     /**
8267      * Displays the shadow under the target element
8268      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8269      */
8270     show : function(target){
8271         target = Roo.get(target);
8272         if(!this.el){
8273             this.el = Roo.Shadow.Pool.pull();
8274             if(this.el.dom.nextSibling != target.dom){
8275                 this.el.insertBefore(target);
8276             }
8277         }
8278         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8279         if(Roo.isIE){
8280             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8281         }
8282         this.realign(
8283             target.getLeft(true),
8284             target.getTop(true),
8285             target.getWidth(),
8286             target.getHeight()
8287         );
8288         this.el.dom.style.display = "block";
8289     },
8290
8291     /**
8292      * Returns true if the shadow is visible, else false
8293      */
8294     isVisible : function(){
8295         return this.el ? true : false;  
8296     },
8297
8298     /**
8299      * Direct alignment when values are already available. Show must be called at least once before
8300      * calling this method to ensure it is initialized.
8301      * @param {Number} left The target element left position
8302      * @param {Number} top The target element top position
8303      * @param {Number} width The target element width
8304      * @param {Number} height The target element height
8305      */
8306     realign : function(l, t, w, h){
8307         if(!this.el){
8308             return;
8309         }
8310         var a = this.adjusts, d = this.el.dom, s = d.style;
8311         var iea = 0;
8312         s.left = (l+a.l)+"px";
8313         s.top = (t+a.t)+"px";
8314         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8315  
8316         if(s.width != sws || s.height != shs){
8317             s.width = sws;
8318             s.height = shs;
8319             if(!Roo.isIE){
8320                 var cn = d.childNodes;
8321                 var sww = Math.max(0, (sw-12))+"px";
8322                 cn[0].childNodes[1].style.width = sww;
8323                 cn[1].childNodes[1].style.width = sww;
8324                 cn[2].childNodes[1].style.width = sww;
8325                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8326             }
8327         }
8328     },
8329
8330     /**
8331      * Hides this shadow
8332      */
8333     hide : function(){
8334         if(this.el){
8335             this.el.dom.style.display = "none";
8336             Roo.Shadow.Pool.push(this.el);
8337             delete this.el;
8338         }
8339     },
8340
8341     /**
8342      * Adjust the z-index of this shadow
8343      * @param {Number} zindex The new z-index
8344      */
8345     setZIndex : function(z){
8346         this.zIndex = z;
8347         if(this.el){
8348             this.el.setStyle("z-index", z);
8349         }
8350     }
8351 };
8352
8353 // Private utility class that manages the internal Shadow cache
8354 Roo.Shadow.Pool = function(){
8355     var p = [];
8356     var markup = Roo.isIE ?
8357                  '<div class="x-ie-shadow"></div>' :
8358                  '<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>';
8359     return {
8360         pull : function(){
8361             var sh = p.shift();
8362             if(!sh){
8363                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8364                 sh.autoBoxAdjust = false;
8365             }
8366             return sh;
8367         },
8368
8369         push : function(sh){
8370             p.push(sh);
8371         }
8372     };
8373 }();/*
8374  * Based on:
8375  * Ext JS Library 1.1.1
8376  * Copyright(c) 2006-2007, Ext JS, LLC.
8377  *
8378  * Originally Released Under LGPL - original licence link has changed is not relivant.
8379  *
8380  * Fork - LGPL
8381  * <script type="text/javascript">
8382  */
8383
8384 /**
8385  * @class Roo.BoxComponent
8386  * @extends Roo.Component
8387  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8388  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8389  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8390  * layout containers.
8391  * @constructor
8392  * @param {Roo.Element/String/Object} config The configuration options.
8393  */
8394 Roo.BoxComponent = function(config){
8395     Roo.Component.call(this, config);
8396     this.addEvents({
8397         /**
8398          * @event resize
8399          * Fires after the component is resized.
8400              * @param {Roo.Component} this
8401              * @param {Number} adjWidth The box-adjusted width that was set
8402              * @param {Number} adjHeight The box-adjusted height that was set
8403              * @param {Number} rawWidth The width that was originally specified
8404              * @param {Number} rawHeight The height that was originally specified
8405              */
8406         resize : true,
8407         /**
8408          * @event move
8409          * Fires after the component is moved.
8410              * @param {Roo.Component} this
8411              * @param {Number} x The new x position
8412              * @param {Number} y The new y position
8413              */
8414         move : true
8415     });
8416 };
8417
8418 Roo.extend(Roo.BoxComponent, Roo.Component, {
8419     // private, set in afterRender to signify that the component has been rendered
8420     boxReady : false,
8421     // private, used to defer height settings to subclasses
8422     deferHeight: false,
8423     /** @cfg {Number} width
8424      * width (optional) size of component
8425      */
8426      /** @cfg {Number} height
8427      * height (optional) size of component
8428      */
8429      
8430     /**
8431      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8432      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8433      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8434      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8435      * @return {Roo.BoxComponent} this
8436      */
8437     setSize : function(w, h){
8438         // support for standard size objects
8439         if(typeof w == 'object'){
8440             h = w.height;
8441             w = w.width;
8442         }
8443         // not rendered
8444         if(!this.boxReady){
8445             this.width = w;
8446             this.height = h;
8447             return this;
8448         }
8449
8450         // prevent recalcs when not needed
8451         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8452             return this;
8453         }
8454         this.lastSize = {width: w, height: h};
8455
8456         var adj = this.adjustSize(w, h);
8457         var aw = adj.width, ah = adj.height;
8458         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8459             var rz = this.getResizeEl();
8460             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8461                 rz.setSize(aw, ah);
8462             }else if(!this.deferHeight && ah !== undefined){
8463                 rz.setHeight(ah);
8464             }else if(aw !== undefined){
8465                 rz.setWidth(aw);
8466             }
8467             this.onResize(aw, ah, w, h);
8468             this.fireEvent('resize', this, aw, ah, w, h);
8469         }
8470         return this;
8471     },
8472
8473     /**
8474      * Gets the current size of the component's underlying element.
8475      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8476      */
8477     getSize : function(){
8478         return this.el.getSize();
8479     },
8480
8481     /**
8482      * Gets the current XY position of the component's underlying element.
8483      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8484      * @return {Array} The XY position of the element (e.g., [100, 200])
8485      */
8486     getPosition : function(local){
8487         if(local === true){
8488             return [this.el.getLeft(true), this.el.getTop(true)];
8489         }
8490         return this.xy || this.el.getXY();
8491     },
8492
8493     /**
8494      * Gets the current box measurements of the component's underlying element.
8495      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8496      * @returns {Object} box An object in the format {x, y, width, height}
8497      */
8498     getBox : function(local){
8499         var s = this.el.getSize();
8500         if(local){
8501             s.x = this.el.getLeft(true);
8502             s.y = this.el.getTop(true);
8503         }else{
8504             var xy = this.xy || this.el.getXY();
8505             s.x = xy[0];
8506             s.y = xy[1];
8507         }
8508         return s;
8509     },
8510
8511     /**
8512      * Sets the current box measurements of the component's underlying element.
8513      * @param {Object} box An object in the format {x, y, width, height}
8514      * @returns {Roo.BoxComponent} this
8515      */
8516     updateBox : function(box){
8517         this.setSize(box.width, box.height);
8518         this.setPagePosition(box.x, box.y);
8519         return this;
8520     },
8521
8522     // protected
8523     getResizeEl : function(){
8524         return this.resizeEl || this.el;
8525     },
8526
8527     // protected
8528     getPositionEl : function(){
8529         return this.positionEl || this.el;
8530     },
8531
8532     /**
8533      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8534      * This method fires the move event.
8535      * @param {Number} left The new left
8536      * @param {Number} top The new top
8537      * @returns {Roo.BoxComponent} this
8538      */
8539     setPosition : function(x, y){
8540         this.x = x;
8541         this.y = y;
8542         if(!this.boxReady){
8543             return this;
8544         }
8545         var adj = this.adjustPosition(x, y);
8546         var ax = adj.x, ay = adj.y;
8547
8548         var el = this.getPositionEl();
8549         if(ax !== undefined || ay !== undefined){
8550             if(ax !== undefined && ay !== undefined){
8551                 el.setLeftTop(ax, ay);
8552             }else if(ax !== undefined){
8553                 el.setLeft(ax);
8554             }else if(ay !== undefined){
8555                 el.setTop(ay);
8556             }
8557             this.onPosition(ax, ay);
8558             this.fireEvent('move', this, ax, ay);
8559         }
8560         return this;
8561     },
8562
8563     /**
8564      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8565      * This method fires the move event.
8566      * @param {Number} x The new x position
8567      * @param {Number} y The new y position
8568      * @returns {Roo.BoxComponent} this
8569      */
8570     setPagePosition : function(x, y){
8571         this.pageX = x;
8572         this.pageY = y;
8573         if(!this.boxReady){
8574             return;
8575         }
8576         if(x === undefined || y === undefined){ // cannot translate undefined points
8577             return;
8578         }
8579         var p = this.el.translatePoints(x, y);
8580         this.setPosition(p.left, p.top);
8581         return this;
8582     },
8583
8584     // private
8585     onRender : function(ct, position){
8586         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8587         if(this.resizeEl){
8588             this.resizeEl = Roo.get(this.resizeEl);
8589         }
8590         if(this.positionEl){
8591             this.positionEl = Roo.get(this.positionEl);
8592         }
8593     },
8594
8595     // private
8596     afterRender : function(){
8597         Roo.BoxComponent.superclass.afterRender.call(this);
8598         this.boxReady = true;
8599         this.setSize(this.width, this.height);
8600         if(this.x || this.y){
8601             this.setPosition(this.x, this.y);
8602         }
8603         if(this.pageX || this.pageY){
8604             this.setPagePosition(this.pageX, this.pageY);
8605         }
8606     },
8607
8608     /**
8609      * Force the component's size to recalculate based on the underlying element's current height and width.
8610      * @returns {Roo.BoxComponent} this
8611      */
8612     syncSize : function(){
8613         delete this.lastSize;
8614         this.setSize(this.el.getWidth(), this.el.getHeight());
8615         return this;
8616     },
8617
8618     /**
8619      * Called after the component is resized, this method is empty by default but can be implemented by any
8620      * subclass that needs to perform custom logic after a resize occurs.
8621      * @param {Number} adjWidth The box-adjusted width that was set
8622      * @param {Number} adjHeight The box-adjusted height that was set
8623      * @param {Number} rawWidth The width that was originally specified
8624      * @param {Number} rawHeight The height that was originally specified
8625      */
8626     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8627
8628     },
8629
8630     /**
8631      * Called after the component is moved, this method is empty by default but can be implemented by any
8632      * subclass that needs to perform custom logic after a move occurs.
8633      * @param {Number} x The new x position
8634      * @param {Number} y The new y position
8635      */
8636     onPosition : function(x, y){
8637
8638     },
8639
8640     // private
8641     adjustSize : function(w, h){
8642         if(this.autoWidth){
8643             w = 'auto';
8644         }
8645         if(this.autoHeight){
8646             h = 'auto';
8647         }
8648         return {width : w, height: h};
8649     },
8650
8651     // private
8652     adjustPosition : function(x, y){
8653         return {x : x, y: y};
8654     }
8655 });/*
8656  * Based on:
8657  * Ext JS Library 1.1.1
8658  * Copyright(c) 2006-2007, Ext JS, LLC.
8659  *
8660  * Originally Released Under LGPL - original licence link has changed is not relivant.
8661  *
8662  * Fork - LGPL
8663  * <script type="text/javascript">
8664  */
8665
8666
8667 /**
8668  * @class Roo.SplitBar
8669  * @extends Roo.util.Observable
8670  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8671  * <br><br>
8672  * Usage:
8673  * <pre><code>
8674 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8675                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8676 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8677 split.minSize = 100;
8678 split.maxSize = 600;
8679 split.animate = true;
8680 split.on('moved', splitterMoved);
8681 </code></pre>
8682  * @constructor
8683  * Create a new SplitBar
8684  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8685  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8686  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8687  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8688                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8689                         position of the SplitBar).
8690  */
8691 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8692     
8693     /** @private */
8694     this.el = Roo.get(dragElement, true);
8695     this.el.dom.unselectable = "on";
8696     /** @private */
8697     this.resizingEl = Roo.get(resizingElement, true);
8698
8699     /**
8700      * @private
8701      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8702      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8703      * @type Number
8704      */
8705     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8706     
8707     /**
8708      * The minimum size of the resizing element. (Defaults to 0)
8709      * @type Number
8710      */
8711     this.minSize = 0;
8712     
8713     /**
8714      * The maximum size of the resizing element. (Defaults to 2000)
8715      * @type Number
8716      */
8717     this.maxSize = 2000;
8718     
8719     /**
8720      * Whether to animate the transition to the new size
8721      * @type Boolean
8722      */
8723     this.animate = false;
8724     
8725     /**
8726      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8727      * @type Boolean
8728      */
8729     this.useShim = false;
8730     
8731     /** @private */
8732     this.shim = null;
8733     
8734     if(!existingProxy){
8735         /** @private */
8736         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8737     }else{
8738         this.proxy = Roo.get(existingProxy).dom;
8739     }
8740     /** @private */
8741     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8742     
8743     /** @private */
8744     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8745     
8746     /** @private */
8747     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8748     
8749     /** @private */
8750     this.dragSpecs = {};
8751     
8752     /**
8753      * @private The adapter to use to positon and resize elements
8754      */
8755     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8756     this.adapter.init(this);
8757     
8758     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8759         /** @private */
8760         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8761         this.el.addClass("x-splitbar-h");
8762     }else{
8763         /** @private */
8764         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8765         this.el.addClass("x-splitbar-v");
8766     }
8767     
8768     this.addEvents({
8769         /**
8770          * @event resize
8771          * Fires when the splitter is moved (alias for {@link #event-moved})
8772          * @param {Roo.SplitBar} this
8773          * @param {Number} newSize the new width or height
8774          */
8775         "resize" : true,
8776         /**
8777          * @event moved
8778          * Fires when the splitter is moved
8779          * @param {Roo.SplitBar} this
8780          * @param {Number} newSize the new width or height
8781          */
8782         "moved" : true,
8783         /**
8784          * @event beforeresize
8785          * Fires before the splitter is dragged
8786          * @param {Roo.SplitBar} this
8787          */
8788         "beforeresize" : true,
8789
8790         "beforeapply" : true
8791     });
8792
8793     Roo.util.Observable.call(this);
8794 };
8795
8796 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8797     onStartProxyDrag : function(x, y){
8798         this.fireEvent("beforeresize", this);
8799         if(!this.overlay){
8800             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8801             o.unselectable();
8802             o.enableDisplayMode("block");
8803             // all splitbars share the same overlay
8804             Roo.SplitBar.prototype.overlay = o;
8805         }
8806         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8807         this.overlay.show();
8808         Roo.get(this.proxy).setDisplayed("block");
8809         var size = this.adapter.getElementSize(this);
8810         this.activeMinSize = this.getMinimumSize();;
8811         this.activeMaxSize = this.getMaximumSize();;
8812         var c1 = size - this.activeMinSize;
8813         var c2 = Math.max(this.activeMaxSize - size, 0);
8814         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8815             this.dd.resetConstraints();
8816             this.dd.setXConstraint(
8817                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8818                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8819             );
8820             this.dd.setYConstraint(0, 0);
8821         }else{
8822             this.dd.resetConstraints();
8823             this.dd.setXConstraint(0, 0);
8824             this.dd.setYConstraint(
8825                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8826                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8827             );
8828          }
8829         this.dragSpecs.startSize = size;
8830         this.dragSpecs.startPoint = [x, y];
8831         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8832     },
8833     
8834     /** 
8835      * @private Called after the drag operation by the DDProxy
8836      */
8837     onEndProxyDrag : function(e){
8838         Roo.get(this.proxy).setDisplayed(false);
8839         var endPoint = Roo.lib.Event.getXY(e);
8840         if(this.overlay){
8841             this.overlay.hide();
8842         }
8843         var newSize;
8844         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8845             newSize = this.dragSpecs.startSize + 
8846                 (this.placement == Roo.SplitBar.LEFT ?
8847                     endPoint[0] - this.dragSpecs.startPoint[0] :
8848                     this.dragSpecs.startPoint[0] - endPoint[0]
8849                 );
8850         }else{
8851             newSize = this.dragSpecs.startSize + 
8852                 (this.placement == Roo.SplitBar.TOP ?
8853                     endPoint[1] - this.dragSpecs.startPoint[1] :
8854                     this.dragSpecs.startPoint[1] - endPoint[1]
8855                 );
8856         }
8857         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8858         if(newSize != this.dragSpecs.startSize){
8859             if(this.fireEvent('beforeapply', this, newSize) !== false){
8860                 this.adapter.setElementSize(this, newSize);
8861                 this.fireEvent("moved", this, newSize);
8862                 this.fireEvent("resize", this, newSize);
8863             }
8864         }
8865     },
8866     
8867     /**
8868      * Get the adapter this SplitBar uses
8869      * @return The adapter object
8870      */
8871     getAdapter : function(){
8872         return this.adapter;
8873     },
8874     
8875     /**
8876      * Set the adapter this SplitBar uses
8877      * @param {Object} adapter A SplitBar adapter object
8878      */
8879     setAdapter : function(adapter){
8880         this.adapter = adapter;
8881         this.adapter.init(this);
8882     },
8883     
8884     /**
8885      * Gets the minimum size for the resizing element
8886      * @return {Number} The minimum size
8887      */
8888     getMinimumSize : function(){
8889         return this.minSize;
8890     },
8891     
8892     /**
8893      * Sets the minimum size for the resizing element
8894      * @param {Number} minSize The minimum size
8895      */
8896     setMinimumSize : function(minSize){
8897         this.minSize = minSize;
8898     },
8899     
8900     /**
8901      * Gets the maximum size for the resizing element
8902      * @return {Number} The maximum size
8903      */
8904     getMaximumSize : function(){
8905         return this.maxSize;
8906     },
8907     
8908     /**
8909      * Sets the maximum size for the resizing element
8910      * @param {Number} maxSize The maximum size
8911      */
8912     setMaximumSize : function(maxSize){
8913         this.maxSize = maxSize;
8914     },
8915     
8916     /**
8917      * Sets the initialize size for the resizing element
8918      * @param {Number} size The initial size
8919      */
8920     setCurrentSize : function(size){
8921         var oldAnimate = this.animate;
8922         this.animate = false;
8923         this.adapter.setElementSize(this, size);
8924         this.animate = oldAnimate;
8925     },
8926     
8927     /**
8928      * Destroy this splitbar. 
8929      * @param {Boolean} removeEl True to remove the element
8930      */
8931     destroy : function(removeEl){
8932         if(this.shim){
8933             this.shim.remove();
8934         }
8935         this.dd.unreg();
8936         this.proxy.parentNode.removeChild(this.proxy);
8937         if(removeEl){
8938             this.el.remove();
8939         }
8940     }
8941 });
8942
8943 /**
8944  * @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.
8945  */
8946 Roo.SplitBar.createProxy = function(dir){
8947     var proxy = new Roo.Element(document.createElement("div"));
8948     proxy.unselectable();
8949     var cls = 'x-splitbar-proxy';
8950     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8951     document.body.appendChild(proxy.dom);
8952     return proxy.dom;
8953 };
8954
8955 /** 
8956  * @class Roo.SplitBar.BasicLayoutAdapter
8957  * Default Adapter. It assumes the splitter and resizing element are not positioned
8958  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8959  */
8960 Roo.SplitBar.BasicLayoutAdapter = function(){
8961 };
8962
8963 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8964     // do nothing for now
8965     init : function(s){
8966     
8967     },
8968     /**
8969      * Called before drag operations to get the current size of the resizing element. 
8970      * @param {Roo.SplitBar} s The SplitBar using this adapter
8971      */
8972      getElementSize : function(s){
8973         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8974             return s.resizingEl.getWidth();
8975         }else{
8976             return s.resizingEl.getHeight();
8977         }
8978     },
8979     
8980     /**
8981      * Called after drag operations to set the size of the resizing element.
8982      * @param {Roo.SplitBar} s The SplitBar using this adapter
8983      * @param {Number} newSize The new size to set
8984      * @param {Function} onComplete A function to be invoked when resizing is complete
8985      */
8986     setElementSize : function(s, newSize, onComplete){
8987         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8988             if(!s.animate){
8989                 s.resizingEl.setWidth(newSize);
8990                 if(onComplete){
8991                     onComplete(s, newSize);
8992                 }
8993             }else{
8994                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8995             }
8996         }else{
8997             
8998             if(!s.animate){
8999                 s.resizingEl.setHeight(newSize);
9000                 if(onComplete){
9001                     onComplete(s, newSize);
9002                 }
9003             }else{
9004                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9005             }
9006         }
9007     }
9008 };
9009
9010 /** 
9011  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9012  * @extends Roo.SplitBar.BasicLayoutAdapter
9013  * Adapter that  moves the splitter element to align with the resized sizing element. 
9014  * Used with an absolute positioned SplitBar.
9015  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9016  * document.body, make sure you assign an id to the body element.
9017  */
9018 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9019     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9020     this.container = Roo.get(container);
9021 };
9022
9023 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9024     init : function(s){
9025         this.basic.init(s);
9026     },
9027     
9028     getElementSize : function(s){
9029         return this.basic.getElementSize(s);
9030     },
9031     
9032     setElementSize : function(s, newSize, onComplete){
9033         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9034     },
9035     
9036     moveSplitter : function(s){
9037         var yes = Roo.SplitBar;
9038         switch(s.placement){
9039             case yes.LEFT:
9040                 s.el.setX(s.resizingEl.getRight());
9041                 break;
9042             case yes.RIGHT:
9043                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9044                 break;
9045             case yes.TOP:
9046                 s.el.setY(s.resizingEl.getBottom());
9047                 break;
9048             case yes.BOTTOM:
9049                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9050                 break;
9051         }
9052     }
9053 };
9054
9055 /**
9056  * Orientation constant - Create a vertical SplitBar
9057  * @static
9058  * @type Number
9059  */
9060 Roo.SplitBar.VERTICAL = 1;
9061
9062 /**
9063  * Orientation constant - Create a horizontal SplitBar
9064  * @static
9065  * @type Number
9066  */
9067 Roo.SplitBar.HORIZONTAL = 2;
9068
9069 /**
9070  * Placement constant - The resizing element is to the left of the splitter element
9071  * @static
9072  * @type Number
9073  */
9074 Roo.SplitBar.LEFT = 1;
9075
9076 /**
9077  * Placement constant - The resizing element is to the right of the splitter element
9078  * @static
9079  * @type Number
9080  */
9081 Roo.SplitBar.RIGHT = 2;
9082
9083 /**
9084  * Placement constant - The resizing element is positioned above the splitter element
9085  * @static
9086  * @type Number
9087  */
9088 Roo.SplitBar.TOP = 3;
9089
9090 /**
9091  * Placement constant - The resizing element is positioned under splitter element
9092  * @static
9093  * @type Number
9094  */
9095 Roo.SplitBar.BOTTOM = 4;
9096 /*
9097  * Based on:
9098  * Ext JS Library 1.1.1
9099  * Copyright(c) 2006-2007, Ext JS, LLC.
9100  *
9101  * Originally Released Under LGPL - original licence link has changed is not relivant.
9102  *
9103  * Fork - LGPL
9104  * <script type="text/javascript">
9105  */
9106
9107 /**
9108  * @class Roo.View
9109  * @extends Roo.util.Observable
9110  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9111  * This class also supports single and multi selection modes. <br>
9112  * Create a data model bound view:
9113  <pre><code>
9114  var store = new Roo.data.Store(...);
9115
9116  var view = new Roo.View({
9117     el : "my-element",
9118     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9119  
9120     singleSelect: true,
9121     selectedClass: "ydataview-selected",
9122     store: store
9123  });
9124
9125  // listen for node click?
9126  view.on("click", function(vw, index, node, e){
9127  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9128  });
9129
9130  // load XML data
9131  dataModel.load("foobar.xml");
9132  </code></pre>
9133  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9134  * <br><br>
9135  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9136  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9137  * 
9138  * Note: old style constructor is still suported (container, template, config)
9139  * 
9140  * @constructor
9141  * Create a new View
9142  * @param {Object} config The config object
9143  * 
9144  */
9145 Roo.View = function(config, depreciated_tpl, depreciated_config){
9146     
9147     if (typeof(depreciated_tpl) == 'undefined') {
9148         // new way.. - universal constructor.
9149         Roo.apply(this, config);
9150         this.el  = Roo.get(this.el);
9151     } else {
9152         // old format..
9153         this.el  = Roo.get(config);
9154         this.tpl = depreciated_tpl;
9155         Roo.apply(this, depreciated_config);
9156     }
9157      
9158     
9159     if(typeof(this.tpl) == "string"){
9160         this.tpl = new Roo.Template(this.tpl);
9161     } else {
9162         // support xtype ctors..
9163         this.tpl = new Roo.factory(this.tpl, Roo);
9164     }
9165     
9166     
9167     this.tpl.compile();
9168    
9169
9170      
9171     /** @private */
9172     this.addEvents({
9173         /**
9174          * @event beforeclick
9175          * Fires before a click is processed. Returns false to cancel the default action.
9176          * @param {Roo.View} this
9177          * @param {Number} index The index of the target node
9178          * @param {HTMLElement} node The target node
9179          * @param {Roo.EventObject} e The raw event object
9180          */
9181             "beforeclick" : true,
9182         /**
9183          * @event click
9184          * Fires when a template node is clicked.
9185          * @param {Roo.View} this
9186          * @param {Number} index The index of the target node
9187          * @param {HTMLElement} node The target node
9188          * @param {Roo.EventObject} e The raw event object
9189          */
9190             "click" : true,
9191         /**
9192          * @event dblclick
9193          * Fires when a template node is double clicked.
9194          * @param {Roo.View} this
9195          * @param {Number} index The index of the target node
9196          * @param {HTMLElement} node The target node
9197          * @param {Roo.EventObject} e The raw event object
9198          */
9199             "dblclick" : true,
9200         /**
9201          * @event contextmenu
9202          * Fires when a template node is right clicked.
9203          * @param {Roo.View} this
9204          * @param {Number} index The index of the target node
9205          * @param {HTMLElement} node The target node
9206          * @param {Roo.EventObject} e The raw event object
9207          */
9208             "contextmenu" : true,
9209         /**
9210          * @event selectionchange
9211          * Fires when the selected nodes change.
9212          * @param {Roo.View} this
9213          * @param {Array} selections Array of the selected nodes
9214          */
9215             "selectionchange" : true,
9216     
9217         /**
9218          * @event beforeselect
9219          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9220          * @param {Roo.View} this
9221          * @param {HTMLElement} node The node to be selected
9222          * @param {Array} selections Array of currently selected nodes
9223          */
9224             "beforeselect" : true,
9225         /**
9226          * @event preparedata
9227          * Fires on every row to render, to allow you to change the data.
9228          * @param {Roo.View} this
9229          * @param {Object} data to be rendered (change this)
9230          */
9231           "preparedata" : true
9232         });
9233
9234     this.el.on({
9235         "click": this.onClick,
9236         "dblclick": this.onDblClick,
9237         "contextmenu": this.onContextMenu,
9238         scope:this
9239     });
9240
9241     this.selections = [];
9242     this.nodes = [];
9243     this.cmp = new Roo.CompositeElementLite([]);
9244     if(this.store){
9245         this.store = Roo.factory(this.store, Roo.data);
9246         this.setStore(this.store, true);
9247     }
9248     Roo.View.superclass.constructor.call(this);
9249 };
9250
9251 Roo.extend(Roo.View, Roo.util.Observable, {
9252     
9253      /**
9254      * @cfg {Roo.data.Store} store Data store to load data from.
9255      */
9256     store : false,
9257     
9258     /**
9259      * @cfg {String|Roo.Element} el The container element.
9260      */
9261     el : '',
9262     
9263     /**
9264      * @cfg {String|Roo.Template} tpl The template used by this View 
9265      */
9266     tpl : false,
9267     /**
9268      * @cfg {String} dataName the named area of the template to use as the data area
9269      *                          Works with domtemplates roo-name="name"
9270      */
9271     dataName: false,
9272     /**
9273      * @cfg {String} selectedClass The css class to add to selected nodes
9274      */
9275     selectedClass : "x-view-selected",
9276      /**
9277      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9278      */
9279     emptyText : "",
9280     /**
9281      * @cfg {Boolean} multiSelect Allow multiple selection
9282      */
9283     multiSelect : false,
9284     /**
9285      * @cfg {Boolean} singleSelect Allow single selection
9286      */
9287     singleSelect:  false,
9288     
9289     /**
9290      * @cfg {Boolean} toggleSelect - selecting 
9291      */
9292     toggleSelect : false,
9293     
9294     /**
9295      * Returns the element this view is bound to.
9296      * @return {Roo.Element}
9297      */
9298     getEl : function(){
9299         return this.el;
9300     },
9301
9302     /**
9303      * Refreshes the view.
9304      */
9305     refresh : function(){
9306         var t = this.tpl;
9307         
9308         // if we are using something like 'domtemplate', then
9309         // the what gets used is:
9310         // t.applySubtemplate(NAME, data, wrapping data..)
9311         // the outer template then get' applied with
9312         //     the store 'extra data'
9313         // and the body get's added to the
9314         //      roo-name="data" node?
9315         //      <span class='roo-tpl-{name}'></span> ?????
9316         
9317         
9318         
9319         this.clearSelections();
9320         this.el.update("");
9321         var html = [];
9322         var records = this.store.getRange();
9323         if(records.length < 1) {
9324             
9325             // is this valid??  = should it render a template??
9326             
9327             this.el.update(this.emptyText);
9328             return;
9329         }
9330         var el = this.el;
9331         if (this.dataName) {
9332             this.el.update(t.apply(this.store.meta)); //????
9333             el = this.el.child('.roo-tpl-' + this.dataName);
9334         }
9335         
9336         for(var i = 0, len = records.length; i < len; i++){
9337             var data = this.prepareData(records[i].data, i, records[i]);
9338             this.fireEvent("preparedata", this, data, i, records[i]);
9339             html[html.length] = Roo.util.Format.trim(
9340                 this.dataName ?
9341                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9342                     t.apply(data)
9343             );
9344         }
9345         
9346         
9347         
9348         el.update(html.join(""));
9349         this.nodes = el.dom.childNodes;
9350         this.updateIndexes(0);
9351     },
9352
9353     /**
9354      * Function to override to reformat the data that is sent to
9355      * the template for each node.
9356      * DEPRICATED - use the preparedata event handler.
9357      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9358      * a JSON object for an UpdateManager bound view).
9359      */
9360     prepareData : function(data, index, record)
9361     {
9362         this.fireEvent("preparedata", this, data, index, record);
9363         return data;
9364     },
9365
9366     onUpdate : function(ds, record){
9367         this.clearSelections();
9368         var index = this.store.indexOf(record);
9369         var n = this.nodes[index];
9370         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9371         n.parentNode.removeChild(n);
9372         this.updateIndexes(index, index);
9373     },
9374
9375     
9376     
9377 // --------- FIXME     
9378     onAdd : function(ds, records, index)
9379     {
9380         this.clearSelections();
9381         if(this.nodes.length == 0){
9382             this.refresh();
9383             return;
9384         }
9385         var n = this.nodes[index];
9386         for(var i = 0, len = records.length; i < len; i++){
9387             var d = this.prepareData(records[i].data, i, records[i]);
9388             if(n){
9389                 this.tpl.insertBefore(n, d);
9390             }else{
9391                 
9392                 this.tpl.append(this.el, d);
9393             }
9394         }
9395         this.updateIndexes(index);
9396     },
9397
9398     onRemove : function(ds, record, index){
9399         this.clearSelections();
9400         var el = this.dataName  ?
9401             this.el.child('.roo-tpl-' + this.dataName) :
9402             this.el; 
9403         el.dom.removeChild(this.nodes[index]);
9404         this.updateIndexes(index);
9405     },
9406
9407     /**
9408      * Refresh an individual node.
9409      * @param {Number} index
9410      */
9411     refreshNode : function(index){
9412         this.onUpdate(this.store, this.store.getAt(index));
9413     },
9414
9415     updateIndexes : function(startIndex, endIndex){
9416         var ns = this.nodes;
9417         startIndex = startIndex || 0;
9418         endIndex = endIndex || ns.length - 1;
9419         for(var i = startIndex; i <= endIndex; i++){
9420             ns[i].nodeIndex = i;
9421         }
9422     },
9423
9424     /**
9425      * Changes the data store this view uses and refresh the view.
9426      * @param {Store} store
9427      */
9428     setStore : function(store, initial){
9429         if(!initial && this.store){
9430             this.store.un("datachanged", this.refresh);
9431             this.store.un("add", this.onAdd);
9432             this.store.un("remove", this.onRemove);
9433             this.store.un("update", this.onUpdate);
9434             this.store.un("clear", this.refresh);
9435         }
9436         if(store){
9437           
9438             store.on("datachanged", this.refresh, this);
9439             store.on("add", this.onAdd, this);
9440             store.on("remove", this.onRemove, this);
9441             store.on("update", this.onUpdate, this);
9442             store.on("clear", this.refresh, this);
9443         }
9444         
9445         if(store){
9446             this.refresh();
9447         }
9448     },
9449
9450     /**
9451      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9452      * @param {HTMLElement} node
9453      * @return {HTMLElement} The template node
9454      */
9455     findItemFromChild : function(node){
9456         var el = this.dataName  ?
9457             this.el.child('.roo-tpl-' + this.dataName,true) :
9458             this.el.dom; 
9459         
9460         if(!node || node.parentNode == el){
9461                     return node;
9462             }
9463             var p = node.parentNode;
9464             while(p && p != el){
9465             if(p.parentNode == el){
9466                 return p;
9467             }
9468             p = p.parentNode;
9469         }
9470             return null;
9471     },
9472
9473     /** @ignore */
9474     onClick : function(e){
9475         var item = this.findItemFromChild(e.getTarget());
9476         if(item){
9477             var index = this.indexOf(item);
9478             if(this.onItemClick(item, index, e) !== false){
9479                 this.fireEvent("click", this, index, item, e);
9480             }
9481         }else{
9482             this.clearSelections();
9483         }
9484     },
9485
9486     /** @ignore */
9487     onContextMenu : function(e){
9488         var item = this.findItemFromChild(e.getTarget());
9489         if(item){
9490             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9491         }
9492     },
9493
9494     /** @ignore */
9495     onDblClick : function(e){
9496         var item = this.findItemFromChild(e.getTarget());
9497         if(item){
9498             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9499         }
9500     },
9501
9502     onItemClick : function(item, index, e)
9503     {
9504         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9505             return false;
9506         }
9507         if (this.toggleSelect) {
9508             var m = this.isSelected(item) ? 'unselect' : 'select';
9509             Roo.log(m);
9510             var _t = this;
9511             _t[m](item, true, false);
9512             return true;
9513         }
9514         if(this.multiSelect || this.singleSelect){
9515             if(this.multiSelect && e.shiftKey && this.lastSelection){
9516                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9517             }else{
9518                 this.select(item, this.multiSelect && e.ctrlKey);
9519                 this.lastSelection = item;
9520             }
9521             e.preventDefault();
9522         }
9523         return true;
9524     },
9525
9526     /**
9527      * Get the number of selected nodes.
9528      * @return {Number}
9529      */
9530     getSelectionCount : function(){
9531         return this.selections.length;
9532     },
9533
9534     /**
9535      * Get the currently selected nodes.
9536      * @return {Array} An array of HTMLElements
9537      */
9538     getSelectedNodes : function(){
9539         return this.selections;
9540     },
9541
9542     /**
9543      * Get the indexes of the selected nodes.
9544      * @return {Array}
9545      */
9546     getSelectedIndexes : function(){
9547         var indexes = [], s = this.selections;
9548         for(var i = 0, len = s.length; i < len; i++){
9549             indexes.push(s[i].nodeIndex);
9550         }
9551         return indexes;
9552     },
9553
9554     /**
9555      * Clear all selections
9556      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9557      */
9558     clearSelections : function(suppressEvent){
9559         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9560             this.cmp.elements = this.selections;
9561             this.cmp.removeClass(this.selectedClass);
9562             this.selections = [];
9563             if(!suppressEvent){
9564                 this.fireEvent("selectionchange", this, this.selections);
9565             }
9566         }
9567     },
9568
9569     /**
9570      * Returns true if the passed node is selected
9571      * @param {HTMLElement/Number} node The node or node index
9572      * @return {Boolean}
9573      */
9574     isSelected : function(node){
9575         var s = this.selections;
9576         if(s.length < 1){
9577             return false;
9578         }
9579         node = this.getNode(node);
9580         return s.indexOf(node) !== -1;
9581     },
9582
9583     /**
9584      * Selects nodes.
9585      * @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
9586      * @param {Boolean} keepExisting (optional) true to keep existing selections
9587      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9588      */
9589     select : function(nodeInfo, keepExisting, suppressEvent){
9590         if(nodeInfo instanceof Array){
9591             if(!keepExisting){
9592                 this.clearSelections(true);
9593             }
9594             for(var i = 0, len = nodeInfo.length; i < len; i++){
9595                 this.select(nodeInfo[i], true, true);
9596             }
9597             return;
9598         } 
9599         var node = this.getNode(nodeInfo);
9600         if(!node || this.isSelected(node)){
9601             return; // already selected.
9602         }
9603         if(!keepExisting){
9604             this.clearSelections(true);
9605         }
9606         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9607             Roo.fly(node).addClass(this.selectedClass);
9608             this.selections.push(node);
9609             if(!suppressEvent){
9610                 this.fireEvent("selectionchange", this, this.selections);
9611             }
9612         }
9613         
9614         
9615     },
9616       /**
9617      * Unselects nodes.
9618      * @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
9619      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9620      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9621      */
9622     unselect : function(nodeInfo, keepExisting, suppressEvent)
9623     {
9624         if(nodeInfo instanceof Array){
9625             Roo.each(this.selections, function(s) {
9626                 this.unselect(s, nodeInfo);
9627             }, this);
9628             return;
9629         }
9630         var node = this.getNode(nodeInfo);
9631         if(!node || !this.isSelected(node)){
9632             Roo.log("not selected");
9633             return; // not selected.
9634         }
9635         // fireevent???
9636         var ns = [];
9637         Roo.each(this.selections, function(s) {
9638             if (s == node ) {
9639                 Roo.fly(node).removeClass(this.selectedClass);
9640
9641                 return;
9642             }
9643             ns.push(s);
9644         },this);
9645         
9646         this.selections= ns;
9647         this.fireEvent("selectionchange", this, this.selections);
9648     },
9649
9650     /**
9651      * Gets a template node.
9652      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9653      * @return {HTMLElement} The node or null if it wasn't found
9654      */
9655     getNode : function(nodeInfo){
9656         if(typeof nodeInfo == "string"){
9657             return document.getElementById(nodeInfo);
9658         }else if(typeof nodeInfo == "number"){
9659             return this.nodes[nodeInfo];
9660         }
9661         return nodeInfo;
9662     },
9663
9664     /**
9665      * Gets a range template nodes.
9666      * @param {Number} startIndex
9667      * @param {Number} endIndex
9668      * @return {Array} An array of nodes
9669      */
9670     getNodes : function(start, end){
9671         var ns = this.nodes;
9672         start = start || 0;
9673         end = typeof end == "undefined" ? ns.length - 1 : end;
9674         var nodes = [];
9675         if(start <= end){
9676             for(var i = start; i <= end; i++){
9677                 nodes.push(ns[i]);
9678             }
9679         } else{
9680             for(var i = start; i >= end; i--){
9681                 nodes.push(ns[i]);
9682             }
9683         }
9684         return nodes;
9685     },
9686
9687     /**
9688      * Finds the index of the passed node
9689      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9690      * @return {Number} The index of the node or -1
9691      */
9692     indexOf : function(node){
9693         node = this.getNode(node);
9694         if(typeof node.nodeIndex == "number"){
9695             return node.nodeIndex;
9696         }
9697         var ns = this.nodes;
9698         for(var i = 0, len = ns.length; i < len; i++){
9699             if(ns[i] == node){
9700                 return i;
9701             }
9702         }
9703         return -1;
9704     }
9705 });
9706 /*
9707  * Based on:
9708  * Ext JS Library 1.1.1
9709  * Copyright(c) 2006-2007, Ext JS, LLC.
9710  *
9711  * Originally Released Under LGPL - original licence link has changed is not relivant.
9712  *
9713  * Fork - LGPL
9714  * <script type="text/javascript">
9715  */
9716
9717 /**
9718  * @class Roo.JsonView
9719  * @extends Roo.View
9720  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9721 <pre><code>
9722 var view = new Roo.JsonView({
9723     container: "my-element",
9724     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9725     multiSelect: true, 
9726     jsonRoot: "data" 
9727 });
9728
9729 // listen for node click?
9730 view.on("click", function(vw, index, node, e){
9731     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9732 });
9733
9734 // direct load of JSON data
9735 view.load("foobar.php");
9736
9737 // Example from my blog list
9738 var tpl = new Roo.Template(
9739     '&lt;div class="entry"&gt;' +
9740     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9741     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9742     "&lt;/div&gt;&lt;hr /&gt;"
9743 );
9744
9745 var moreView = new Roo.JsonView({
9746     container :  "entry-list", 
9747     template : tpl,
9748     jsonRoot: "posts"
9749 });
9750 moreView.on("beforerender", this.sortEntries, this);
9751 moreView.load({
9752     url: "/blog/get-posts.php",
9753     params: "allposts=true",
9754     text: "Loading Blog Entries..."
9755 });
9756 </code></pre>
9757
9758 * Note: old code is supported with arguments : (container, template, config)
9759
9760
9761  * @constructor
9762  * Create a new JsonView
9763  * 
9764  * @param {Object} config The config object
9765  * 
9766  */
9767 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9768     
9769     
9770     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9771
9772     var um = this.el.getUpdateManager();
9773     um.setRenderer(this);
9774     um.on("update", this.onLoad, this);
9775     um.on("failure", this.onLoadException, this);
9776
9777     /**
9778      * @event beforerender
9779      * Fires before rendering of the downloaded JSON data.
9780      * @param {Roo.JsonView} this
9781      * @param {Object} data The JSON data loaded
9782      */
9783     /**
9784      * @event load
9785      * Fires when data is loaded.
9786      * @param {Roo.JsonView} this
9787      * @param {Object} data The JSON data loaded
9788      * @param {Object} response The raw Connect response object
9789      */
9790     /**
9791      * @event loadexception
9792      * Fires when loading fails.
9793      * @param {Roo.JsonView} this
9794      * @param {Object} response The raw Connect response object
9795      */
9796     this.addEvents({
9797         'beforerender' : true,
9798         'load' : true,
9799         'loadexception' : true
9800     });
9801 };
9802 Roo.extend(Roo.JsonView, Roo.View, {
9803     /**
9804      * @type {String} The root property in the loaded JSON object that contains the data
9805      */
9806     jsonRoot : "",
9807
9808     /**
9809      * Refreshes the view.
9810      */
9811     refresh : function(){
9812         this.clearSelections();
9813         this.el.update("");
9814         var html = [];
9815         var o = this.jsonData;
9816         if(o && o.length > 0){
9817             for(var i = 0, len = o.length; i < len; i++){
9818                 var data = this.prepareData(o[i], i, o);
9819                 html[html.length] = this.tpl.apply(data);
9820             }
9821         }else{
9822             html.push(this.emptyText);
9823         }
9824         this.el.update(html.join(""));
9825         this.nodes = this.el.dom.childNodes;
9826         this.updateIndexes(0);
9827     },
9828
9829     /**
9830      * 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.
9831      * @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:
9832      <pre><code>
9833      view.load({
9834          url: "your-url.php",
9835          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9836          callback: yourFunction,
9837          scope: yourObject, //(optional scope)
9838          discardUrl: false,
9839          nocache: false,
9840          text: "Loading...",
9841          timeout: 30,
9842          scripts: false
9843      });
9844      </code></pre>
9845      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9846      * 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.
9847      * @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}
9848      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9849      * @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.
9850      */
9851     load : function(){
9852         var um = this.el.getUpdateManager();
9853         um.update.apply(um, arguments);
9854     },
9855
9856     render : function(el, response){
9857         this.clearSelections();
9858         this.el.update("");
9859         var o;
9860         try{
9861             o = Roo.util.JSON.decode(response.responseText);
9862             if(this.jsonRoot){
9863                 
9864                 o = o[this.jsonRoot];
9865             }
9866         } catch(e){
9867         }
9868         /**
9869          * The current JSON data or null
9870          */
9871         this.jsonData = o;
9872         this.beforeRender();
9873         this.refresh();
9874     },
9875
9876 /**
9877  * Get the number of records in the current JSON dataset
9878  * @return {Number}
9879  */
9880     getCount : function(){
9881         return this.jsonData ? this.jsonData.length : 0;
9882     },
9883
9884 /**
9885  * Returns the JSON object for the specified node(s)
9886  * @param {HTMLElement/Array} node The node or an array of nodes
9887  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9888  * you get the JSON object for the node
9889  */
9890     getNodeData : function(node){
9891         if(node instanceof Array){
9892             var data = [];
9893             for(var i = 0, len = node.length; i < len; i++){
9894                 data.push(this.getNodeData(node[i]));
9895             }
9896             return data;
9897         }
9898         return this.jsonData[this.indexOf(node)] || null;
9899     },
9900
9901     beforeRender : function(){
9902         this.snapshot = this.jsonData;
9903         if(this.sortInfo){
9904             this.sort.apply(this, this.sortInfo);
9905         }
9906         this.fireEvent("beforerender", this, this.jsonData);
9907     },
9908
9909     onLoad : function(el, o){
9910         this.fireEvent("load", this, this.jsonData, o);
9911     },
9912
9913     onLoadException : function(el, o){
9914         this.fireEvent("loadexception", this, o);
9915     },
9916
9917 /**
9918  * Filter the data by a specific property.
9919  * @param {String} property A property on your JSON objects
9920  * @param {String/RegExp} value Either string that the property values
9921  * should start with, or a RegExp to test against the property
9922  */
9923     filter : function(property, value){
9924         if(this.jsonData){
9925             var data = [];
9926             var ss = this.snapshot;
9927             if(typeof value == "string"){
9928                 var vlen = value.length;
9929                 if(vlen == 0){
9930                     this.clearFilter();
9931                     return;
9932                 }
9933                 value = value.toLowerCase();
9934                 for(var i = 0, len = ss.length; i < len; i++){
9935                     var o = ss[i];
9936                     if(o[property].substr(0, vlen).toLowerCase() == value){
9937                         data.push(o);
9938                     }
9939                 }
9940             } else if(value.exec){ // regex?
9941                 for(var i = 0, len = ss.length; i < len; i++){
9942                     var o = ss[i];
9943                     if(value.test(o[property])){
9944                         data.push(o);
9945                     }
9946                 }
9947             } else{
9948                 return;
9949             }
9950             this.jsonData = data;
9951             this.refresh();
9952         }
9953     },
9954
9955 /**
9956  * Filter by a function. The passed function will be called with each
9957  * object in the current dataset. If the function returns true the value is kept,
9958  * otherwise it is filtered.
9959  * @param {Function} fn
9960  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9961  */
9962     filterBy : function(fn, scope){
9963         if(this.jsonData){
9964             var data = [];
9965             var ss = this.snapshot;
9966             for(var i = 0, len = ss.length; i < len; i++){
9967                 var o = ss[i];
9968                 if(fn.call(scope || this, o)){
9969                     data.push(o);
9970                 }
9971             }
9972             this.jsonData = data;
9973             this.refresh();
9974         }
9975     },
9976
9977 /**
9978  * Clears the current filter.
9979  */
9980     clearFilter : function(){
9981         if(this.snapshot && this.jsonData != this.snapshot){
9982             this.jsonData = this.snapshot;
9983             this.refresh();
9984         }
9985     },
9986
9987
9988 /**
9989  * Sorts the data for this view and refreshes it.
9990  * @param {String} property A property on your JSON objects to sort on
9991  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9992  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9993  */
9994     sort : function(property, dir, sortType){
9995         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9996         if(this.jsonData){
9997             var p = property;
9998             var dsc = dir && dir.toLowerCase() == "desc";
9999             var f = function(o1, o2){
10000                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10001                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10002                 ;
10003                 if(v1 < v2){
10004                     return dsc ? +1 : -1;
10005                 } else if(v1 > v2){
10006                     return dsc ? -1 : +1;
10007                 } else{
10008                     return 0;
10009                 }
10010             };
10011             this.jsonData.sort(f);
10012             this.refresh();
10013             if(this.jsonData != this.snapshot){
10014                 this.snapshot.sort(f);
10015             }
10016         }
10017     }
10018 });/*
10019  * Based on:
10020  * Ext JS Library 1.1.1
10021  * Copyright(c) 2006-2007, Ext JS, LLC.
10022  *
10023  * Originally Released Under LGPL - original licence link has changed is not relivant.
10024  *
10025  * Fork - LGPL
10026  * <script type="text/javascript">
10027  */
10028  
10029
10030 /**
10031  * @class Roo.ColorPalette
10032  * @extends Roo.Component
10033  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10034  * Here's an example of typical usage:
10035  * <pre><code>
10036 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10037 cp.render('my-div');
10038
10039 cp.on('select', function(palette, selColor){
10040     // do something with selColor
10041 });
10042 </code></pre>
10043  * @constructor
10044  * Create a new ColorPalette
10045  * @param {Object} config The config object
10046  */
10047 Roo.ColorPalette = function(config){
10048     Roo.ColorPalette.superclass.constructor.call(this, config);
10049     this.addEvents({
10050         /**
10051              * @event select
10052              * Fires when a color is selected
10053              * @param {ColorPalette} this
10054              * @param {String} color The 6-digit color hex code (without the # symbol)
10055              */
10056         select: true
10057     });
10058
10059     if(this.handler){
10060         this.on("select", this.handler, this.scope, true);
10061     }
10062 };
10063 Roo.extend(Roo.ColorPalette, Roo.Component, {
10064     /**
10065      * @cfg {String} itemCls
10066      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10067      */
10068     itemCls : "x-color-palette",
10069     /**
10070      * @cfg {String} value
10071      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10072      * the hex codes are case-sensitive.
10073      */
10074     value : null,
10075     clickEvent:'click',
10076     // private
10077     ctype: "Roo.ColorPalette",
10078
10079     /**
10080      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10081      */
10082     allowReselect : false,
10083
10084     /**
10085      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10086      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10087      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10088      * of colors with the width setting until the box is symmetrical.</p>
10089      * <p>You can override individual colors if needed:</p>
10090      * <pre><code>
10091 var cp = new Roo.ColorPalette();
10092 cp.colors[0] = "FF0000";  // change the first box to red
10093 </code></pre>
10094
10095 Or you can provide a custom array of your own for complete control:
10096 <pre><code>
10097 var cp = new Roo.ColorPalette();
10098 cp.colors = ["000000", "993300", "333300"];
10099 </code></pre>
10100      * @type Array
10101      */
10102     colors : [
10103         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10104         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10105         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10106         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10107         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10108     ],
10109
10110     // private
10111     onRender : function(container, position){
10112         var t = new Roo.MasterTemplate(
10113             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10114         );
10115         var c = this.colors;
10116         for(var i = 0, len = c.length; i < len; i++){
10117             t.add([c[i]]);
10118         }
10119         var el = document.createElement("div");
10120         el.className = this.itemCls;
10121         t.overwrite(el);
10122         container.dom.insertBefore(el, position);
10123         this.el = Roo.get(el);
10124         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10125         if(this.clickEvent != 'click'){
10126             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10127         }
10128     },
10129
10130     // private
10131     afterRender : function(){
10132         Roo.ColorPalette.superclass.afterRender.call(this);
10133         if(this.value){
10134             var s = this.value;
10135             this.value = null;
10136             this.select(s);
10137         }
10138     },
10139
10140     // private
10141     handleClick : function(e, t){
10142         e.preventDefault();
10143         if(!this.disabled){
10144             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10145             this.select(c.toUpperCase());
10146         }
10147     },
10148
10149     /**
10150      * Selects the specified color in the palette (fires the select event)
10151      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10152      */
10153     select : function(color){
10154         color = color.replace("#", "");
10155         if(color != this.value || this.allowReselect){
10156             var el = this.el;
10157             if(this.value){
10158                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10159             }
10160             el.child("a.color-"+color).addClass("x-color-palette-sel");
10161             this.value = color;
10162             this.fireEvent("select", this, color);
10163         }
10164     }
10165 });/*
10166  * Based on:
10167  * Ext JS Library 1.1.1
10168  * Copyright(c) 2006-2007, Ext JS, LLC.
10169  *
10170  * Originally Released Under LGPL - original licence link has changed is not relivant.
10171  *
10172  * Fork - LGPL
10173  * <script type="text/javascript">
10174  */
10175  
10176 /**
10177  * @class Roo.DatePicker
10178  * @extends Roo.Component
10179  * Simple date picker class.
10180  * @constructor
10181  * Create a new DatePicker
10182  * @param {Object} config The config object
10183  */
10184 Roo.DatePicker = function(config){
10185     Roo.DatePicker.superclass.constructor.call(this, config);
10186
10187     this.value = config && config.value ?
10188                  config.value.clearTime() : new Date().clearTime();
10189
10190     this.addEvents({
10191         /**
10192              * @event select
10193              * Fires when a date is selected
10194              * @param {DatePicker} this
10195              * @param {Date} date The selected date
10196              */
10197         'select': true,
10198         /**
10199              * @event monthchange
10200              * Fires when the displayed month changes 
10201              * @param {DatePicker} this
10202              * @param {Date} date The selected month
10203              */
10204         'monthchange': true
10205     });
10206
10207     if(this.handler){
10208         this.on("select", this.handler,  this.scope || this);
10209     }
10210     // build the disabledDatesRE
10211     if(!this.disabledDatesRE && this.disabledDates){
10212         var dd = this.disabledDates;
10213         var re = "(?:";
10214         for(var i = 0; i < dd.length; i++){
10215             re += dd[i];
10216             if(i != dd.length-1) re += "|";
10217         }
10218         this.disabledDatesRE = new RegExp(re + ")");
10219     }
10220 };
10221
10222 Roo.extend(Roo.DatePicker, Roo.Component, {
10223     /**
10224      * @cfg {String} todayText
10225      * The text to display on the button that selects the current date (defaults to "Today")
10226      */
10227     todayText : "Today",
10228     /**
10229      * @cfg {String} okText
10230      * The text to display on the ok button
10231      */
10232     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10233     /**
10234      * @cfg {String} cancelText
10235      * The text to display on the cancel button
10236      */
10237     cancelText : "Cancel",
10238     /**
10239      * @cfg {String} todayTip
10240      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10241      */
10242     todayTip : "{0} (Spacebar)",
10243     /**
10244      * @cfg {Date} minDate
10245      * Minimum allowable date (JavaScript date object, defaults to null)
10246      */
10247     minDate : null,
10248     /**
10249      * @cfg {Date} maxDate
10250      * Maximum allowable date (JavaScript date object, defaults to null)
10251      */
10252     maxDate : null,
10253     /**
10254      * @cfg {String} minText
10255      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10256      */
10257     minText : "This date is before the minimum date",
10258     /**
10259      * @cfg {String} maxText
10260      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10261      */
10262     maxText : "This date is after the maximum date",
10263     /**
10264      * @cfg {String} format
10265      * The default date format string which can be overriden for localization support.  The format must be
10266      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10267      */
10268     format : "m/d/y",
10269     /**
10270      * @cfg {Array} disabledDays
10271      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10272      */
10273     disabledDays : null,
10274     /**
10275      * @cfg {String} disabledDaysText
10276      * The tooltip to display when the date falls on a disabled day (defaults to "")
10277      */
10278     disabledDaysText : "",
10279     /**
10280      * @cfg {RegExp} disabledDatesRE
10281      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10282      */
10283     disabledDatesRE : null,
10284     /**
10285      * @cfg {String} disabledDatesText
10286      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10287      */
10288     disabledDatesText : "",
10289     /**
10290      * @cfg {Boolean} constrainToViewport
10291      * True to constrain the date picker to the viewport (defaults to true)
10292      */
10293     constrainToViewport : true,
10294     /**
10295      * @cfg {Array} monthNames
10296      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10297      */
10298     monthNames : Date.monthNames,
10299     /**
10300      * @cfg {Array} dayNames
10301      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10302      */
10303     dayNames : Date.dayNames,
10304     /**
10305      * @cfg {String} nextText
10306      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10307      */
10308     nextText: 'Next Month (Control+Right)',
10309     /**
10310      * @cfg {String} prevText
10311      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10312      */
10313     prevText: 'Previous Month (Control+Left)',
10314     /**
10315      * @cfg {String} monthYearText
10316      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10317      */
10318     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10319     /**
10320      * @cfg {Number} startDay
10321      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10322      */
10323     startDay : 0,
10324     /**
10325      * @cfg {Bool} showClear
10326      * Show a clear button (usefull for date form elements that can be blank.)
10327      */
10328     
10329     showClear: false,
10330     
10331     /**
10332      * Sets the value of the date field
10333      * @param {Date} value The date to set
10334      */
10335     setValue : function(value){
10336         var old = this.value;
10337         this.value = value.clearTime(true);
10338         if(this.el){
10339             this.update(this.value);
10340         }
10341     },
10342
10343     /**
10344      * Gets the current selected value of the date field
10345      * @return {Date} The selected date
10346      */
10347     getValue : function(){
10348         return this.value;
10349     },
10350
10351     // private
10352     focus : function(){
10353         if(this.el){
10354             this.update(this.activeDate);
10355         }
10356     },
10357
10358     // private
10359     onRender : function(container, position){
10360         var m = [
10361              '<table cellspacing="0">',
10362                 '<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>',
10363                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10364         var dn = this.dayNames;
10365         for(var i = 0; i < 7; i++){
10366             var d = this.startDay+i;
10367             if(d > 6){
10368                 d = d-7;
10369             }
10370             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10371         }
10372         m[m.length] = "</tr></thead><tbody><tr>";
10373         for(var i = 0; i < 42; i++) {
10374             if(i % 7 == 0 && i != 0){
10375                 m[m.length] = "</tr><tr>";
10376             }
10377             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10378         }
10379         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10380             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10381
10382         var el = document.createElement("div");
10383         el.className = "x-date-picker";
10384         el.innerHTML = m.join("");
10385
10386         container.dom.insertBefore(el, position);
10387
10388         this.el = Roo.get(el);
10389         this.eventEl = Roo.get(el.firstChild);
10390
10391         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10392             handler: this.showPrevMonth,
10393             scope: this,
10394             preventDefault:true,
10395             stopDefault:true
10396         });
10397
10398         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10399             handler: this.showNextMonth,
10400             scope: this,
10401             preventDefault:true,
10402             stopDefault:true
10403         });
10404
10405         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10406
10407         this.monthPicker = this.el.down('div.x-date-mp');
10408         this.monthPicker.enableDisplayMode('block');
10409         
10410         var kn = new Roo.KeyNav(this.eventEl, {
10411             "left" : function(e){
10412                 e.ctrlKey ?
10413                     this.showPrevMonth() :
10414                     this.update(this.activeDate.add("d", -1));
10415             },
10416
10417             "right" : function(e){
10418                 e.ctrlKey ?
10419                     this.showNextMonth() :
10420                     this.update(this.activeDate.add("d", 1));
10421             },
10422
10423             "up" : function(e){
10424                 e.ctrlKey ?
10425                     this.showNextYear() :
10426                     this.update(this.activeDate.add("d", -7));
10427             },
10428
10429             "down" : function(e){
10430                 e.ctrlKey ?
10431                     this.showPrevYear() :
10432                     this.update(this.activeDate.add("d", 7));
10433             },
10434
10435             "pageUp" : function(e){
10436                 this.showNextMonth();
10437             },
10438
10439             "pageDown" : function(e){
10440                 this.showPrevMonth();
10441             },
10442
10443             "enter" : function(e){
10444                 e.stopPropagation();
10445                 return true;
10446             },
10447
10448             scope : this
10449         });
10450
10451         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10452
10453         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10454
10455         this.el.unselectable();
10456         
10457         this.cells = this.el.select("table.x-date-inner tbody td");
10458         this.textNodes = this.el.query("table.x-date-inner tbody span");
10459
10460         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10461             text: "&#160;",
10462             tooltip: this.monthYearText
10463         });
10464
10465         this.mbtn.on('click', this.showMonthPicker, this);
10466         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10467
10468
10469         var today = (new Date()).dateFormat(this.format);
10470         
10471         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10472         if (this.showClear) {
10473             baseTb.add( new Roo.Toolbar.Fill());
10474         }
10475         baseTb.add({
10476             text: String.format(this.todayText, today),
10477             tooltip: String.format(this.todayTip, today),
10478             handler: this.selectToday,
10479             scope: this
10480         });
10481         
10482         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10483             
10484         //});
10485         if (this.showClear) {
10486             
10487             baseTb.add( new Roo.Toolbar.Fill());
10488             baseTb.add({
10489                 text: '&#160;',
10490                 cls: 'x-btn-icon x-btn-clear',
10491                 handler: function() {
10492                     //this.value = '';
10493                     this.fireEvent("select", this, '');
10494                 },
10495                 scope: this
10496             });
10497         }
10498         
10499         
10500         if(Roo.isIE){
10501             this.el.repaint();
10502         }
10503         this.update(this.value);
10504     },
10505
10506     createMonthPicker : function(){
10507         if(!this.monthPicker.dom.firstChild){
10508             var buf = ['<table border="0" cellspacing="0">'];
10509             for(var i = 0; i < 6; i++){
10510                 buf.push(
10511                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10512                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10513                     i == 0 ?
10514                     '<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>' :
10515                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10516                 );
10517             }
10518             buf.push(
10519                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10520                     this.okText,
10521                     '</button><button type="button" class="x-date-mp-cancel">',
10522                     this.cancelText,
10523                     '</button></td></tr>',
10524                 '</table>'
10525             );
10526             this.monthPicker.update(buf.join(''));
10527             this.monthPicker.on('click', this.onMonthClick, this);
10528             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10529
10530             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10531             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10532
10533             this.mpMonths.each(function(m, a, i){
10534                 i += 1;
10535                 if((i%2) == 0){
10536                     m.dom.xmonth = 5 + Math.round(i * .5);
10537                 }else{
10538                     m.dom.xmonth = Math.round((i-1) * .5);
10539                 }
10540             });
10541         }
10542     },
10543
10544     showMonthPicker : function(){
10545         this.createMonthPicker();
10546         var size = this.el.getSize();
10547         this.monthPicker.setSize(size);
10548         this.monthPicker.child('table').setSize(size);
10549
10550         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10551         this.updateMPMonth(this.mpSelMonth);
10552         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10553         this.updateMPYear(this.mpSelYear);
10554
10555         this.monthPicker.slideIn('t', {duration:.2});
10556     },
10557
10558     updateMPYear : function(y){
10559         this.mpyear = y;
10560         var ys = this.mpYears.elements;
10561         for(var i = 1; i <= 10; i++){
10562             var td = ys[i-1], y2;
10563             if((i%2) == 0){
10564                 y2 = y + Math.round(i * .5);
10565                 td.firstChild.innerHTML = y2;
10566                 td.xyear = y2;
10567             }else{
10568                 y2 = y - (5-Math.round(i * .5));
10569                 td.firstChild.innerHTML = y2;
10570                 td.xyear = y2;
10571             }
10572             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10573         }
10574     },
10575
10576     updateMPMonth : function(sm){
10577         this.mpMonths.each(function(m, a, i){
10578             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10579         });
10580     },
10581
10582     selectMPMonth: function(m){
10583         
10584     },
10585
10586     onMonthClick : function(e, t){
10587         e.stopEvent();
10588         var el = new Roo.Element(t), pn;
10589         if(el.is('button.x-date-mp-cancel')){
10590             this.hideMonthPicker();
10591         }
10592         else if(el.is('button.x-date-mp-ok')){
10593             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10594             this.hideMonthPicker();
10595         }
10596         else if(pn = el.up('td.x-date-mp-month', 2)){
10597             this.mpMonths.removeClass('x-date-mp-sel');
10598             pn.addClass('x-date-mp-sel');
10599             this.mpSelMonth = pn.dom.xmonth;
10600         }
10601         else if(pn = el.up('td.x-date-mp-year', 2)){
10602             this.mpYears.removeClass('x-date-mp-sel');
10603             pn.addClass('x-date-mp-sel');
10604             this.mpSelYear = pn.dom.xyear;
10605         }
10606         else if(el.is('a.x-date-mp-prev')){
10607             this.updateMPYear(this.mpyear-10);
10608         }
10609         else if(el.is('a.x-date-mp-next')){
10610             this.updateMPYear(this.mpyear+10);
10611         }
10612     },
10613
10614     onMonthDblClick : function(e, t){
10615         e.stopEvent();
10616         var el = new Roo.Element(t), pn;
10617         if(pn = el.up('td.x-date-mp-month', 2)){
10618             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10619             this.hideMonthPicker();
10620         }
10621         else if(pn = el.up('td.x-date-mp-year', 2)){
10622             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10623             this.hideMonthPicker();
10624         }
10625     },
10626
10627     hideMonthPicker : function(disableAnim){
10628         if(this.monthPicker){
10629             if(disableAnim === true){
10630                 this.monthPicker.hide();
10631             }else{
10632                 this.monthPicker.slideOut('t', {duration:.2});
10633             }
10634         }
10635     },
10636
10637     // private
10638     showPrevMonth : function(e){
10639         this.update(this.activeDate.add("mo", -1));
10640     },
10641
10642     // private
10643     showNextMonth : function(e){
10644         this.update(this.activeDate.add("mo", 1));
10645     },
10646
10647     // private
10648     showPrevYear : function(){
10649         this.update(this.activeDate.add("y", -1));
10650     },
10651
10652     // private
10653     showNextYear : function(){
10654         this.update(this.activeDate.add("y", 1));
10655     },
10656
10657     // private
10658     handleMouseWheel : function(e){
10659         var delta = e.getWheelDelta();
10660         if(delta > 0){
10661             this.showPrevMonth();
10662             e.stopEvent();
10663         } else if(delta < 0){
10664             this.showNextMonth();
10665             e.stopEvent();
10666         }
10667     },
10668
10669     // private
10670     handleDateClick : function(e, t){
10671         e.stopEvent();
10672         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10673             this.setValue(new Date(t.dateValue));
10674             this.fireEvent("select", this, this.value);
10675         }
10676     },
10677
10678     // private
10679     selectToday : function(){
10680         this.setValue(new Date().clearTime());
10681         this.fireEvent("select", this, this.value);
10682     },
10683
10684     // private
10685     update : function(date)
10686     {
10687         var vd = this.activeDate;
10688         this.activeDate = date;
10689         if(vd && this.el){
10690             var t = date.getTime();
10691             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10692                 this.cells.removeClass("x-date-selected");
10693                 this.cells.each(function(c){
10694                    if(c.dom.firstChild.dateValue == t){
10695                        c.addClass("x-date-selected");
10696                        setTimeout(function(){
10697                             try{c.dom.firstChild.focus();}catch(e){}
10698                        }, 50);
10699                        return false;
10700                    }
10701                 });
10702                 return;
10703             }
10704         }
10705         
10706         var days = date.getDaysInMonth();
10707         var firstOfMonth = date.getFirstDateOfMonth();
10708         var startingPos = firstOfMonth.getDay()-this.startDay;
10709
10710         if(startingPos <= this.startDay){
10711             startingPos += 7;
10712         }
10713
10714         var pm = date.add("mo", -1);
10715         var prevStart = pm.getDaysInMonth()-startingPos;
10716
10717         var cells = this.cells.elements;
10718         var textEls = this.textNodes;
10719         days += startingPos;
10720
10721         // convert everything to numbers so it's fast
10722         var day = 86400000;
10723         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10724         var today = new Date().clearTime().getTime();
10725         var sel = date.clearTime().getTime();
10726         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10727         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10728         var ddMatch = this.disabledDatesRE;
10729         var ddText = this.disabledDatesText;
10730         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10731         var ddaysText = this.disabledDaysText;
10732         var format = this.format;
10733
10734         var setCellClass = function(cal, cell){
10735             cell.title = "";
10736             var t = d.getTime();
10737             cell.firstChild.dateValue = t;
10738             if(t == today){
10739                 cell.className += " x-date-today";
10740                 cell.title = cal.todayText;
10741             }
10742             if(t == sel){
10743                 cell.className += " x-date-selected";
10744                 setTimeout(function(){
10745                     try{cell.firstChild.focus();}catch(e){}
10746                 }, 50);
10747             }
10748             // disabling
10749             if(t < min) {
10750                 cell.className = " x-date-disabled";
10751                 cell.title = cal.minText;
10752                 return;
10753             }
10754             if(t > max) {
10755                 cell.className = " x-date-disabled";
10756                 cell.title = cal.maxText;
10757                 return;
10758             }
10759             if(ddays){
10760                 if(ddays.indexOf(d.getDay()) != -1){
10761                     cell.title = ddaysText;
10762                     cell.className = " x-date-disabled";
10763                 }
10764             }
10765             if(ddMatch && format){
10766                 var fvalue = d.dateFormat(format);
10767                 if(ddMatch.test(fvalue)){
10768                     cell.title = ddText.replace("%0", fvalue);
10769                     cell.className = " x-date-disabled";
10770                 }
10771             }
10772         };
10773
10774         var i = 0;
10775         for(; i < startingPos; i++) {
10776             textEls[i].innerHTML = (++prevStart);
10777             d.setDate(d.getDate()+1);
10778             cells[i].className = "x-date-prevday";
10779             setCellClass(this, cells[i]);
10780         }
10781         for(; i < days; i++){
10782             intDay = i - startingPos + 1;
10783             textEls[i].innerHTML = (intDay);
10784             d.setDate(d.getDate()+1);
10785             cells[i].className = "x-date-active";
10786             setCellClass(this, cells[i]);
10787         }
10788         var extraDays = 0;
10789         for(; i < 42; i++) {
10790              textEls[i].innerHTML = (++extraDays);
10791              d.setDate(d.getDate()+1);
10792              cells[i].className = "x-date-nextday";
10793              setCellClass(this, cells[i]);
10794         }
10795
10796         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10797         this.fireEvent('monthchange', this, date);
10798         
10799         if(!this.internalRender){
10800             var main = this.el.dom.firstChild;
10801             var w = main.offsetWidth;
10802             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10803             Roo.fly(main).setWidth(w);
10804             this.internalRender = true;
10805             // opera does not respect the auto grow header center column
10806             // then, after it gets a width opera refuses to recalculate
10807             // without a second pass
10808             if(Roo.isOpera && !this.secondPass){
10809                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10810                 this.secondPass = true;
10811                 this.update.defer(10, this, [date]);
10812             }
10813         }
10814         
10815         
10816     }
10817 });        /*
10818  * Based on:
10819  * Ext JS Library 1.1.1
10820  * Copyright(c) 2006-2007, Ext JS, LLC.
10821  *
10822  * Originally Released Under LGPL - original licence link has changed is not relivant.
10823  *
10824  * Fork - LGPL
10825  * <script type="text/javascript">
10826  */
10827 /**
10828  * @class Roo.TabPanel
10829  * @extends Roo.util.Observable
10830  * A lightweight tab container.
10831  * <br><br>
10832  * Usage:
10833  * <pre><code>
10834 // basic tabs 1, built from existing content
10835 var tabs = new Roo.TabPanel("tabs1");
10836 tabs.addTab("script", "View Script");
10837 tabs.addTab("markup", "View Markup");
10838 tabs.activate("script");
10839
10840 // more advanced tabs, built from javascript
10841 var jtabs = new Roo.TabPanel("jtabs");
10842 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10843
10844 // set up the UpdateManager
10845 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10846 var updater = tab2.getUpdateManager();
10847 updater.setDefaultUrl("ajax1.htm");
10848 tab2.on('activate', updater.refresh, updater, true);
10849
10850 // Use setUrl for Ajax loading
10851 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10852 tab3.setUrl("ajax2.htm", null, true);
10853
10854 // Disabled tab
10855 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10856 tab4.disable();
10857
10858 jtabs.activate("jtabs-1");
10859  * </code></pre>
10860  * @constructor
10861  * Create a new TabPanel.
10862  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10863  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10864  */
10865 Roo.TabPanel = function(container, config){
10866     /**
10867     * The container element for this TabPanel.
10868     * @type Roo.Element
10869     */
10870     this.el = Roo.get(container, true);
10871     if(config){
10872         if(typeof config == "boolean"){
10873             this.tabPosition = config ? "bottom" : "top";
10874         }else{
10875             Roo.apply(this, config);
10876         }
10877     }
10878     if(this.tabPosition == "bottom"){
10879         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10880         this.el.addClass("x-tabs-bottom");
10881     }
10882     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10883     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10884     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10885     if(Roo.isIE){
10886         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10887     }
10888     if(this.tabPosition != "bottom"){
10889         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10890          * @type Roo.Element
10891          */
10892         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10893         this.el.addClass("x-tabs-top");
10894     }
10895     this.items = [];
10896
10897     this.bodyEl.setStyle("position", "relative");
10898
10899     this.active = null;
10900     this.activateDelegate = this.activate.createDelegate(this);
10901
10902     this.addEvents({
10903         /**
10904          * @event tabchange
10905          * Fires when the active tab changes
10906          * @param {Roo.TabPanel} this
10907          * @param {Roo.TabPanelItem} activePanel The new active tab
10908          */
10909         "tabchange": true,
10910         /**
10911          * @event beforetabchange
10912          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10913          * @param {Roo.TabPanel} this
10914          * @param {Object} e Set cancel to true on this object to cancel the tab change
10915          * @param {Roo.TabPanelItem} tab The tab being changed to
10916          */
10917         "beforetabchange" : true
10918     });
10919
10920     Roo.EventManager.onWindowResize(this.onResize, this);
10921     this.cpad = this.el.getPadding("lr");
10922     this.hiddenCount = 0;
10923
10924
10925     // toolbar on the tabbar support...
10926     if (this.toolbar) {
10927         var tcfg = this.toolbar;
10928         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10929         this.toolbar = new Roo.Toolbar(tcfg);
10930         if (Roo.isSafari) {
10931             var tbl = tcfg.container.child('table', true);
10932             tbl.setAttribute('width', '100%');
10933         }
10934         
10935     }
10936    
10937
10938
10939     Roo.TabPanel.superclass.constructor.call(this);
10940 };
10941
10942 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10943     /*
10944      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10945      */
10946     tabPosition : "top",
10947     /*
10948      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10949      */
10950     currentTabWidth : 0,
10951     /*
10952      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10953      */
10954     minTabWidth : 40,
10955     /*
10956      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10957      */
10958     maxTabWidth : 250,
10959     /*
10960      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10961      */
10962     preferredTabWidth : 175,
10963     /*
10964      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10965      */
10966     resizeTabs : false,
10967     /*
10968      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10969      */
10970     monitorResize : true,
10971     /*
10972      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10973      */
10974     toolbar : false,
10975
10976     /**
10977      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10978      * @param {String} id The id of the div to use <b>or create</b>
10979      * @param {String} text The text for the tab
10980      * @param {String} content (optional) Content to put in the TabPanelItem body
10981      * @param {Boolean} closable (optional) True to create a close icon on the tab
10982      * @return {Roo.TabPanelItem} The created TabPanelItem
10983      */
10984     addTab : function(id, text, content, closable){
10985         var item = new Roo.TabPanelItem(this, id, text, closable);
10986         this.addTabItem(item);
10987         if(content){
10988             item.setContent(content);
10989         }
10990         return item;
10991     },
10992
10993     /**
10994      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10995      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10996      * @return {Roo.TabPanelItem}
10997      */
10998     getTab : function(id){
10999         return this.items[id];
11000     },
11001
11002     /**
11003      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11004      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11005      */
11006     hideTab : function(id){
11007         var t = this.items[id];
11008         if(!t.isHidden()){
11009            t.setHidden(true);
11010            this.hiddenCount++;
11011            this.autoSizeTabs();
11012         }
11013     },
11014
11015     /**
11016      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11017      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11018      */
11019     unhideTab : function(id){
11020         var t = this.items[id];
11021         if(t.isHidden()){
11022            t.setHidden(false);
11023            this.hiddenCount--;
11024            this.autoSizeTabs();
11025         }
11026     },
11027
11028     /**
11029      * Adds an existing {@link Roo.TabPanelItem}.
11030      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11031      */
11032     addTabItem : function(item){
11033         this.items[item.id] = item;
11034         this.items.push(item);
11035         if(this.resizeTabs){
11036            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11037            this.autoSizeTabs();
11038         }else{
11039             item.autoSize();
11040         }
11041     },
11042
11043     /**
11044      * Removes a {@link Roo.TabPanelItem}.
11045      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11046      */
11047     removeTab : function(id){
11048         var items = this.items;
11049         var tab = items[id];
11050         if(!tab) { return; }
11051         var index = items.indexOf(tab);
11052         if(this.active == tab && items.length > 1){
11053             var newTab = this.getNextAvailable(index);
11054             if(newTab) {
11055                 newTab.activate();
11056             }
11057         }
11058         this.stripEl.dom.removeChild(tab.pnode.dom);
11059         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11060             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11061         }
11062         items.splice(index, 1);
11063         delete this.items[tab.id];
11064         tab.fireEvent("close", tab);
11065         tab.purgeListeners();
11066         this.autoSizeTabs();
11067     },
11068
11069     getNextAvailable : function(start){
11070         var items = this.items;
11071         var index = start;
11072         // look for a next tab that will slide over to
11073         // replace the one being removed
11074         while(index < items.length){
11075             var item = items[++index];
11076             if(item && !item.isHidden()){
11077                 return item;
11078             }
11079         }
11080         // if one isn't found select the previous tab (on the left)
11081         index = start;
11082         while(index >= 0){
11083             var item = items[--index];
11084             if(item && !item.isHidden()){
11085                 return item;
11086             }
11087         }
11088         return null;
11089     },
11090
11091     /**
11092      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11093      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11094      */
11095     disableTab : function(id){
11096         var tab = this.items[id];
11097         if(tab && this.active != tab){
11098             tab.disable();
11099         }
11100     },
11101
11102     /**
11103      * Enables a {@link Roo.TabPanelItem} that is disabled.
11104      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11105      */
11106     enableTab : function(id){
11107         var tab = this.items[id];
11108         tab.enable();
11109     },
11110
11111     /**
11112      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11113      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11114      * @return {Roo.TabPanelItem} The TabPanelItem.
11115      */
11116     activate : function(id){
11117         var tab = this.items[id];
11118         if(!tab){
11119             return null;
11120         }
11121         if(tab == this.active || tab.disabled){
11122             return tab;
11123         }
11124         var e = {};
11125         this.fireEvent("beforetabchange", this, e, tab);
11126         if(e.cancel !== true && !tab.disabled){
11127             if(this.active){
11128                 this.active.hide();
11129             }
11130             this.active = this.items[id];
11131             this.active.show();
11132             this.fireEvent("tabchange", this, this.active);
11133         }
11134         return tab;
11135     },
11136
11137     /**
11138      * Gets the active {@link Roo.TabPanelItem}.
11139      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11140      */
11141     getActiveTab : function(){
11142         return this.active;
11143     },
11144
11145     /**
11146      * Updates the tab body element to fit the height of the container element
11147      * for overflow scrolling
11148      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11149      */
11150     syncHeight : function(targetHeight){
11151         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11152         var bm = this.bodyEl.getMargins();
11153         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11154         this.bodyEl.setHeight(newHeight);
11155         return newHeight;
11156     },
11157
11158     onResize : function(){
11159         if(this.monitorResize){
11160             this.autoSizeTabs();
11161         }
11162     },
11163
11164     /**
11165      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11166      */
11167     beginUpdate : function(){
11168         this.updating = true;
11169     },
11170
11171     /**
11172      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11173      */
11174     endUpdate : function(){
11175         this.updating = false;
11176         this.autoSizeTabs();
11177     },
11178
11179     /**
11180      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11181      */
11182     autoSizeTabs : function(){
11183         var count = this.items.length;
11184         var vcount = count - this.hiddenCount;
11185         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11186         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11187         var availWidth = Math.floor(w / vcount);
11188         var b = this.stripBody;
11189         if(b.getWidth() > w){
11190             var tabs = this.items;
11191             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11192             if(availWidth < this.minTabWidth){
11193                 /*if(!this.sleft){    // incomplete scrolling code
11194                     this.createScrollButtons();
11195                 }
11196                 this.showScroll();
11197                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11198             }
11199         }else{
11200             if(this.currentTabWidth < this.preferredTabWidth){
11201                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11202             }
11203         }
11204     },
11205
11206     /**
11207      * Returns the number of tabs in this TabPanel.
11208      * @return {Number}
11209      */
11210      getCount : function(){
11211          return this.items.length;
11212      },
11213
11214     /**
11215      * Resizes all the tabs to the passed width
11216      * @param {Number} The new width
11217      */
11218     setTabWidth : function(width){
11219         this.currentTabWidth = width;
11220         for(var i = 0, len = this.items.length; i < len; i++) {
11221                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11222         }
11223     },
11224
11225     /**
11226      * Destroys this TabPanel
11227      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11228      */
11229     destroy : function(removeEl){
11230         Roo.EventManager.removeResizeListener(this.onResize, this);
11231         for(var i = 0, len = this.items.length; i < len; i++){
11232             this.items[i].purgeListeners();
11233         }
11234         if(removeEl === true){
11235             this.el.update("");
11236             this.el.remove();
11237         }
11238     }
11239 });
11240
11241 /**
11242  * @class Roo.TabPanelItem
11243  * @extends Roo.util.Observable
11244  * Represents an individual item (tab plus body) in a TabPanel.
11245  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11246  * @param {String} id The id of this TabPanelItem
11247  * @param {String} text The text for the tab of this TabPanelItem
11248  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11249  */
11250 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11251     /**
11252      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11253      * @type Roo.TabPanel
11254      */
11255     this.tabPanel = tabPanel;
11256     /**
11257      * The id for this TabPanelItem
11258      * @type String
11259      */
11260     this.id = id;
11261     /** @private */
11262     this.disabled = false;
11263     /** @private */
11264     this.text = text;
11265     /** @private */
11266     this.loaded = false;
11267     this.closable = closable;
11268
11269     /**
11270      * The body element for this TabPanelItem.
11271      * @type Roo.Element
11272      */
11273     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11274     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11275     this.bodyEl.setStyle("display", "block");
11276     this.bodyEl.setStyle("zoom", "1");
11277     this.hideAction();
11278
11279     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11280     /** @private */
11281     this.el = Roo.get(els.el, true);
11282     this.inner = Roo.get(els.inner, true);
11283     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11284     this.pnode = Roo.get(els.el.parentNode, true);
11285     this.el.on("mousedown", this.onTabMouseDown, this);
11286     this.el.on("click", this.onTabClick, this);
11287     /** @private */
11288     if(closable){
11289         var c = Roo.get(els.close, true);
11290         c.dom.title = this.closeText;
11291         c.addClassOnOver("close-over");
11292         c.on("click", this.closeClick, this);
11293      }
11294
11295     this.addEvents({
11296          /**
11297          * @event activate
11298          * Fires when this tab becomes the active tab.
11299          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11300          * @param {Roo.TabPanelItem} this
11301          */
11302         "activate": true,
11303         /**
11304          * @event beforeclose
11305          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11306          * @param {Roo.TabPanelItem} this
11307          * @param {Object} e Set cancel to true on this object to cancel the close.
11308          */
11309         "beforeclose": true,
11310         /**
11311          * @event close
11312          * Fires when this tab is closed.
11313          * @param {Roo.TabPanelItem} this
11314          */
11315          "close": true,
11316         /**
11317          * @event deactivate
11318          * Fires when this tab is no longer the active tab.
11319          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11320          * @param {Roo.TabPanelItem} this
11321          */
11322          "deactivate" : true
11323     });
11324     this.hidden = false;
11325
11326     Roo.TabPanelItem.superclass.constructor.call(this);
11327 };
11328
11329 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11330     purgeListeners : function(){
11331        Roo.util.Observable.prototype.purgeListeners.call(this);
11332        this.el.removeAllListeners();
11333     },
11334     /**
11335      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11336      */
11337     show : function(){
11338         this.pnode.addClass("on");
11339         this.showAction();
11340         if(Roo.isOpera){
11341             this.tabPanel.stripWrap.repaint();
11342         }
11343         this.fireEvent("activate", this.tabPanel, this);
11344     },
11345
11346     /**
11347      * Returns true if this tab is the active tab.
11348      * @return {Boolean}
11349      */
11350     isActive : function(){
11351         return this.tabPanel.getActiveTab() == this;
11352     },
11353
11354     /**
11355      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11356      */
11357     hide : function(){
11358         this.pnode.removeClass("on");
11359         this.hideAction();
11360         this.fireEvent("deactivate", this.tabPanel, this);
11361     },
11362
11363     hideAction : function(){
11364         this.bodyEl.hide();
11365         this.bodyEl.setStyle("position", "absolute");
11366         this.bodyEl.setLeft("-20000px");
11367         this.bodyEl.setTop("-20000px");
11368     },
11369
11370     showAction : function(){
11371         this.bodyEl.setStyle("position", "relative");
11372         this.bodyEl.setTop("");
11373         this.bodyEl.setLeft("");
11374         this.bodyEl.show();
11375     },
11376
11377     /**
11378      * Set the tooltip for the tab.
11379      * @param {String} tooltip The tab's tooltip
11380      */
11381     setTooltip : function(text){
11382         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11383             this.textEl.dom.qtip = text;
11384             this.textEl.dom.removeAttribute('title');
11385         }else{
11386             this.textEl.dom.title = text;
11387         }
11388     },
11389
11390     onTabClick : function(e){
11391         e.preventDefault();
11392         this.tabPanel.activate(this.id);
11393     },
11394
11395     onTabMouseDown : function(e){
11396         e.preventDefault();
11397         this.tabPanel.activate(this.id);
11398     },
11399
11400     getWidth : function(){
11401         return this.inner.getWidth();
11402     },
11403
11404     setWidth : function(width){
11405         var iwidth = width - this.pnode.getPadding("lr");
11406         this.inner.setWidth(iwidth);
11407         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11408         this.pnode.setWidth(width);
11409     },
11410
11411     /**
11412      * Show or hide the tab
11413      * @param {Boolean} hidden True to hide or false to show.
11414      */
11415     setHidden : function(hidden){
11416         this.hidden = hidden;
11417         this.pnode.setStyle("display", hidden ? "none" : "");
11418     },
11419
11420     /**
11421      * Returns true if this tab is "hidden"
11422      * @return {Boolean}
11423      */
11424     isHidden : function(){
11425         return this.hidden;
11426     },
11427
11428     /**
11429      * Returns the text for this tab
11430      * @return {String}
11431      */
11432     getText : function(){
11433         return this.text;
11434     },
11435
11436     autoSize : function(){
11437         //this.el.beginMeasure();
11438         this.textEl.setWidth(1);
11439         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11440         //this.el.endMeasure();
11441     },
11442
11443     /**
11444      * Sets the text for the tab (Note: this also sets the tooltip text)
11445      * @param {String} text The tab's text and tooltip
11446      */
11447     setText : function(text){
11448         this.text = text;
11449         this.textEl.update(text);
11450         this.setTooltip(text);
11451         if(!this.tabPanel.resizeTabs){
11452             this.autoSize();
11453         }
11454     },
11455     /**
11456      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11457      */
11458     activate : function(){
11459         this.tabPanel.activate(this.id);
11460     },
11461
11462     /**
11463      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11464      */
11465     disable : function(){
11466         if(this.tabPanel.active != this){
11467             this.disabled = true;
11468             this.pnode.addClass("disabled");
11469         }
11470     },
11471
11472     /**
11473      * Enables this TabPanelItem if it was previously disabled.
11474      */
11475     enable : function(){
11476         this.disabled = false;
11477         this.pnode.removeClass("disabled");
11478     },
11479
11480     /**
11481      * Sets the content for this TabPanelItem.
11482      * @param {String} content The content
11483      * @param {Boolean} loadScripts true to look for and load scripts
11484      */
11485     setContent : function(content, loadScripts){
11486         this.bodyEl.update(content, loadScripts);
11487     },
11488
11489     /**
11490      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11491      * @return {Roo.UpdateManager} The UpdateManager
11492      */
11493     getUpdateManager : function(){
11494         return this.bodyEl.getUpdateManager();
11495     },
11496
11497     /**
11498      * Set a URL to be used to load the content for this TabPanelItem.
11499      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11500      * @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)
11501      * @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)
11502      * @return {Roo.UpdateManager} The UpdateManager
11503      */
11504     setUrl : function(url, params, loadOnce){
11505         if(this.refreshDelegate){
11506             this.un('activate', this.refreshDelegate);
11507         }
11508         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11509         this.on("activate", this.refreshDelegate);
11510         return this.bodyEl.getUpdateManager();
11511     },
11512
11513     /** @private */
11514     _handleRefresh : function(url, params, loadOnce){
11515         if(!loadOnce || !this.loaded){
11516             var updater = this.bodyEl.getUpdateManager();
11517             updater.update(url, params, this._setLoaded.createDelegate(this));
11518         }
11519     },
11520
11521     /**
11522      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11523      *   Will fail silently if the setUrl method has not been called.
11524      *   This does not activate the panel, just updates its content.
11525      */
11526     refresh : function(){
11527         if(this.refreshDelegate){
11528            this.loaded = false;
11529            this.refreshDelegate();
11530         }
11531     },
11532
11533     /** @private */
11534     _setLoaded : function(){
11535         this.loaded = true;
11536     },
11537
11538     /** @private */
11539     closeClick : function(e){
11540         var o = {};
11541         e.stopEvent();
11542         this.fireEvent("beforeclose", this, o);
11543         if(o.cancel !== true){
11544             this.tabPanel.removeTab(this.id);
11545         }
11546     },
11547     /**
11548      * The text displayed in the tooltip for the close icon.
11549      * @type String
11550      */
11551     closeText : "Close this tab"
11552 });
11553
11554 /** @private */
11555 Roo.TabPanel.prototype.createStrip = function(container){
11556     var strip = document.createElement("div");
11557     strip.className = "x-tabs-wrap";
11558     container.appendChild(strip);
11559     return strip;
11560 };
11561 /** @private */
11562 Roo.TabPanel.prototype.createStripList = function(strip){
11563     // div wrapper for retard IE
11564     // returns the "tr" element.
11565     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11566         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11567         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11568     return strip.firstChild.firstChild.firstChild.firstChild;
11569 };
11570 /** @private */
11571 Roo.TabPanel.prototype.createBody = function(container){
11572     var body = document.createElement("div");
11573     Roo.id(body, "tab-body");
11574     Roo.fly(body).addClass("x-tabs-body");
11575     container.appendChild(body);
11576     return body;
11577 };
11578 /** @private */
11579 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11580     var body = Roo.getDom(id);
11581     if(!body){
11582         body = document.createElement("div");
11583         body.id = id;
11584     }
11585     Roo.fly(body).addClass("x-tabs-item-body");
11586     bodyEl.insertBefore(body, bodyEl.firstChild);
11587     return body;
11588 };
11589 /** @private */
11590 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11591     var td = document.createElement("td");
11592     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11593     //stripEl.appendChild(td);
11594     if(closable){
11595         td.className = "x-tabs-closable";
11596         if(!this.closeTpl){
11597             this.closeTpl = new Roo.Template(
11598                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11599                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11600                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11601             );
11602         }
11603         var el = this.closeTpl.overwrite(td, {"text": text});
11604         var close = el.getElementsByTagName("div")[0];
11605         var inner = el.getElementsByTagName("em")[0];
11606         return {"el": el, "close": close, "inner": inner};
11607     } else {
11608         if(!this.tabTpl){
11609             this.tabTpl = new Roo.Template(
11610                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11611                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11612             );
11613         }
11614         var el = this.tabTpl.overwrite(td, {"text": text});
11615         var inner = el.getElementsByTagName("em")[0];
11616         return {"el": el, "inner": inner};
11617     }
11618 };/*
11619  * Based on:
11620  * Ext JS Library 1.1.1
11621  * Copyright(c) 2006-2007, Ext JS, LLC.
11622  *
11623  * Originally Released Under LGPL - original licence link has changed is not relivant.
11624  *
11625  * Fork - LGPL
11626  * <script type="text/javascript">
11627  */
11628
11629 /**
11630  * @class Roo.Button
11631  * @extends Roo.util.Observable
11632  * Simple Button class
11633  * @cfg {String} text The button text
11634  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11635  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11636  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11637  * @cfg {Object} scope The scope of the handler
11638  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11639  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11640  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11641  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11642  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11643  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11644    applies if enableToggle = true)
11645  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11646  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11647   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11648  * @constructor
11649  * Create a new button
11650  * @param {Object} config The config object
11651  */
11652 Roo.Button = function(renderTo, config)
11653 {
11654     if (!config) {
11655         config = renderTo;
11656         renderTo = config.renderTo || false;
11657     }
11658     
11659     Roo.apply(this, config);
11660     this.addEvents({
11661         /**
11662              * @event click
11663              * Fires when this button is clicked
11664              * @param {Button} this
11665              * @param {EventObject} e The click event
11666              */
11667             "click" : true,
11668         /**
11669              * @event toggle
11670              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11671              * @param {Button} this
11672              * @param {Boolean} pressed
11673              */
11674             "toggle" : true,
11675         /**
11676              * @event mouseover
11677              * Fires when the mouse hovers over the button
11678              * @param {Button} this
11679              * @param {Event} e The event object
11680              */
11681         'mouseover' : true,
11682         /**
11683              * @event mouseout
11684              * Fires when the mouse exits the button
11685              * @param {Button} this
11686              * @param {Event} e The event object
11687              */
11688         'mouseout': true,
11689          /**
11690              * @event render
11691              * Fires when the button is rendered
11692              * @param {Button} this
11693              */
11694         'render': true
11695     });
11696     if(this.menu){
11697         this.menu = Roo.menu.MenuMgr.get(this.menu);
11698     }
11699     // register listeners first!!  - so render can be captured..
11700     Roo.util.Observable.call(this);
11701     if(renderTo){
11702         this.render(renderTo);
11703     }
11704     
11705   
11706 };
11707
11708 Roo.extend(Roo.Button, Roo.util.Observable, {
11709     /**
11710      * 
11711      */
11712     
11713     /**
11714      * Read-only. True if this button is hidden
11715      * @type Boolean
11716      */
11717     hidden : false,
11718     /**
11719      * Read-only. True if this button is disabled
11720      * @type Boolean
11721      */
11722     disabled : false,
11723     /**
11724      * Read-only. True if this button is pressed (only if enableToggle = true)
11725      * @type Boolean
11726      */
11727     pressed : false,
11728
11729     /**
11730      * @cfg {Number} tabIndex 
11731      * The DOM tabIndex for this button (defaults to undefined)
11732      */
11733     tabIndex : undefined,
11734
11735     /**
11736      * @cfg {Boolean} enableToggle
11737      * True to enable pressed/not pressed toggling (defaults to false)
11738      */
11739     enableToggle: false,
11740     /**
11741      * @cfg {Mixed} menu
11742      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11743      */
11744     menu : undefined,
11745     /**
11746      * @cfg {String} menuAlign
11747      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11748      */
11749     menuAlign : "tl-bl?",
11750
11751     /**
11752      * @cfg {String} iconCls
11753      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11754      */
11755     iconCls : undefined,
11756     /**
11757      * @cfg {String} type
11758      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11759      */
11760     type : 'button',
11761
11762     // private
11763     menuClassTarget: 'tr',
11764
11765     /**
11766      * @cfg {String} clickEvent
11767      * The type of event to map to the button's event handler (defaults to 'click')
11768      */
11769     clickEvent : 'click',
11770
11771     /**
11772      * @cfg {Boolean} handleMouseEvents
11773      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11774      */
11775     handleMouseEvents : true,
11776
11777     /**
11778      * @cfg {String} tooltipType
11779      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11780      */
11781     tooltipType : 'qtip',
11782
11783     /**
11784      * @cfg {String} cls
11785      * A CSS class to apply to the button's main element.
11786      */
11787     
11788     /**
11789      * @cfg {Roo.Template} template (Optional)
11790      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11791      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11792      * require code modifications if required elements (e.g. a button) aren't present.
11793      */
11794
11795     // private
11796     render : function(renderTo){
11797         var btn;
11798         if(this.hideParent){
11799             this.parentEl = Roo.get(renderTo);
11800         }
11801         if(!this.dhconfig){
11802             if(!this.template){
11803                 if(!Roo.Button.buttonTemplate){
11804                     // hideous table template
11805                     Roo.Button.buttonTemplate = new Roo.Template(
11806                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11807                         '<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>',
11808                         "</tr></tbody></table>");
11809                 }
11810                 this.template = Roo.Button.buttonTemplate;
11811             }
11812             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11813             var btnEl = btn.child("button:first");
11814             btnEl.on('focus', this.onFocus, this);
11815             btnEl.on('blur', this.onBlur, this);
11816             if(this.cls){
11817                 btn.addClass(this.cls);
11818             }
11819             if(this.icon){
11820                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11821             }
11822             if(this.iconCls){
11823                 btnEl.addClass(this.iconCls);
11824                 if(!this.cls){
11825                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11826                 }
11827             }
11828             if(this.tabIndex !== undefined){
11829                 btnEl.dom.tabIndex = this.tabIndex;
11830             }
11831             if(this.tooltip){
11832                 if(typeof this.tooltip == 'object'){
11833                     Roo.QuickTips.tips(Roo.apply({
11834                           target: btnEl.id
11835                     }, this.tooltip));
11836                 } else {
11837                     btnEl.dom[this.tooltipType] = this.tooltip;
11838                 }
11839             }
11840         }else{
11841             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11842         }
11843         this.el = btn;
11844         if(this.id){
11845             this.el.dom.id = this.el.id = this.id;
11846         }
11847         if(this.menu){
11848             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11849             this.menu.on("show", this.onMenuShow, this);
11850             this.menu.on("hide", this.onMenuHide, this);
11851         }
11852         btn.addClass("x-btn");
11853         if(Roo.isIE && !Roo.isIE7){
11854             this.autoWidth.defer(1, this);
11855         }else{
11856             this.autoWidth();
11857         }
11858         if(this.handleMouseEvents){
11859             btn.on("mouseover", this.onMouseOver, this);
11860             btn.on("mouseout", this.onMouseOut, this);
11861             btn.on("mousedown", this.onMouseDown, this);
11862         }
11863         btn.on(this.clickEvent, this.onClick, this);
11864         //btn.on("mouseup", this.onMouseUp, this);
11865         if(this.hidden){
11866             this.hide();
11867         }
11868         if(this.disabled){
11869             this.disable();
11870         }
11871         Roo.ButtonToggleMgr.register(this);
11872         if(this.pressed){
11873             this.el.addClass("x-btn-pressed");
11874         }
11875         if(this.repeat){
11876             var repeater = new Roo.util.ClickRepeater(btn,
11877                 typeof this.repeat == "object" ? this.repeat : {}
11878             );
11879             repeater.on("click", this.onClick,  this);
11880         }
11881         
11882         this.fireEvent('render', this);
11883         
11884     },
11885     /**
11886      * Returns the button's underlying element
11887      * @return {Roo.Element} The element
11888      */
11889     getEl : function(){
11890         return this.el;  
11891     },
11892     
11893     /**
11894      * Destroys this Button and removes any listeners.
11895      */
11896     destroy : function(){
11897         Roo.ButtonToggleMgr.unregister(this);
11898         this.el.removeAllListeners();
11899         this.purgeListeners();
11900         this.el.remove();
11901     },
11902
11903     // private
11904     autoWidth : function(){
11905         if(this.el){
11906             this.el.setWidth("auto");
11907             if(Roo.isIE7 && Roo.isStrict){
11908                 var ib = this.el.child('button');
11909                 if(ib && ib.getWidth() > 20){
11910                     ib.clip();
11911                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11912                 }
11913             }
11914             if(this.minWidth){
11915                 if(this.hidden){
11916                     this.el.beginMeasure();
11917                 }
11918                 if(this.el.getWidth() < this.minWidth){
11919                     this.el.setWidth(this.minWidth);
11920                 }
11921                 if(this.hidden){
11922                     this.el.endMeasure();
11923                 }
11924             }
11925         }
11926     },
11927
11928     /**
11929      * Assigns this button's click handler
11930      * @param {Function} handler The function to call when the button is clicked
11931      * @param {Object} scope (optional) Scope for the function passed in
11932      */
11933     setHandler : function(handler, scope){
11934         this.handler = handler;
11935         this.scope = scope;  
11936     },
11937     
11938     /**
11939      * Sets this button's text
11940      * @param {String} text The button text
11941      */
11942     setText : function(text){
11943         this.text = text;
11944         if(this.el){
11945             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11946         }
11947         this.autoWidth();
11948     },
11949     
11950     /**
11951      * Gets the text for this button
11952      * @return {String} The button text
11953      */
11954     getText : function(){
11955         return this.text;  
11956     },
11957     
11958     /**
11959      * Show this button
11960      */
11961     show: function(){
11962         this.hidden = false;
11963         if(this.el){
11964             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11965         }
11966     },
11967     
11968     /**
11969      * Hide this button
11970      */
11971     hide: function(){
11972         this.hidden = true;
11973         if(this.el){
11974             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11975         }
11976     },
11977     
11978     /**
11979      * Convenience function for boolean show/hide
11980      * @param {Boolean} visible True to show, false to hide
11981      */
11982     setVisible: function(visible){
11983         if(visible) {
11984             this.show();
11985         }else{
11986             this.hide();
11987         }
11988     },
11989     
11990     /**
11991      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11992      * @param {Boolean} state (optional) Force a particular state
11993      */
11994     toggle : function(state){
11995         state = state === undefined ? !this.pressed : state;
11996         if(state != this.pressed){
11997             if(state){
11998                 this.el.addClass("x-btn-pressed");
11999                 this.pressed = true;
12000                 this.fireEvent("toggle", this, true);
12001             }else{
12002                 this.el.removeClass("x-btn-pressed");
12003                 this.pressed = false;
12004                 this.fireEvent("toggle", this, false);
12005             }
12006             if(this.toggleHandler){
12007                 this.toggleHandler.call(this.scope || this, this, state);
12008             }
12009         }
12010     },
12011     
12012     /**
12013      * Focus the button
12014      */
12015     focus : function(){
12016         this.el.child('button:first').focus();
12017     },
12018     
12019     /**
12020      * Disable this button
12021      */
12022     disable : function(){
12023         if(this.el){
12024             this.el.addClass("x-btn-disabled");
12025         }
12026         this.disabled = true;
12027     },
12028     
12029     /**
12030      * Enable this button
12031      */
12032     enable : function(){
12033         if(this.el){
12034             this.el.removeClass("x-btn-disabled");
12035         }
12036         this.disabled = false;
12037     },
12038
12039     /**
12040      * Convenience function for boolean enable/disable
12041      * @param {Boolean} enabled True to enable, false to disable
12042      */
12043     setDisabled : function(v){
12044         this[v !== true ? "enable" : "disable"]();
12045     },
12046
12047     // private
12048     onClick : function(e){
12049         if(e){
12050             e.preventDefault();
12051         }
12052         if(e.button != 0){
12053             return;
12054         }
12055         if(!this.disabled){
12056             if(this.enableToggle){
12057                 this.toggle();
12058             }
12059             if(this.menu && !this.menu.isVisible()){
12060                 this.menu.show(this.el, this.menuAlign);
12061             }
12062             this.fireEvent("click", this, e);
12063             if(this.handler){
12064                 this.el.removeClass("x-btn-over");
12065                 this.handler.call(this.scope || this, this, e);
12066             }
12067         }
12068     },
12069     // private
12070     onMouseOver : function(e){
12071         if(!this.disabled){
12072             this.el.addClass("x-btn-over");
12073             this.fireEvent('mouseover', this, e);
12074         }
12075     },
12076     // private
12077     onMouseOut : function(e){
12078         if(!e.within(this.el,  true)){
12079             this.el.removeClass("x-btn-over");
12080             this.fireEvent('mouseout', this, e);
12081         }
12082     },
12083     // private
12084     onFocus : function(e){
12085         if(!this.disabled){
12086             this.el.addClass("x-btn-focus");
12087         }
12088     },
12089     // private
12090     onBlur : function(e){
12091         this.el.removeClass("x-btn-focus");
12092     },
12093     // private
12094     onMouseDown : function(e){
12095         if(!this.disabled && e.button == 0){
12096             this.el.addClass("x-btn-click");
12097             Roo.get(document).on('mouseup', this.onMouseUp, this);
12098         }
12099     },
12100     // private
12101     onMouseUp : function(e){
12102         if(e.button == 0){
12103             this.el.removeClass("x-btn-click");
12104             Roo.get(document).un('mouseup', this.onMouseUp, this);
12105         }
12106     },
12107     // private
12108     onMenuShow : function(e){
12109         this.el.addClass("x-btn-menu-active");
12110     },
12111     // private
12112     onMenuHide : function(e){
12113         this.el.removeClass("x-btn-menu-active");
12114     }   
12115 });
12116
12117 // Private utility class used by Button
12118 Roo.ButtonToggleMgr = function(){
12119    var groups = {};
12120    
12121    function toggleGroup(btn, state){
12122        if(state){
12123            var g = groups[btn.toggleGroup];
12124            for(var i = 0, l = g.length; i < l; i++){
12125                if(g[i] != btn){
12126                    g[i].toggle(false);
12127                }
12128            }
12129        }
12130    }
12131    
12132    return {
12133        register : function(btn){
12134            if(!btn.toggleGroup){
12135                return;
12136            }
12137            var g = groups[btn.toggleGroup];
12138            if(!g){
12139                g = groups[btn.toggleGroup] = [];
12140            }
12141            g.push(btn);
12142            btn.on("toggle", toggleGroup);
12143        },
12144        
12145        unregister : function(btn){
12146            if(!btn.toggleGroup){
12147                return;
12148            }
12149            var g = groups[btn.toggleGroup];
12150            if(g){
12151                g.remove(btn);
12152                btn.un("toggle", toggleGroup);
12153            }
12154        }
12155    };
12156 }();/*
12157  * Based on:
12158  * Ext JS Library 1.1.1
12159  * Copyright(c) 2006-2007, Ext JS, LLC.
12160  *
12161  * Originally Released Under LGPL - original licence link has changed is not relivant.
12162  *
12163  * Fork - LGPL
12164  * <script type="text/javascript">
12165  */
12166  
12167 /**
12168  * @class Roo.SplitButton
12169  * @extends Roo.Button
12170  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12171  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12172  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12173  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12174  * @cfg {String} arrowTooltip The title attribute of the arrow
12175  * @constructor
12176  * Create a new menu button
12177  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12178  * @param {Object} config The config object
12179  */
12180 Roo.SplitButton = function(renderTo, config){
12181     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12182     /**
12183      * @event arrowclick
12184      * Fires when this button's arrow is clicked
12185      * @param {SplitButton} this
12186      * @param {EventObject} e The click event
12187      */
12188     this.addEvents({"arrowclick":true});
12189 };
12190
12191 Roo.extend(Roo.SplitButton, Roo.Button, {
12192     render : function(renderTo){
12193         // this is one sweet looking template!
12194         var tpl = new Roo.Template(
12195             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12196             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12197             '<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>',
12198             "</tbody></table></td><td>",
12199             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12200             '<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>',
12201             "</tbody></table></td></tr></table>"
12202         );
12203         var btn = tpl.append(renderTo, [this.text, this.type], true);
12204         var btnEl = btn.child("button");
12205         if(this.cls){
12206             btn.addClass(this.cls);
12207         }
12208         if(this.icon){
12209             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12210         }
12211         if(this.iconCls){
12212             btnEl.addClass(this.iconCls);
12213             if(!this.cls){
12214                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12215             }
12216         }
12217         this.el = btn;
12218         if(this.handleMouseEvents){
12219             btn.on("mouseover", this.onMouseOver, this);
12220             btn.on("mouseout", this.onMouseOut, this);
12221             btn.on("mousedown", this.onMouseDown, this);
12222             btn.on("mouseup", this.onMouseUp, this);
12223         }
12224         btn.on(this.clickEvent, this.onClick, this);
12225         if(this.tooltip){
12226             if(typeof this.tooltip == 'object'){
12227                 Roo.QuickTips.tips(Roo.apply({
12228                       target: btnEl.id
12229                 }, this.tooltip));
12230             } else {
12231                 btnEl.dom[this.tooltipType] = this.tooltip;
12232             }
12233         }
12234         if(this.arrowTooltip){
12235             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12236         }
12237         if(this.hidden){
12238             this.hide();
12239         }
12240         if(this.disabled){
12241             this.disable();
12242         }
12243         if(this.pressed){
12244             this.el.addClass("x-btn-pressed");
12245         }
12246         if(Roo.isIE && !Roo.isIE7){
12247             this.autoWidth.defer(1, this);
12248         }else{
12249             this.autoWidth();
12250         }
12251         if(this.menu){
12252             this.menu.on("show", this.onMenuShow, this);
12253             this.menu.on("hide", this.onMenuHide, this);
12254         }
12255         this.fireEvent('render', this);
12256     },
12257
12258     // private
12259     autoWidth : function(){
12260         if(this.el){
12261             var tbl = this.el.child("table:first");
12262             var tbl2 = this.el.child("table:last");
12263             this.el.setWidth("auto");
12264             tbl.setWidth("auto");
12265             if(Roo.isIE7 && Roo.isStrict){
12266                 var ib = this.el.child('button:first');
12267                 if(ib && ib.getWidth() > 20){
12268                     ib.clip();
12269                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12270                 }
12271             }
12272             if(this.minWidth){
12273                 if(this.hidden){
12274                     this.el.beginMeasure();
12275                 }
12276                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12277                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12278                 }
12279                 if(this.hidden){
12280                     this.el.endMeasure();
12281                 }
12282             }
12283             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12284         } 
12285     },
12286     /**
12287      * Sets this button's click handler
12288      * @param {Function} handler The function to call when the button is clicked
12289      * @param {Object} scope (optional) Scope for the function passed above
12290      */
12291     setHandler : function(handler, scope){
12292         this.handler = handler;
12293         this.scope = scope;  
12294     },
12295     
12296     /**
12297      * Sets this button's arrow click handler
12298      * @param {Function} handler The function to call when the arrow is clicked
12299      * @param {Object} scope (optional) Scope for the function passed above
12300      */
12301     setArrowHandler : function(handler, scope){
12302         this.arrowHandler = handler;
12303         this.scope = scope;  
12304     },
12305     
12306     /**
12307      * Focus the button
12308      */
12309     focus : function(){
12310         if(this.el){
12311             this.el.child("button:first").focus();
12312         }
12313     },
12314
12315     // private
12316     onClick : function(e){
12317         e.preventDefault();
12318         if(!this.disabled){
12319             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12320                 if(this.menu && !this.menu.isVisible()){
12321                     this.menu.show(this.el, this.menuAlign);
12322                 }
12323                 this.fireEvent("arrowclick", this, e);
12324                 if(this.arrowHandler){
12325                     this.arrowHandler.call(this.scope || this, this, e);
12326                 }
12327             }else{
12328                 this.fireEvent("click", this, e);
12329                 if(this.handler){
12330                     this.handler.call(this.scope || this, this, e);
12331                 }
12332             }
12333         }
12334     },
12335     // private
12336     onMouseDown : function(e){
12337         if(!this.disabled){
12338             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12339         }
12340     },
12341     // private
12342     onMouseUp : function(e){
12343         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12344     }   
12345 });
12346
12347
12348 // backwards compat
12349 Roo.MenuButton = Roo.SplitButton;/*
12350  * Based on:
12351  * Ext JS Library 1.1.1
12352  * Copyright(c) 2006-2007, Ext JS, LLC.
12353  *
12354  * Originally Released Under LGPL - original licence link has changed is not relivant.
12355  *
12356  * Fork - LGPL
12357  * <script type="text/javascript">
12358  */
12359
12360 /**
12361  * @class Roo.Toolbar
12362  * Basic Toolbar class.
12363  * @constructor
12364  * Creates a new Toolbar
12365  * @param {Object} container The config object
12366  */ 
12367 Roo.Toolbar = function(container, buttons, config)
12368 {
12369     /// old consturctor format still supported..
12370     if(container instanceof Array){ // omit the container for later rendering
12371         buttons = container;
12372         config = buttons;
12373         container = null;
12374     }
12375     if (typeof(container) == 'object' && container.xtype) {
12376         config = container;
12377         container = config.container;
12378         buttons = config.buttons || []; // not really - use items!!
12379     }
12380     var xitems = [];
12381     if (config && config.items) {
12382         xitems = config.items;
12383         delete config.items;
12384     }
12385     Roo.apply(this, config);
12386     this.buttons = buttons;
12387     
12388     if(container){
12389         this.render(container);
12390     }
12391     this.xitems = xitems;
12392     Roo.each(xitems, function(b) {
12393         this.add(b);
12394     }, this);
12395     
12396 };
12397
12398 Roo.Toolbar.prototype = {
12399     /**
12400      * @cfg {Array} items
12401      * array of button configs or elements to add (will be converted to a MixedCollection)
12402      */
12403     
12404     /**
12405      * @cfg {String/HTMLElement/Element} container
12406      * The id or element that will contain the toolbar
12407      */
12408     // private
12409     render : function(ct){
12410         this.el = Roo.get(ct);
12411         if(this.cls){
12412             this.el.addClass(this.cls);
12413         }
12414         // using a table allows for vertical alignment
12415         // 100% width is needed by Safari...
12416         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12417         this.tr = this.el.child("tr", true);
12418         var autoId = 0;
12419         this.items = new Roo.util.MixedCollection(false, function(o){
12420             return o.id || ("item" + (++autoId));
12421         });
12422         if(this.buttons){
12423             this.add.apply(this, this.buttons);
12424             delete this.buttons;
12425         }
12426     },
12427
12428     /**
12429      * Adds element(s) to the toolbar -- this function takes a variable number of 
12430      * arguments of mixed type and adds them to the toolbar.
12431      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12432      * <ul>
12433      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12434      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12435      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12436      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12437      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12438      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12439      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12440      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12441      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12442      * </ul>
12443      * @param {Mixed} arg2
12444      * @param {Mixed} etc.
12445      */
12446     add : function(){
12447         var a = arguments, l = a.length;
12448         for(var i = 0; i < l; i++){
12449             this._add(a[i]);
12450         }
12451     },
12452     // private..
12453     _add : function(el) {
12454         
12455         if (el.xtype) {
12456             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12457         }
12458         
12459         if (el.applyTo){ // some kind of form field
12460             return this.addField(el);
12461         } 
12462         if (el.render){ // some kind of Toolbar.Item
12463             return this.addItem(el);
12464         }
12465         if (typeof el == "string"){ // string
12466             if(el == "separator" || el == "-"){
12467                 return this.addSeparator();
12468             }
12469             if (el == " "){
12470                 return this.addSpacer();
12471             }
12472             if(el == "->"){
12473                 return this.addFill();
12474             }
12475             return this.addText(el);
12476             
12477         }
12478         if(el.tagName){ // element
12479             return this.addElement(el);
12480         }
12481         if(typeof el == "object"){ // must be button config?
12482             return this.addButton(el);
12483         }
12484         // and now what?!?!
12485         return false;
12486         
12487     },
12488     
12489     /**
12490      * Add an Xtype element
12491      * @param {Object} xtype Xtype Object
12492      * @return {Object} created Object
12493      */
12494     addxtype : function(e){
12495         return this.add(e);  
12496     },
12497     
12498     /**
12499      * Returns the Element for this toolbar.
12500      * @return {Roo.Element}
12501      */
12502     getEl : function(){
12503         return this.el;  
12504     },
12505     
12506     /**
12507      * Adds a separator
12508      * @return {Roo.Toolbar.Item} The separator item
12509      */
12510     addSeparator : function(){
12511         return this.addItem(new Roo.Toolbar.Separator());
12512     },
12513
12514     /**
12515      * Adds a spacer element
12516      * @return {Roo.Toolbar.Spacer} The spacer item
12517      */
12518     addSpacer : function(){
12519         return this.addItem(new Roo.Toolbar.Spacer());
12520     },
12521
12522     /**
12523      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12524      * @return {Roo.Toolbar.Fill} The fill item
12525      */
12526     addFill : function(){
12527         return this.addItem(new Roo.Toolbar.Fill());
12528     },
12529
12530     /**
12531      * Adds any standard HTML element to the toolbar
12532      * @param {String/HTMLElement/Element} el The element or id of the element to add
12533      * @return {Roo.Toolbar.Item} The element's item
12534      */
12535     addElement : function(el){
12536         return this.addItem(new Roo.Toolbar.Item(el));
12537     },
12538     /**
12539      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12540      * @type Roo.util.MixedCollection  
12541      */
12542     items : false,
12543      
12544     /**
12545      * Adds any Toolbar.Item or subclass
12546      * @param {Roo.Toolbar.Item} item
12547      * @return {Roo.Toolbar.Item} The item
12548      */
12549     addItem : function(item){
12550         var td = this.nextBlock();
12551         item.render(td);
12552         this.items.add(item);
12553         return item;
12554     },
12555     
12556     /**
12557      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12558      * @param {Object/Array} config A button config or array of configs
12559      * @return {Roo.Toolbar.Button/Array}
12560      */
12561     addButton : function(config){
12562         if(config instanceof Array){
12563             var buttons = [];
12564             for(var i = 0, len = config.length; i < len; i++) {
12565                 buttons.push(this.addButton(config[i]));
12566             }
12567             return buttons;
12568         }
12569         var b = config;
12570         if(!(config instanceof Roo.Toolbar.Button)){
12571             b = config.split ?
12572                 new Roo.Toolbar.SplitButton(config) :
12573                 new Roo.Toolbar.Button(config);
12574         }
12575         var td = this.nextBlock();
12576         b.render(td);
12577         this.items.add(b);
12578         return b;
12579     },
12580     
12581     /**
12582      * Adds text to the toolbar
12583      * @param {String} text The text to add
12584      * @return {Roo.Toolbar.Item} The element's item
12585      */
12586     addText : function(text){
12587         return this.addItem(new Roo.Toolbar.TextItem(text));
12588     },
12589     
12590     /**
12591      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12592      * @param {Number} index The index where the item is to be inserted
12593      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12594      * @return {Roo.Toolbar.Button/Item}
12595      */
12596     insertButton : function(index, item){
12597         if(item instanceof Array){
12598             var buttons = [];
12599             for(var i = 0, len = item.length; i < len; i++) {
12600                buttons.push(this.insertButton(index + i, item[i]));
12601             }
12602             return buttons;
12603         }
12604         if (!(item instanceof Roo.Toolbar.Button)){
12605            item = new Roo.Toolbar.Button(item);
12606         }
12607         var td = document.createElement("td");
12608         this.tr.insertBefore(td, this.tr.childNodes[index]);
12609         item.render(td);
12610         this.items.insert(index, item);
12611         return item;
12612     },
12613     
12614     /**
12615      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12616      * @param {Object} config
12617      * @return {Roo.Toolbar.Item} The element's item
12618      */
12619     addDom : function(config, returnEl){
12620         var td = this.nextBlock();
12621         Roo.DomHelper.overwrite(td, config);
12622         var ti = new Roo.Toolbar.Item(td.firstChild);
12623         ti.render(td);
12624         this.items.add(ti);
12625         return ti;
12626     },
12627
12628     /**
12629      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12630      * @type Roo.util.MixedCollection  
12631      */
12632     fields : false,
12633     
12634     /**
12635      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12636      * Note: the field should not have been rendered yet. For a field that has already been
12637      * rendered, use {@link #addElement}.
12638      * @param {Roo.form.Field} field
12639      * @return {Roo.ToolbarItem}
12640      */
12641      
12642       
12643     addField : function(field) {
12644         if (!this.fields) {
12645             var autoId = 0;
12646             this.fields = new Roo.util.MixedCollection(false, function(o){
12647                 return o.id || ("item" + (++autoId));
12648             });
12649
12650         }
12651         
12652         var td = this.nextBlock();
12653         field.render(td);
12654         var ti = new Roo.Toolbar.Item(td.firstChild);
12655         ti.render(td);
12656         this.items.add(ti);
12657         this.fields.add(field);
12658         return ti;
12659     },
12660     /**
12661      * Hide the toolbar
12662      * @method hide
12663      */
12664      
12665       
12666     hide : function()
12667     {
12668         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12669         this.el.child('div').hide();
12670     },
12671     /**
12672      * Show the toolbar
12673      * @method show
12674      */
12675     show : function()
12676     {
12677         this.el.child('div').show();
12678     },
12679       
12680     // private
12681     nextBlock : function(){
12682         var td = document.createElement("td");
12683         this.tr.appendChild(td);
12684         return td;
12685     },
12686
12687     // private
12688     destroy : function(){
12689         if(this.items){ // rendered?
12690             Roo.destroy.apply(Roo, this.items.items);
12691         }
12692         if(this.fields){ // rendered?
12693             Roo.destroy.apply(Roo, this.fields.items);
12694         }
12695         Roo.Element.uncache(this.el, this.tr);
12696     }
12697 };
12698
12699 /**
12700  * @class Roo.Toolbar.Item
12701  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12702  * @constructor
12703  * Creates a new Item
12704  * @param {HTMLElement} el 
12705  */
12706 Roo.Toolbar.Item = function(el){
12707     this.el = Roo.getDom(el);
12708     this.id = Roo.id(this.el);
12709     this.hidden = false;
12710 };
12711
12712 Roo.Toolbar.Item.prototype = {
12713     
12714     /**
12715      * Get this item's HTML Element
12716      * @return {HTMLElement}
12717      */
12718     getEl : function(){
12719        return this.el;  
12720     },
12721
12722     // private
12723     render : function(td){
12724         this.td = td;
12725         td.appendChild(this.el);
12726     },
12727     
12728     /**
12729      * Removes and destroys this item.
12730      */
12731     destroy : function(){
12732         this.td.parentNode.removeChild(this.td);
12733     },
12734     
12735     /**
12736      * Shows this item.
12737      */
12738     show: function(){
12739         this.hidden = false;
12740         this.td.style.display = "";
12741     },
12742     
12743     /**
12744      * Hides this item.
12745      */
12746     hide: function(){
12747         this.hidden = true;
12748         this.td.style.display = "none";
12749     },
12750     
12751     /**
12752      * Convenience function for boolean show/hide.
12753      * @param {Boolean} visible true to show/false to hide
12754      */
12755     setVisible: function(visible){
12756         if(visible) {
12757             this.show();
12758         }else{
12759             this.hide();
12760         }
12761     },
12762     
12763     /**
12764      * Try to focus this item.
12765      */
12766     focus : function(){
12767         Roo.fly(this.el).focus();
12768     },
12769     
12770     /**
12771      * Disables this item.
12772      */
12773     disable : function(){
12774         Roo.fly(this.td).addClass("x-item-disabled");
12775         this.disabled = true;
12776         this.el.disabled = true;
12777     },
12778     
12779     /**
12780      * Enables this item.
12781      */
12782     enable : function(){
12783         Roo.fly(this.td).removeClass("x-item-disabled");
12784         this.disabled = false;
12785         this.el.disabled = false;
12786     }
12787 };
12788
12789
12790 /**
12791  * @class Roo.Toolbar.Separator
12792  * @extends Roo.Toolbar.Item
12793  * A simple toolbar separator class
12794  * @constructor
12795  * Creates a new Separator
12796  */
12797 Roo.Toolbar.Separator = function(){
12798     var s = document.createElement("span");
12799     s.className = "ytb-sep";
12800     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12801 };
12802 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12803     enable:Roo.emptyFn,
12804     disable:Roo.emptyFn,
12805     focus:Roo.emptyFn
12806 });
12807
12808 /**
12809  * @class Roo.Toolbar.Spacer
12810  * @extends Roo.Toolbar.Item
12811  * A simple element that adds extra horizontal space to a toolbar.
12812  * @constructor
12813  * Creates a new Spacer
12814  */
12815 Roo.Toolbar.Spacer = function(){
12816     var s = document.createElement("div");
12817     s.className = "ytb-spacer";
12818     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12819 };
12820 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12821     enable:Roo.emptyFn,
12822     disable:Roo.emptyFn,
12823     focus:Roo.emptyFn
12824 });
12825
12826 /**
12827  * @class Roo.Toolbar.Fill
12828  * @extends Roo.Toolbar.Spacer
12829  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12830  * @constructor
12831  * Creates a new Spacer
12832  */
12833 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12834     // private
12835     render : function(td){
12836         td.style.width = '100%';
12837         Roo.Toolbar.Fill.superclass.render.call(this, td);
12838     }
12839 });
12840
12841 /**
12842  * @class Roo.Toolbar.TextItem
12843  * @extends Roo.Toolbar.Item
12844  * A simple class that renders text directly into a toolbar.
12845  * @constructor
12846  * Creates a new TextItem
12847  * @param {String} text
12848  */
12849 Roo.Toolbar.TextItem = function(text){
12850     if (typeof(text) == 'object') {
12851         text = text.text;
12852     }
12853     var s = document.createElement("span");
12854     s.className = "ytb-text";
12855     s.innerHTML = text;
12856     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12857 };
12858 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12859     enable:Roo.emptyFn,
12860     disable:Roo.emptyFn,
12861     focus:Roo.emptyFn
12862 });
12863
12864 /**
12865  * @class Roo.Toolbar.Button
12866  * @extends Roo.Button
12867  * A button that renders into a toolbar.
12868  * @constructor
12869  * Creates a new Button
12870  * @param {Object} config A standard {@link Roo.Button} config object
12871  */
12872 Roo.Toolbar.Button = function(config){
12873     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12874 };
12875 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12876     render : function(td){
12877         this.td = td;
12878         Roo.Toolbar.Button.superclass.render.call(this, td);
12879     },
12880     
12881     /**
12882      * Removes and destroys this button
12883      */
12884     destroy : function(){
12885         Roo.Toolbar.Button.superclass.destroy.call(this);
12886         this.td.parentNode.removeChild(this.td);
12887     },
12888     
12889     /**
12890      * Shows this button
12891      */
12892     show: function(){
12893         this.hidden = false;
12894         this.td.style.display = "";
12895     },
12896     
12897     /**
12898      * Hides this button
12899      */
12900     hide: function(){
12901         this.hidden = true;
12902         this.td.style.display = "none";
12903     },
12904
12905     /**
12906      * Disables this item
12907      */
12908     disable : function(){
12909         Roo.fly(this.td).addClass("x-item-disabled");
12910         this.disabled = true;
12911     },
12912
12913     /**
12914      * Enables this item
12915      */
12916     enable : function(){
12917         Roo.fly(this.td).removeClass("x-item-disabled");
12918         this.disabled = false;
12919     }
12920 });
12921 // backwards compat
12922 Roo.ToolbarButton = Roo.Toolbar.Button;
12923
12924 /**
12925  * @class Roo.Toolbar.SplitButton
12926  * @extends Roo.SplitButton
12927  * A menu button that renders into a toolbar.
12928  * @constructor
12929  * Creates a new SplitButton
12930  * @param {Object} config A standard {@link Roo.SplitButton} config object
12931  */
12932 Roo.Toolbar.SplitButton = function(config){
12933     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12934 };
12935 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12936     render : function(td){
12937         this.td = td;
12938         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12939     },
12940     
12941     /**
12942      * Removes and destroys this button
12943      */
12944     destroy : function(){
12945         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12946         this.td.parentNode.removeChild(this.td);
12947     },
12948     
12949     /**
12950      * Shows this button
12951      */
12952     show: function(){
12953         this.hidden = false;
12954         this.td.style.display = "";
12955     },
12956     
12957     /**
12958      * Hides this button
12959      */
12960     hide: function(){
12961         this.hidden = true;
12962         this.td.style.display = "none";
12963     }
12964 });
12965
12966 // backwards compat
12967 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12968  * Based on:
12969  * Ext JS Library 1.1.1
12970  * Copyright(c) 2006-2007, Ext JS, LLC.
12971  *
12972  * Originally Released Under LGPL - original licence link has changed is not relivant.
12973  *
12974  * Fork - LGPL
12975  * <script type="text/javascript">
12976  */
12977  
12978 /**
12979  * @class Roo.PagingToolbar
12980  * @extends Roo.Toolbar
12981  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12982  * @constructor
12983  * Create a new PagingToolbar
12984  * @param {Object} config The config object
12985  */
12986 Roo.PagingToolbar = function(el, ds, config)
12987 {
12988     // old args format still supported... - xtype is prefered..
12989     if (typeof(el) == 'object' && el.xtype) {
12990         // created from xtype...
12991         config = el;
12992         ds = el.dataSource;
12993         el = config.container;
12994     }
12995     var items = [];
12996     if (config.items) {
12997         items = config.items;
12998         config.items = [];
12999     }
13000     
13001     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13002     this.ds = ds;
13003     this.cursor = 0;
13004     this.renderButtons(this.el);
13005     this.bind(ds);
13006     
13007     // supprot items array.
13008    
13009     Roo.each(items, function(e) {
13010         this.add(Roo.factory(e));
13011     },this);
13012     
13013 };
13014
13015 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13016     /**
13017      * @cfg {Roo.data.Store} dataSource
13018      * The underlying data store providing the paged data
13019      */
13020     /**
13021      * @cfg {String/HTMLElement/Element} container
13022      * container The id or element that will contain the toolbar
13023      */
13024     /**
13025      * @cfg {Boolean} displayInfo
13026      * True to display the displayMsg (defaults to false)
13027      */
13028     /**
13029      * @cfg {Number} pageSize
13030      * The number of records to display per page (defaults to 20)
13031      */
13032     pageSize: 20,
13033     /**
13034      * @cfg {String} displayMsg
13035      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13036      */
13037     displayMsg : 'Displaying {0} - {1} of {2}',
13038     /**
13039      * @cfg {String} emptyMsg
13040      * The message to display when no records are found (defaults to "No data to display")
13041      */
13042     emptyMsg : 'No data to display',
13043     /**
13044      * Customizable piece of the default paging text (defaults to "Page")
13045      * @type String
13046      */
13047     beforePageText : "Page",
13048     /**
13049      * Customizable piece of the default paging text (defaults to "of %0")
13050      * @type String
13051      */
13052     afterPageText : "of {0}",
13053     /**
13054      * Customizable piece of the default paging text (defaults to "First Page")
13055      * @type String
13056      */
13057     firstText : "First Page",
13058     /**
13059      * Customizable piece of the default paging text (defaults to "Previous Page")
13060      * @type String
13061      */
13062     prevText : "Previous Page",
13063     /**
13064      * Customizable piece of the default paging text (defaults to "Next Page")
13065      * @type String
13066      */
13067     nextText : "Next Page",
13068     /**
13069      * Customizable piece of the default paging text (defaults to "Last Page")
13070      * @type String
13071      */
13072     lastText : "Last Page",
13073     /**
13074      * Customizable piece of the default paging text (defaults to "Refresh")
13075      * @type String
13076      */
13077     refreshText : "Refresh",
13078
13079     // private
13080     renderButtons : function(el){
13081         Roo.PagingToolbar.superclass.render.call(this, el);
13082         this.first = this.addButton({
13083             tooltip: this.firstText,
13084             cls: "x-btn-icon x-grid-page-first",
13085             disabled: true,
13086             handler: this.onClick.createDelegate(this, ["first"])
13087         });
13088         this.prev = this.addButton({
13089             tooltip: this.prevText,
13090             cls: "x-btn-icon x-grid-page-prev",
13091             disabled: true,
13092             handler: this.onClick.createDelegate(this, ["prev"])
13093         });
13094         //this.addSeparator();
13095         this.add(this.beforePageText);
13096         this.field = Roo.get(this.addDom({
13097            tag: "input",
13098            type: "text",
13099            size: "3",
13100            value: "1",
13101            cls: "x-grid-page-number"
13102         }).el);
13103         this.field.on("keydown", this.onPagingKeydown, this);
13104         this.field.on("focus", function(){this.dom.select();});
13105         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13106         this.field.setHeight(18);
13107         //this.addSeparator();
13108         this.next = this.addButton({
13109             tooltip: this.nextText,
13110             cls: "x-btn-icon x-grid-page-next",
13111             disabled: true,
13112             handler: this.onClick.createDelegate(this, ["next"])
13113         });
13114         this.last = this.addButton({
13115             tooltip: this.lastText,
13116             cls: "x-btn-icon x-grid-page-last",
13117             disabled: true,
13118             handler: this.onClick.createDelegate(this, ["last"])
13119         });
13120         //this.addSeparator();
13121         this.loading = this.addButton({
13122             tooltip: this.refreshText,
13123             cls: "x-btn-icon x-grid-loading",
13124             handler: this.onClick.createDelegate(this, ["refresh"])
13125         });
13126
13127         if(this.displayInfo){
13128             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13129         }
13130     },
13131
13132     // private
13133     updateInfo : function(){
13134         if(this.displayEl){
13135             var count = this.ds.getCount();
13136             var msg = count == 0 ?
13137                 this.emptyMsg :
13138                 String.format(
13139                     this.displayMsg,
13140                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13141                 );
13142             this.displayEl.update(msg);
13143         }
13144     },
13145
13146     // private
13147     onLoad : function(ds, r, o){
13148        this.cursor = o.params ? o.params.start : 0;
13149        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13150
13151        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13152        this.field.dom.value = ap;
13153        this.first.setDisabled(ap == 1);
13154        this.prev.setDisabled(ap == 1);
13155        this.next.setDisabled(ap == ps);
13156        this.last.setDisabled(ap == ps);
13157        this.loading.enable();
13158        this.updateInfo();
13159     },
13160
13161     // private
13162     getPageData : function(){
13163         var total = this.ds.getTotalCount();
13164         return {
13165             total : total,
13166             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13167             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13168         };
13169     },
13170
13171     // private
13172     onLoadError : function(){
13173         this.loading.enable();
13174     },
13175
13176     // private
13177     onPagingKeydown : function(e){
13178         var k = e.getKey();
13179         var d = this.getPageData();
13180         if(k == e.RETURN){
13181             var v = this.field.dom.value, pageNum;
13182             if(!v || isNaN(pageNum = parseInt(v, 10))){
13183                 this.field.dom.value = d.activePage;
13184                 return;
13185             }
13186             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13187             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13188             e.stopEvent();
13189         }
13190         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))
13191         {
13192           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13193           this.field.dom.value = pageNum;
13194           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13195           e.stopEvent();
13196         }
13197         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13198         {
13199           var v = this.field.dom.value, pageNum; 
13200           var increment = (e.shiftKey) ? 10 : 1;
13201           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13202             increment *= -1;
13203           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13204             this.field.dom.value = d.activePage;
13205             return;
13206           }
13207           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13208           {
13209             this.field.dom.value = parseInt(v, 10) + increment;
13210             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13211             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13212           }
13213           e.stopEvent();
13214         }
13215     },
13216
13217     // private
13218     beforeLoad : function(){
13219         if(this.loading){
13220             this.loading.disable();
13221         }
13222     },
13223
13224     // private
13225     onClick : function(which){
13226         var ds = this.ds;
13227         switch(which){
13228             case "first":
13229                 ds.load({params:{start: 0, limit: this.pageSize}});
13230             break;
13231             case "prev":
13232                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13233             break;
13234             case "next":
13235                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13236             break;
13237             case "last":
13238                 var total = ds.getTotalCount();
13239                 var extra = total % this.pageSize;
13240                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13241                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13242             break;
13243             case "refresh":
13244                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13245             break;
13246         }
13247     },
13248
13249     /**
13250      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13251      * @param {Roo.data.Store} store The data store to unbind
13252      */
13253     unbind : function(ds){
13254         ds.un("beforeload", this.beforeLoad, this);
13255         ds.un("load", this.onLoad, this);
13256         ds.un("loadexception", this.onLoadError, this);
13257         ds.un("remove", this.updateInfo, this);
13258         ds.un("add", this.updateInfo, this);
13259         this.ds = undefined;
13260     },
13261
13262     /**
13263      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13264      * @param {Roo.data.Store} store The data store to bind
13265      */
13266     bind : function(ds){
13267         ds.on("beforeload", this.beforeLoad, this);
13268         ds.on("load", this.onLoad, this);
13269         ds.on("loadexception", this.onLoadError, this);
13270         ds.on("remove", this.updateInfo, this);
13271         ds.on("add", this.updateInfo, this);
13272         this.ds = ds;
13273     }
13274 });/*
13275  * Based on:
13276  * Ext JS Library 1.1.1
13277  * Copyright(c) 2006-2007, Ext JS, LLC.
13278  *
13279  * Originally Released Under LGPL - original licence link has changed is not relivant.
13280  *
13281  * Fork - LGPL
13282  * <script type="text/javascript">
13283  */
13284
13285 /**
13286  * @class Roo.Resizable
13287  * @extends Roo.util.Observable
13288  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13289  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13290  * 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
13291  * the element will be wrapped for you automatically.</p>
13292  * <p>Here is the list of valid resize handles:</p>
13293  * <pre>
13294 Value   Description
13295 ------  -------------------
13296  'n'     north
13297  's'     south
13298  'e'     east
13299  'w'     west
13300  'nw'    northwest
13301  'sw'    southwest
13302  'se'    southeast
13303  'ne'    northeast
13304  'hd'    horizontal drag
13305  'all'   all
13306 </pre>
13307  * <p>Here's an example showing the creation of a typical Resizable:</p>
13308  * <pre><code>
13309 var resizer = new Roo.Resizable("element-id", {
13310     handles: 'all',
13311     minWidth: 200,
13312     minHeight: 100,
13313     maxWidth: 500,
13314     maxHeight: 400,
13315     pinned: true
13316 });
13317 resizer.on("resize", myHandler);
13318 </code></pre>
13319  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13320  * resizer.east.setDisplayed(false);</p>
13321  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13322  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13323  * resize operation's new size (defaults to [0, 0])
13324  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13325  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13326  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13327  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13328  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13329  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13330  * @cfg {Number} width The width of the element in pixels (defaults to null)
13331  * @cfg {Number} height The height of the element in pixels (defaults to null)
13332  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13333  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13334  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13335  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13336  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13337  * in favor of the handles config option (defaults to false)
13338  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13339  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13340  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13341  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13342  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13343  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13344  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13345  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13346  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13347  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13348  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13349  * @constructor
13350  * Create a new resizable component
13351  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13352  * @param {Object} config configuration options
13353   */
13354 Roo.Resizable = function(el, config)
13355 {
13356     this.el = Roo.get(el);
13357
13358     if(config && config.wrap){
13359         config.resizeChild = this.el;
13360         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13361         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13362         this.el.setStyle("overflow", "hidden");
13363         this.el.setPositioning(config.resizeChild.getPositioning());
13364         config.resizeChild.clearPositioning();
13365         if(!config.width || !config.height){
13366             var csize = config.resizeChild.getSize();
13367             this.el.setSize(csize.width, csize.height);
13368         }
13369         if(config.pinned && !config.adjustments){
13370             config.adjustments = "auto";
13371         }
13372     }
13373
13374     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13375     this.proxy.unselectable();
13376     this.proxy.enableDisplayMode('block');
13377
13378     Roo.apply(this, config);
13379
13380     if(this.pinned){
13381         this.disableTrackOver = true;
13382         this.el.addClass("x-resizable-pinned");
13383     }
13384     // if the element isn't positioned, make it relative
13385     var position = this.el.getStyle("position");
13386     if(position != "absolute" && position != "fixed"){
13387         this.el.setStyle("position", "relative");
13388     }
13389     if(!this.handles){ // no handles passed, must be legacy style
13390         this.handles = 's,e,se';
13391         if(this.multiDirectional){
13392             this.handles += ',n,w';
13393         }
13394     }
13395     if(this.handles == "all"){
13396         this.handles = "n s e w ne nw se sw";
13397     }
13398     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13399     var ps = Roo.Resizable.positions;
13400     for(var i = 0, len = hs.length; i < len; i++){
13401         if(hs[i] && ps[hs[i]]){
13402             var pos = ps[hs[i]];
13403             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13404         }
13405     }
13406     // legacy
13407     this.corner = this.southeast;
13408     
13409     // updateBox = the box can move..
13410     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13411         this.updateBox = true;
13412     }
13413
13414     this.activeHandle = null;
13415
13416     if(this.resizeChild){
13417         if(typeof this.resizeChild == "boolean"){
13418             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13419         }else{
13420             this.resizeChild = Roo.get(this.resizeChild, true);
13421         }
13422     }
13423     
13424     if(this.adjustments == "auto"){
13425         var rc = this.resizeChild;
13426         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13427         if(rc && (hw || hn)){
13428             rc.position("relative");
13429             rc.setLeft(hw ? hw.el.getWidth() : 0);
13430             rc.setTop(hn ? hn.el.getHeight() : 0);
13431         }
13432         this.adjustments = [
13433             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13434             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13435         ];
13436     }
13437
13438     if(this.draggable){
13439         this.dd = this.dynamic ?
13440             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13441         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13442     }
13443
13444     // public events
13445     this.addEvents({
13446         /**
13447          * @event beforeresize
13448          * Fired before resize is allowed. Set enabled to false to cancel resize.
13449          * @param {Roo.Resizable} this
13450          * @param {Roo.EventObject} e The mousedown event
13451          */
13452         "beforeresize" : true,
13453         /**
13454          * @event resize
13455          * Fired after a resize.
13456          * @param {Roo.Resizable} this
13457          * @param {Number} width The new width
13458          * @param {Number} height The new height
13459          * @param {Roo.EventObject} e The mouseup event
13460          */
13461         "resize" : true
13462     });
13463
13464     if(this.width !== null && this.height !== null){
13465         this.resizeTo(this.width, this.height);
13466     }else{
13467         this.updateChildSize();
13468     }
13469     if(Roo.isIE){
13470         this.el.dom.style.zoom = 1;
13471     }
13472     Roo.Resizable.superclass.constructor.call(this);
13473 };
13474
13475 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13476         resizeChild : false,
13477         adjustments : [0, 0],
13478         minWidth : 5,
13479         minHeight : 5,
13480         maxWidth : 10000,
13481         maxHeight : 10000,
13482         enabled : true,
13483         animate : false,
13484         duration : .35,
13485         dynamic : false,
13486         handles : false,
13487         multiDirectional : false,
13488         disableTrackOver : false,
13489         easing : 'easeOutStrong',
13490         widthIncrement : 0,
13491         heightIncrement : 0,
13492         pinned : false,
13493         width : null,
13494         height : null,
13495         preserveRatio : false,
13496         transparent: false,
13497         minX: 0,
13498         minY: 0,
13499         draggable: false,
13500
13501         /**
13502          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13503          */
13504         constrainTo: undefined,
13505         /**
13506          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13507          */
13508         resizeRegion: undefined,
13509
13510
13511     /**
13512      * Perform a manual resize
13513      * @param {Number} width
13514      * @param {Number} height
13515      */
13516     resizeTo : function(width, height){
13517         this.el.setSize(width, height);
13518         this.updateChildSize();
13519         this.fireEvent("resize", this, width, height, null);
13520     },
13521
13522     // private
13523     startSizing : function(e, handle){
13524         this.fireEvent("beforeresize", this, e);
13525         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13526
13527             if(!this.overlay){
13528                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13529                 this.overlay.unselectable();
13530                 this.overlay.enableDisplayMode("block");
13531                 this.overlay.on("mousemove", this.onMouseMove, this);
13532                 this.overlay.on("mouseup", this.onMouseUp, this);
13533             }
13534             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13535
13536             this.resizing = true;
13537             this.startBox = this.el.getBox();
13538             this.startPoint = e.getXY();
13539             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13540                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13541
13542             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13543             this.overlay.show();
13544
13545             if(this.constrainTo) {
13546                 var ct = Roo.get(this.constrainTo);
13547                 this.resizeRegion = ct.getRegion().adjust(
13548                     ct.getFrameWidth('t'),
13549                     ct.getFrameWidth('l'),
13550                     -ct.getFrameWidth('b'),
13551                     -ct.getFrameWidth('r')
13552                 );
13553             }
13554
13555             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13556             this.proxy.show();
13557             this.proxy.setBox(this.startBox);
13558             if(!this.dynamic){
13559                 this.proxy.setStyle('visibility', 'visible');
13560             }
13561         }
13562     },
13563
13564     // private
13565     onMouseDown : function(handle, e){
13566         if(this.enabled){
13567             e.stopEvent();
13568             this.activeHandle = handle;
13569             this.startSizing(e, handle);
13570         }
13571     },
13572
13573     // private
13574     onMouseUp : function(e){
13575         var size = this.resizeElement();
13576         this.resizing = false;
13577         this.handleOut();
13578         this.overlay.hide();
13579         this.proxy.hide();
13580         this.fireEvent("resize", this, size.width, size.height, e);
13581     },
13582
13583     // private
13584     updateChildSize : function(){
13585         if(this.resizeChild){
13586             var el = this.el;
13587             var child = this.resizeChild;
13588             var adj = this.adjustments;
13589             if(el.dom.offsetWidth){
13590                 var b = el.getSize(true);
13591                 child.setSize(b.width+adj[0], b.height+adj[1]);
13592             }
13593             // Second call here for IE
13594             // The first call enables instant resizing and
13595             // the second call corrects scroll bars if they
13596             // exist
13597             if(Roo.isIE){
13598                 setTimeout(function(){
13599                     if(el.dom.offsetWidth){
13600                         var b = el.getSize(true);
13601                         child.setSize(b.width+adj[0], b.height+adj[1]);
13602                     }
13603                 }, 10);
13604             }
13605         }
13606     },
13607
13608     // private
13609     snap : function(value, inc, min){
13610         if(!inc || !value) return value;
13611         var newValue = value;
13612         var m = value % inc;
13613         if(m > 0){
13614             if(m > (inc/2)){
13615                 newValue = value + (inc-m);
13616             }else{
13617                 newValue = value - m;
13618             }
13619         }
13620         return Math.max(min, newValue);
13621     },
13622
13623     // private
13624     resizeElement : function(){
13625         var box = this.proxy.getBox();
13626         if(this.updateBox){
13627             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13628         }else{
13629             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13630         }
13631         this.updateChildSize();
13632         if(!this.dynamic){
13633             this.proxy.hide();
13634         }
13635         return box;
13636     },
13637
13638     // private
13639     constrain : function(v, diff, m, mx){
13640         if(v - diff < m){
13641             diff = v - m;
13642         }else if(v - diff > mx){
13643             diff = mx - v;
13644         }
13645         return diff;
13646     },
13647
13648     // private
13649     onMouseMove : function(e){
13650         if(this.enabled){
13651             try{// try catch so if something goes wrong the user doesn't get hung
13652
13653             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13654                 return;
13655             }
13656
13657             //var curXY = this.startPoint;
13658             var curSize = this.curSize || this.startBox;
13659             var x = this.startBox.x, y = this.startBox.y;
13660             var ox = x, oy = y;
13661             var w = curSize.width, h = curSize.height;
13662             var ow = w, oh = h;
13663             var mw = this.minWidth, mh = this.minHeight;
13664             var mxw = this.maxWidth, mxh = this.maxHeight;
13665             var wi = this.widthIncrement;
13666             var hi = this.heightIncrement;
13667
13668             var eventXY = e.getXY();
13669             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13670             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13671
13672             var pos = this.activeHandle.position;
13673
13674             switch(pos){
13675                 case "east":
13676                     w += diffX;
13677                     w = Math.min(Math.max(mw, w), mxw);
13678                     break;
13679              
13680                 case "south":
13681                     h += diffY;
13682                     h = Math.min(Math.max(mh, h), mxh);
13683                     break;
13684                 case "southeast":
13685                     w += diffX;
13686                     h += diffY;
13687                     w = Math.min(Math.max(mw, w), mxw);
13688                     h = Math.min(Math.max(mh, h), mxh);
13689                     break;
13690                 case "north":
13691                     diffY = this.constrain(h, diffY, mh, mxh);
13692                     y += diffY;
13693                     h -= diffY;
13694                     break;
13695                 case "hdrag":
13696                     
13697                     if (wi) {
13698                         var adiffX = Math.abs(diffX);
13699                         var sub = (adiffX % wi); // how much 
13700                         if (sub > (wi/2)) { // far enough to snap
13701                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13702                         } else {
13703                             // remove difference.. 
13704                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13705                         }
13706                     }
13707                     x += diffX;
13708                     x = Math.max(this.minX, x);
13709                     break;
13710                 case "west":
13711                     diffX = this.constrain(w, diffX, mw, mxw);
13712                     x += diffX;
13713                     w -= diffX;
13714                     break;
13715                 case "northeast":
13716                     w += diffX;
13717                     w = Math.min(Math.max(mw, w), mxw);
13718                     diffY = this.constrain(h, diffY, mh, mxh);
13719                     y += diffY;
13720                     h -= diffY;
13721                     break;
13722                 case "northwest":
13723                     diffX = this.constrain(w, diffX, mw, mxw);
13724                     diffY = this.constrain(h, diffY, mh, mxh);
13725                     y += diffY;
13726                     h -= diffY;
13727                     x += diffX;
13728                     w -= diffX;
13729                     break;
13730                case "southwest":
13731                     diffX = this.constrain(w, diffX, mw, mxw);
13732                     h += diffY;
13733                     h = Math.min(Math.max(mh, h), mxh);
13734                     x += diffX;
13735                     w -= diffX;
13736                     break;
13737             }
13738
13739             var sw = this.snap(w, wi, mw);
13740             var sh = this.snap(h, hi, mh);
13741             if(sw != w || sh != h){
13742                 switch(pos){
13743                     case "northeast":
13744                         y -= sh - h;
13745                     break;
13746                     case "north":
13747                         y -= sh - h;
13748                         break;
13749                     case "southwest":
13750                         x -= sw - w;
13751                     break;
13752                     case "west":
13753                         x -= sw - w;
13754                         break;
13755                     case "northwest":
13756                         x -= sw - w;
13757                         y -= sh - h;
13758                     break;
13759                 }
13760                 w = sw;
13761                 h = sh;
13762             }
13763
13764             if(this.preserveRatio){
13765                 switch(pos){
13766                     case "southeast":
13767                     case "east":
13768                         h = oh * (w/ow);
13769                         h = Math.min(Math.max(mh, h), mxh);
13770                         w = ow * (h/oh);
13771                        break;
13772                     case "south":
13773                         w = ow * (h/oh);
13774                         w = Math.min(Math.max(mw, w), mxw);
13775                         h = oh * (w/ow);
13776                         break;
13777                     case "northeast":
13778                         w = ow * (h/oh);
13779                         w = Math.min(Math.max(mw, w), mxw);
13780                         h = oh * (w/ow);
13781                     break;
13782                     case "north":
13783                         var tw = w;
13784                         w = ow * (h/oh);
13785                         w = Math.min(Math.max(mw, w), mxw);
13786                         h = oh * (w/ow);
13787                         x += (tw - w) / 2;
13788                         break;
13789                     case "southwest":
13790                         h = oh * (w/ow);
13791                         h = Math.min(Math.max(mh, h), mxh);
13792                         var tw = w;
13793                         w = ow * (h/oh);
13794                         x += tw - w;
13795                         break;
13796                     case "west":
13797                         var th = h;
13798                         h = oh * (w/ow);
13799                         h = Math.min(Math.max(mh, h), mxh);
13800                         y += (th - h) / 2;
13801                         var tw = w;
13802                         w = ow * (h/oh);
13803                         x += tw - w;
13804                        break;
13805                     case "northwest":
13806                         var tw = w;
13807                         var th = h;
13808                         h = oh * (w/ow);
13809                         h = Math.min(Math.max(mh, h), mxh);
13810                         w = ow * (h/oh);
13811                         y += th - h;
13812                         x += tw - w;
13813                        break;
13814
13815                 }
13816             }
13817             if (pos == 'hdrag') {
13818                 w = ow;
13819             }
13820             this.proxy.setBounds(x, y, w, h);
13821             if(this.dynamic){
13822                 this.resizeElement();
13823             }
13824             }catch(e){}
13825         }
13826     },
13827
13828     // private
13829     handleOver : function(){
13830         if(this.enabled){
13831             this.el.addClass("x-resizable-over");
13832         }
13833     },
13834
13835     // private
13836     handleOut : function(){
13837         if(!this.resizing){
13838             this.el.removeClass("x-resizable-over");
13839         }
13840     },
13841
13842     /**
13843      * Returns the element this component is bound to.
13844      * @return {Roo.Element}
13845      */
13846     getEl : function(){
13847         return this.el;
13848     },
13849
13850     /**
13851      * Returns the resizeChild element (or null).
13852      * @return {Roo.Element}
13853      */
13854     getResizeChild : function(){
13855         return this.resizeChild;
13856     },
13857
13858     /**
13859      * Destroys this resizable. If the element was wrapped and
13860      * removeEl is not true then the element remains.
13861      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13862      */
13863     destroy : function(removeEl){
13864         this.proxy.remove();
13865         if(this.overlay){
13866             this.overlay.removeAllListeners();
13867             this.overlay.remove();
13868         }
13869         var ps = Roo.Resizable.positions;
13870         for(var k in ps){
13871             if(typeof ps[k] != "function" && this[ps[k]]){
13872                 var h = this[ps[k]];
13873                 h.el.removeAllListeners();
13874                 h.el.remove();
13875             }
13876         }
13877         if(removeEl){
13878             this.el.update("");
13879             this.el.remove();
13880         }
13881     }
13882 });
13883
13884 // private
13885 // hash to map config positions to true positions
13886 Roo.Resizable.positions = {
13887     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13888     hd: "hdrag"
13889 };
13890
13891 // private
13892 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13893     if(!this.tpl){
13894         // only initialize the template if resizable is used
13895         var tpl = Roo.DomHelper.createTemplate(
13896             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13897         );
13898         tpl.compile();
13899         Roo.Resizable.Handle.prototype.tpl = tpl;
13900     }
13901     this.position = pos;
13902     this.rz = rz;
13903     // show north drag fro topdra
13904     var handlepos = pos == 'hdrag' ? 'north' : pos;
13905     
13906     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13907     if (pos == 'hdrag') {
13908         this.el.setStyle('cursor', 'pointer');
13909     }
13910     this.el.unselectable();
13911     if(transparent){
13912         this.el.setOpacity(0);
13913     }
13914     this.el.on("mousedown", this.onMouseDown, this);
13915     if(!disableTrackOver){
13916         this.el.on("mouseover", this.onMouseOver, this);
13917         this.el.on("mouseout", this.onMouseOut, this);
13918     }
13919 };
13920
13921 // private
13922 Roo.Resizable.Handle.prototype = {
13923     afterResize : function(rz){
13924         // do nothing
13925     },
13926     // private
13927     onMouseDown : function(e){
13928         this.rz.onMouseDown(this, e);
13929     },
13930     // private
13931     onMouseOver : function(e){
13932         this.rz.handleOver(this, e);
13933     },
13934     // private
13935     onMouseOut : function(e){
13936         this.rz.handleOut(this, e);
13937     }
13938 };/*
13939  * Based on:
13940  * Ext JS Library 1.1.1
13941  * Copyright(c) 2006-2007, Ext JS, LLC.
13942  *
13943  * Originally Released Under LGPL - original licence link has changed is not relivant.
13944  *
13945  * Fork - LGPL
13946  * <script type="text/javascript">
13947  */
13948
13949 /**
13950  * @class Roo.Editor
13951  * @extends Roo.Component
13952  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13953  * @constructor
13954  * Create a new Editor
13955  * @param {Roo.form.Field} field The Field object (or descendant)
13956  * @param {Object} config The config object
13957  */
13958 Roo.Editor = function(field, config){
13959     Roo.Editor.superclass.constructor.call(this, config);
13960     this.field = field;
13961     this.addEvents({
13962         /**
13963              * @event beforestartedit
13964              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13965              * false from the handler of this event.
13966              * @param {Editor} this
13967              * @param {Roo.Element} boundEl The underlying element bound to this editor
13968              * @param {Mixed} value The field value being set
13969              */
13970         "beforestartedit" : true,
13971         /**
13972              * @event startedit
13973              * Fires when this editor is displayed
13974              * @param {Roo.Element} boundEl The underlying element bound to this editor
13975              * @param {Mixed} value The starting field value
13976              */
13977         "startedit" : true,
13978         /**
13979              * @event beforecomplete
13980              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13981              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13982              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13983              * event will not fire since no edit actually occurred.
13984              * @param {Editor} this
13985              * @param {Mixed} value The current field value
13986              * @param {Mixed} startValue The original field value
13987              */
13988         "beforecomplete" : true,
13989         /**
13990              * @event complete
13991              * Fires after editing is complete and any changed value has been written to the underlying field.
13992              * @param {Editor} this
13993              * @param {Mixed} value The current field value
13994              * @param {Mixed} startValue The original field value
13995              */
13996         "complete" : true,
13997         /**
13998          * @event specialkey
13999          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14000          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14001          * @param {Roo.form.Field} this
14002          * @param {Roo.EventObject} e The event object
14003          */
14004         "specialkey" : true
14005     });
14006 };
14007
14008 Roo.extend(Roo.Editor, Roo.Component, {
14009     /**
14010      * @cfg {Boolean/String} autosize
14011      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14012      * or "height" to adopt the height only (defaults to false)
14013      */
14014     /**
14015      * @cfg {Boolean} revertInvalid
14016      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14017      * validation fails (defaults to true)
14018      */
14019     /**
14020      * @cfg {Boolean} ignoreNoChange
14021      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14022      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14023      * will never be ignored.
14024      */
14025     /**
14026      * @cfg {Boolean} hideEl
14027      * False to keep the bound element visible while the editor is displayed (defaults to true)
14028      */
14029     /**
14030      * @cfg {Mixed} value
14031      * The data value of the underlying field (defaults to "")
14032      */
14033     value : "",
14034     /**
14035      * @cfg {String} alignment
14036      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14037      */
14038     alignment: "c-c?",
14039     /**
14040      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14041      * for bottom-right shadow (defaults to "frame")
14042      */
14043     shadow : "frame",
14044     /**
14045      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14046      */
14047     constrain : false,
14048     /**
14049      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14050      */
14051     completeOnEnter : false,
14052     /**
14053      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14054      */
14055     cancelOnEsc : false,
14056     /**
14057      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14058      */
14059     updateEl : false,
14060
14061     // private
14062     onRender : function(ct, position){
14063         this.el = new Roo.Layer({
14064             shadow: this.shadow,
14065             cls: "x-editor",
14066             parentEl : ct,
14067             shim : this.shim,
14068             shadowOffset:4,
14069             id: this.id,
14070             constrain: this.constrain
14071         });
14072         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14073         if(this.field.msgTarget != 'title'){
14074             this.field.msgTarget = 'qtip';
14075         }
14076         this.field.render(this.el);
14077         if(Roo.isGecko){
14078             this.field.el.dom.setAttribute('autocomplete', 'off');
14079         }
14080         this.field.on("specialkey", this.onSpecialKey, this);
14081         if(this.swallowKeys){
14082             this.field.el.swallowEvent(['keydown','keypress']);
14083         }
14084         this.field.show();
14085         this.field.on("blur", this.onBlur, this);
14086         if(this.field.grow){
14087             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14088         }
14089     },
14090
14091     onSpecialKey : function(field, e)
14092     {
14093         //Roo.log('editor onSpecialKey');
14094         if(this.completeOnEnter && e.getKey() == e.ENTER){
14095             e.stopEvent();
14096             this.completeEdit();
14097             return;
14098         }
14099         // do not fire special key otherwise it might hide close the editor...
14100         if(e.getKey() == e.ENTER){    
14101             return;
14102         }
14103         if(this.cancelOnEsc && e.getKey() == e.ESC){
14104             this.cancelEdit();
14105             return;
14106         } 
14107         this.fireEvent('specialkey', field, e);
14108     
14109     },
14110
14111     /**
14112      * Starts the editing process and shows the editor.
14113      * @param {String/HTMLElement/Element} el The element to edit
14114      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14115       * to the innerHTML of el.
14116      */
14117     startEdit : function(el, value){
14118         if(this.editing){
14119             this.completeEdit();
14120         }
14121         this.boundEl = Roo.get(el);
14122         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14123         if(!this.rendered){
14124             this.render(this.parentEl || document.body);
14125         }
14126         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14127             return;
14128         }
14129         this.startValue = v;
14130         this.field.setValue(v);
14131         if(this.autoSize){
14132             var sz = this.boundEl.getSize();
14133             switch(this.autoSize){
14134                 case "width":
14135                 this.setSize(sz.width,  "");
14136                 break;
14137                 case "height":
14138                 this.setSize("",  sz.height);
14139                 break;
14140                 default:
14141                 this.setSize(sz.width,  sz.height);
14142             }
14143         }
14144         this.el.alignTo(this.boundEl, this.alignment);
14145         this.editing = true;
14146         if(Roo.QuickTips){
14147             Roo.QuickTips.disable();
14148         }
14149         this.show();
14150     },
14151
14152     /**
14153      * Sets the height and width of this editor.
14154      * @param {Number} width The new width
14155      * @param {Number} height The new height
14156      */
14157     setSize : function(w, h){
14158         this.field.setSize(w, h);
14159         if(this.el){
14160             this.el.sync();
14161         }
14162     },
14163
14164     /**
14165      * Realigns the editor to the bound field based on the current alignment config value.
14166      */
14167     realign : function(){
14168         this.el.alignTo(this.boundEl, this.alignment);
14169     },
14170
14171     /**
14172      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14173      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14174      */
14175     completeEdit : function(remainVisible){
14176         if(!this.editing){
14177             return;
14178         }
14179         var v = this.getValue();
14180         if(this.revertInvalid !== false && !this.field.isValid()){
14181             v = this.startValue;
14182             this.cancelEdit(true);
14183         }
14184         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14185             this.editing = false;
14186             this.hide();
14187             return;
14188         }
14189         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14190             this.editing = false;
14191             if(this.updateEl && this.boundEl){
14192                 this.boundEl.update(v);
14193             }
14194             if(remainVisible !== true){
14195                 this.hide();
14196             }
14197             this.fireEvent("complete", this, v, this.startValue);
14198         }
14199     },
14200
14201     // private
14202     onShow : function(){
14203         this.el.show();
14204         if(this.hideEl !== false){
14205             this.boundEl.hide();
14206         }
14207         this.field.show();
14208         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14209             this.fixIEFocus = true;
14210             this.deferredFocus.defer(50, this);
14211         }else{
14212             this.field.focus();
14213         }
14214         this.fireEvent("startedit", this.boundEl, this.startValue);
14215     },
14216
14217     deferredFocus : function(){
14218         if(this.editing){
14219             this.field.focus();
14220         }
14221     },
14222
14223     /**
14224      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14225      * reverted to the original starting value.
14226      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14227      * cancel (defaults to false)
14228      */
14229     cancelEdit : function(remainVisible){
14230         if(this.editing){
14231             this.setValue(this.startValue);
14232             if(remainVisible !== true){
14233                 this.hide();
14234             }
14235         }
14236     },
14237
14238     // private
14239     onBlur : function(){
14240         if(this.allowBlur !== true && this.editing){
14241             this.completeEdit();
14242         }
14243     },
14244
14245     // private
14246     onHide : function(){
14247         if(this.editing){
14248             this.completeEdit();
14249             return;
14250         }
14251         this.field.blur();
14252         if(this.field.collapse){
14253             this.field.collapse();
14254         }
14255         this.el.hide();
14256         if(this.hideEl !== false){
14257             this.boundEl.show();
14258         }
14259         if(Roo.QuickTips){
14260             Roo.QuickTips.enable();
14261         }
14262     },
14263
14264     /**
14265      * Sets the data value of the editor
14266      * @param {Mixed} value Any valid value supported by the underlying field
14267      */
14268     setValue : function(v){
14269         this.field.setValue(v);
14270     },
14271
14272     /**
14273      * Gets the data value of the editor
14274      * @return {Mixed} The data value
14275      */
14276     getValue : function(){
14277         return this.field.getValue();
14278     }
14279 });/*
14280  * Based on:
14281  * Ext JS Library 1.1.1
14282  * Copyright(c) 2006-2007, Ext JS, LLC.
14283  *
14284  * Originally Released Under LGPL - original licence link has changed is not relivant.
14285  *
14286  * Fork - LGPL
14287  * <script type="text/javascript">
14288  */
14289  
14290 /**
14291  * @class Roo.BasicDialog
14292  * @extends Roo.util.Observable
14293  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14294  * <pre><code>
14295 var dlg = new Roo.BasicDialog("my-dlg", {
14296     height: 200,
14297     width: 300,
14298     minHeight: 100,
14299     minWidth: 150,
14300     modal: true,
14301     proxyDrag: true,
14302     shadow: true
14303 });
14304 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14305 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14306 dlg.addButton('Cancel', dlg.hide, dlg);
14307 dlg.show();
14308 </code></pre>
14309   <b>A Dialog should always be a direct child of the body element.</b>
14310  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14311  * @cfg {String} title Default text to display in the title bar (defaults to null)
14312  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14313  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14314  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14315  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14316  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14317  * (defaults to null with no animation)
14318  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14319  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14320  * property for valid values (defaults to 'all')
14321  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14322  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14323  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14324  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14325  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14326  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14327  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14328  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14329  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14330  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14331  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14332  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14333  * draggable = true (defaults to false)
14334  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14335  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14336  * shadow (defaults to false)
14337  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14338  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14339  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14340  * @cfg {Array} buttons Array of buttons
14341  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14342  * @constructor
14343  * Create a new BasicDialog.
14344  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14345  * @param {Object} config Configuration options
14346  */
14347 Roo.BasicDialog = function(el, config){
14348     this.el = Roo.get(el);
14349     var dh = Roo.DomHelper;
14350     if(!this.el && config && config.autoCreate){
14351         if(typeof config.autoCreate == "object"){
14352             if(!config.autoCreate.id){
14353                 config.autoCreate.id = el;
14354             }
14355             this.el = dh.append(document.body,
14356                         config.autoCreate, true);
14357         }else{
14358             this.el = dh.append(document.body,
14359                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14360         }
14361     }
14362     el = this.el;
14363     el.setDisplayed(true);
14364     el.hide = this.hideAction;
14365     this.id = el.id;
14366     el.addClass("x-dlg");
14367
14368     Roo.apply(this, config);
14369
14370     this.proxy = el.createProxy("x-dlg-proxy");
14371     this.proxy.hide = this.hideAction;
14372     this.proxy.setOpacity(.5);
14373     this.proxy.hide();
14374
14375     if(config.width){
14376         el.setWidth(config.width);
14377     }
14378     if(config.height){
14379         el.setHeight(config.height);
14380     }
14381     this.size = el.getSize();
14382     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14383         this.xy = [config.x,config.y];
14384     }else{
14385         this.xy = el.getCenterXY(true);
14386     }
14387     /** The header element @type Roo.Element */
14388     this.header = el.child("> .x-dlg-hd");
14389     /** The body element @type Roo.Element */
14390     this.body = el.child("> .x-dlg-bd");
14391     /** The footer element @type Roo.Element */
14392     this.footer = el.child("> .x-dlg-ft");
14393
14394     if(!this.header){
14395         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14396     }
14397     if(!this.body){
14398         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14399     }
14400
14401     this.header.unselectable();
14402     if(this.title){
14403         this.header.update(this.title);
14404     }
14405     // this element allows the dialog to be focused for keyboard event
14406     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14407     this.focusEl.swallowEvent("click", true);
14408
14409     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14410
14411     // wrap the body and footer for special rendering
14412     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14413     if(this.footer){
14414         this.bwrap.dom.appendChild(this.footer.dom);
14415     }
14416
14417     this.bg = this.el.createChild({
14418         tag: "div", cls:"x-dlg-bg",
14419         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14420     });
14421     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14422
14423
14424     if(this.autoScroll !== false && !this.autoTabs){
14425         this.body.setStyle("overflow", "auto");
14426     }
14427
14428     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14429
14430     if(this.closable !== false){
14431         this.el.addClass("x-dlg-closable");
14432         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14433         this.close.on("click", this.closeClick, this);
14434         this.close.addClassOnOver("x-dlg-close-over");
14435     }
14436     if(this.collapsible !== false){
14437         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14438         this.collapseBtn.on("click", this.collapseClick, this);
14439         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14440         this.header.on("dblclick", this.collapseClick, this);
14441     }
14442     if(this.resizable !== false){
14443         this.el.addClass("x-dlg-resizable");
14444         this.resizer = new Roo.Resizable(el, {
14445             minWidth: this.minWidth || 80,
14446             minHeight:this.minHeight || 80,
14447             handles: this.resizeHandles || "all",
14448             pinned: true
14449         });
14450         this.resizer.on("beforeresize", this.beforeResize, this);
14451         this.resizer.on("resize", this.onResize, this);
14452     }
14453     if(this.draggable !== false){
14454         el.addClass("x-dlg-draggable");
14455         if (!this.proxyDrag) {
14456             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14457         }
14458         else {
14459             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14460         }
14461         dd.setHandleElId(this.header.id);
14462         dd.endDrag = this.endMove.createDelegate(this);
14463         dd.startDrag = this.startMove.createDelegate(this);
14464         dd.onDrag = this.onDrag.createDelegate(this);
14465         dd.scroll = false;
14466         this.dd = dd;
14467     }
14468     if(this.modal){
14469         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14470         this.mask.enableDisplayMode("block");
14471         this.mask.hide();
14472         this.el.addClass("x-dlg-modal");
14473     }
14474     if(this.shadow){
14475         this.shadow = new Roo.Shadow({
14476             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14477             offset : this.shadowOffset
14478         });
14479     }else{
14480         this.shadowOffset = 0;
14481     }
14482     if(Roo.useShims && this.shim !== false){
14483         this.shim = this.el.createShim();
14484         this.shim.hide = this.hideAction;
14485         this.shim.hide();
14486     }else{
14487         this.shim = false;
14488     }
14489     if(this.autoTabs){
14490         this.initTabs();
14491     }
14492     if (this.buttons) { 
14493         var bts= this.buttons;
14494         this.buttons = [];
14495         Roo.each(bts, function(b) {
14496             this.addButton(b);
14497         }, this);
14498     }
14499     
14500     
14501     this.addEvents({
14502         /**
14503          * @event keydown
14504          * Fires when a key is pressed
14505          * @param {Roo.BasicDialog} this
14506          * @param {Roo.EventObject} e
14507          */
14508         "keydown" : true,
14509         /**
14510          * @event move
14511          * Fires when this dialog is moved by the user.
14512          * @param {Roo.BasicDialog} this
14513          * @param {Number} x The new page X
14514          * @param {Number} y The new page Y
14515          */
14516         "move" : true,
14517         /**
14518          * @event resize
14519          * Fires when this dialog is resized by the user.
14520          * @param {Roo.BasicDialog} this
14521          * @param {Number} width The new width
14522          * @param {Number} height The new height
14523          */
14524         "resize" : true,
14525         /**
14526          * @event beforehide
14527          * Fires before this dialog is hidden.
14528          * @param {Roo.BasicDialog} this
14529          */
14530         "beforehide" : true,
14531         /**
14532          * @event hide
14533          * Fires when this dialog is hidden.
14534          * @param {Roo.BasicDialog} this
14535          */
14536         "hide" : true,
14537         /**
14538          * @event beforeshow
14539          * Fires before this dialog is shown.
14540          * @param {Roo.BasicDialog} this
14541          */
14542         "beforeshow" : true,
14543         /**
14544          * @event show
14545          * Fires when this dialog is shown.
14546          * @param {Roo.BasicDialog} this
14547          */
14548         "show" : true
14549     });
14550     el.on("keydown", this.onKeyDown, this);
14551     el.on("mousedown", this.toFront, this);
14552     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14553     this.el.hide();
14554     Roo.DialogManager.register(this);
14555     Roo.BasicDialog.superclass.constructor.call(this);
14556 };
14557
14558 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14559     shadowOffset: Roo.isIE ? 6 : 5,
14560     minHeight: 80,
14561     minWidth: 200,
14562     minButtonWidth: 75,
14563     defaultButton: null,
14564     buttonAlign: "right",
14565     tabTag: 'div',
14566     firstShow: true,
14567
14568     /**
14569      * Sets the dialog title text
14570      * @param {String} text The title text to display
14571      * @return {Roo.BasicDialog} this
14572      */
14573     setTitle : function(text){
14574         this.header.update(text);
14575         return this;
14576     },
14577
14578     // private
14579     closeClick : function(){
14580         this.hide();
14581     },
14582
14583     // private
14584     collapseClick : function(){
14585         this[this.collapsed ? "expand" : "collapse"]();
14586     },
14587
14588     /**
14589      * Collapses the dialog to its minimized state (only the title bar is visible).
14590      * Equivalent to the user clicking the collapse dialog button.
14591      */
14592     collapse : function(){
14593         if(!this.collapsed){
14594             this.collapsed = true;
14595             this.el.addClass("x-dlg-collapsed");
14596             this.restoreHeight = this.el.getHeight();
14597             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14598         }
14599     },
14600
14601     /**
14602      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14603      * clicking the expand dialog button.
14604      */
14605     expand : function(){
14606         if(this.collapsed){
14607             this.collapsed = false;
14608             this.el.removeClass("x-dlg-collapsed");
14609             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14610         }
14611     },
14612
14613     /**
14614      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14615      * @return {Roo.TabPanel} The tabs component
14616      */
14617     initTabs : function(){
14618         var tabs = this.getTabs();
14619         while(tabs.getTab(0)){
14620             tabs.removeTab(0);
14621         }
14622         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14623             var dom = el.dom;
14624             tabs.addTab(Roo.id(dom), dom.title);
14625             dom.title = "";
14626         });
14627         tabs.activate(0);
14628         return tabs;
14629     },
14630
14631     // private
14632     beforeResize : function(){
14633         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14634     },
14635
14636     // private
14637     onResize : function(){
14638         this.refreshSize();
14639         this.syncBodyHeight();
14640         this.adjustAssets();
14641         this.focus();
14642         this.fireEvent("resize", this, this.size.width, this.size.height);
14643     },
14644
14645     // private
14646     onKeyDown : function(e){
14647         if(this.isVisible()){
14648             this.fireEvent("keydown", this, e);
14649         }
14650     },
14651
14652     /**
14653      * Resizes the dialog.
14654      * @param {Number} width
14655      * @param {Number} height
14656      * @return {Roo.BasicDialog} this
14657      */
14658     resizeTo : function(width, height){
14659         this.el.setSize(width, height);
14660         this.size = {width: width, height: height};
14661         this.syncBodyHeight();
14662         if(this.fixedcenter){
14663             this.center();
14664         }
14665         if(this.isVisible()){
14666             this.constrainXY();
14667             this.adjustAssets();
14668         }
14669         this.fireEvent("resize", this, width, height);
14670         return this;
14671     },
14672
14673
14674     /**
14675      * Resizes the dialog to fit the specified content size.
14676      * @param {Number} width
14677      * @param {Number} height
14678      * @return {Roo.BasicDialog} this
14679      */
14680     setContentSize : function(w, h){
14681         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14682         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14683         //if(!this.el.isBorderBox()){
14684             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14685             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14686         //}
14687         if(this.tabs){
14688             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14689             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14690         }
14691         this.resizeTo(w, h);
14692         return this;
14693     },
14694
14695     /**
14696      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14697      * executed in response to a particular key being pressed while the dialog is active.
14698      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14699      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14700      * @param {Function} fn The function to call
14701      * @param {Object} scope (optional) The scope of the function
14702      * @return {Roo.BasicDialog} this
14703      */
14704     addKeyListener : function(key, fn, scope){
14705         var keyCode, shift, ctrl, alt;
14706         if(typeof key == "object" && !(key instanceof Array)){
14707             keyCode = key["key"];
14708             shift = key["shift"];
14709             ctrl = key["ctrl"];
14710             alt = key["alt"];
14711         }else{
14712             keyCode = key;
14713         }
14714         var handler = function(dlg, e){
14715             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14716                 var k = e.getKey();
14717                 if(keyCode instanceof Array){
14718                     for(var i = 0, len = keyCode.length; i < len; i++){
14719                         if(keyCode[i] == k){
14720                           fn.call(scope || window, dlg, k, e);
14721                           return;
14722                         }
14723                     }
14724                 }else{
14725                     if(k == keyCode){
14726                         fn.call(scope || window, dlg, k, e);
14727                     }
14728                 }
14729             }
14730         };
14731         this.on("keydown", handler);
14732         return this;
14733     },
14734
14735     /**
14736      * Returns the TabPanel component (creates it if it doesn't exist).
14737      * Note: If you wish to simply check for the existence of tabs without creating them,
14738      * check for a null 'tabs' property.
14739      * @return {Roo.TabPanel} The tabs component
14740      */
14741     getTabs : function(){
14742         if(!this.tabs){
14743             this.el.addClass("x-dlg-auto-tabs");
14744             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14745             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14746         }
14747         return this.tabs;
14748     },
14749
14750     /**
14751      * Adds a button to the footer section of the dialog.
14752      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14753      * object or a valid Roo.DomHelper element config
14754      * @param {Function} handler The function called when the button is clicked
14755      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14756      * @return {Roo.Button} The new button
14757      */
14758     addButton : function(config, handler, scope){
14759         var dh = Roo.DomHelper;
14760         if(!this.footer){
14761             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14762         }
14763         if(!this.btnContainer){
14764             var tb = this.footer.createChild({
14765
14766                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14767                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14768             }, null, true);
14769             this.btnContainer = tb.firstChild.firstChild.firstChild;
14770         }
14771         var bconfig = {
14772             handler: handler,
14773             scope: scope,
14774             minWidth: this.minButtonWidth,
14775             hideParent:true
14776         };
14777         if(typeof config == "string"){
14778             bconfig.text = config;
14779         }else{
14780             if(config.tag){
14781                 bconfig.dhconfig = config;
14782             }else{
14783                 Roo.apply(bconfig, config);
14784             }
14785         }
14786         var fc = false;
14787         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14788             bconfig.position = Math.max(0, bconfig.position);
14789             fc = this.btnContainer.childNodes[bconfig.position];
14790         }
14791          
14792         var btn = new Roo.Button(
14793             fc ? 
14794                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14795                 : this.btnContainer.appendChild(document.createElement("td")),
14796             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14797             bconfig
14798         );
14799         this.syncBodyHeight();
14800         if(!this.buttons){
14801             /**
14802              * Array of all the buttons that have been added to this dialog via addButton
14803              * @type Array
14804              */
14805             this.buttons = [];
14806         }
14807         this.buttons.push(btn);
14808         return btn;
14809     },
14810
14811     /**
14812      * Sets the default button to be focused when the dialog is displayed.
14813      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14814      * @return {Roo.BasicDialog} this
14815      */
14816     setDefaultButton : function(btn){
14817         this.defaultButton = btn;
14818         return this;
14819     },
14820
14821     // private
14822     getHeaderFooterHeight : function(safe){
14823         var height = 0;
14824         if(this.header){
14825            height += this.header.getHeight();
14826         }
14827         if(this.footer){
14828            var fm = this.footer.getMargins();
14829             height += (this.footer.getHeight()+fm.top+fm.bottom);
14830         }
14831         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14832         height += this.centerBg.getPadding("tb");
14833         return height;
14834     },
14835
14836     // private
14837     syncBodyHeight : function(){
14838         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14839         var height = this.size.height - this.getHeaderFooterHeight(false);
14840         bd.setHeight(height-bd.getMargins("tb"));
14841         var hh = this.header.getHeight();
14842         var h = this.size.height-hh;
14843         cb.setHeight(h);
14844         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14845         bw.setHeight(h-cb.getPadding("tb"));
14846         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14847         bd.setWidth(bw.getWidth(true));
14848         if(this.tabs){
14849             this.tabs.syncHeight();
14850             if(Roo.isIE){
14851                 this.tabs.el.repaint();
14852             }
14853         }
14854     },
14855
14856     /**
14857      * Restores the previous state of the dialog if Roo.state is configured.
14858      * @return {Roo.BasicDialog} this
14859      */
14860     restoreState : function(){
14861         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14862         if(box && box.width){
14863             this.xy = [box.x, box.y];
14864             this.resizeTo(box.width, box.height);
14865         }
14866         return this;
14867     },
14868
14869     // private
14870     beforeShow : function(){
14871         this.expand();
14872         if(this.fixedcenter){
14873             this.xy = this.el.getCenterXY(true);
14874         }
14875         if(this.modal){
14876             Roo.get(document.body).addClass("x-body-masked");
14877             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14878             this.mask.show();
14879         }
14880         this.constrainXY();
14881     },
14882
14883     // private
14884     animShow : function(){
14885         var b = Roo.get(this.animateTarget).getBox();
14886         this.proxy.setSize(b.width, b.height);
14887         this.proxy.setLocation(b.x, b.y);
14888         this.proxy.show();
14889         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14890                     true, .35, this.showEl.createDelegate(this));
14891     },
14892
14893     /**
14894      * Shows the dialog.
14895      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14896      * @return {Roo.BasicDialog} this
14897      */
14898     show : function(animateTarget){
14899         if (this.fireEvent("beforeshow", this) === false){
14900             return;
14901         }
14902         if(this.syncHeightBeforeShow){
14903             this.syncBodyHeight();
14904         }else if(this.firstShow){
14905             this.firstShow = false;
14906             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14907         }
14908         this.animateTarget = animateTarget || this.animateTarget;
14909         if(!this.el.isVisible()){
14910             this.beforeShow();
14911             if(this.animateTarget && Roo.get(this.animateTarget)){
14912                 this.animShow();
14913             }else{
14914                 this.showEl();
14915             }
14916         }
14917         return this;
14918     },
14919
14920     // private
14921     showEl : function(){
14922         this.proxy.hide();
14923         this.el.setXY(this.xy);
14924         this.el.show();
14925         this.adjustAssets(true);
14926         this.toFront();
14927         this.focus();
14928         // IE peekaboo bug - fix found by Dave Fenwick
14929         if(Roo.isIE){
14930             this.el.repaint();
14931         }
14932         this.fireEvent("show", this);
14933     },
14934
14935     /**
14936      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14937      * dialog itself will receive focus.
14938      */
14939     focus : function(){
14940         if(this.defaultButton){
14941             this.defaultButton.focus();
14942         }else{
14943             this.focusEl.focus();
14944         }
14945     },
14946
14947     // private
14948     constrainXY : function(){
14949         if(this.constraintoviewport !== false){
14950             if(!this.viewSize){
14951                 if(this.container){
14952                     var s = this.container.getSize();
14953                     this.viewSize = [s.width, s.height];
14954                 }else{
14955                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14956                 }
14957             }
14958             var s = Roo.get(this.container||document).getScroll();
14959
14960             var x = this.xy[0], y = this.xy[1];
14961             var w = this.size.width, h = this.size.height;
14962             var vw = this.viewSize[0], vh = this.viewSize[1];
14963             // only move it if it needs it
14964             var moved = false;
14965             // first validate right/bottom
14966             if(x + w > vw+s.left){
14967                 x = vw - w;
14968                 moved = true;
14969             }
14970             if(y + h > vh+s.top){
14971                 y = vh - h;
14972                 moved = true;
14973             }
14974             // then make sure top/left isn't negative
14975             if(x < s.left){
14976                 x = s.left;
14977                 moved = true;
14978             }
14979             if(y < s.top){
14980                 y = s.top;
14981                 moved = true;
14982             }
14983             if(moved){
14984                 // cache xy
14985                 this.xy = [x, y];
14986                 if(this.isVisible()){
14987                     this.el.setLocation(x, y);
14988                     this.adjustAssets();
14989                 }
14990             }
14991         }
14992     },
14993
14994     // private
14995     onDrag : function(){
14996         if(!this.proxyDrag){
14997             this.xy = this.el.getXY();
14998             this.adjustAssets();
14999         }
15000     },
15001
15002     // private
15003     adjustAssets : function(doShow){
15004         var x = this.xy[0], y = this.xy[1];
15005         var w = this.size.width, h = this.size.height;
15006         if(doShow === true){
15007             if(this.shadow){
15008                 this.shadow.show(this.el);
15009             }
15010             if(this.shim){
15011                 this.shim.show();
15012             }
15013         }
15014         if(this.shadow && this.shadow.isVisible()){
15015             this.shadow.show(this.el);
15016         }
15017         if(this.shim && this.shim.isVisible()){
15018             this.shim.setBounds(x, y, w, h);
15019         }
15020     },
15021
15022     // private
15023     adjustViewport : function(w, h){
15024         if(!w || !h){
15025             w = Roo.lib.Dom.getViewWidth();
15026             h = Roo.lib.Dom.getViewHeight();
15027         }
15028         // cache the size
15029         this.viewSize = [w, h];
15030         if(this.modal && this.mask.isVisible()){
15031             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15032             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15033         }
15034         if(this.isVisible()){
15035             this.constrainXY();
15036         }
15037     },
15038
15039     /**
15040      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15041      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15042      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15043      */
15044     destroy : function(removeEl){
15045         if(this.isVisible()){
15046             this.animateTarget = null;
15047             this.hide();
15048         }
15049         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15050         if(this.tabs){
15051             this.tabs.destroy(removeEl);
15052         }
15053         Roo.destroy(
15054              this.shim,
15055              this.proxy,
15056              this.resizer,
15057              this.close,
15058              this.mask
15059         );
15060         if(this.dd){
15061             this.dd.unreg();
15062         }
15063         if(this.buttons){
15064            for(var i = 0, len = this.buttons.length; i < len; i++){
15065                this.buttons[i].destroy();
15066            }
15067         }
15068         this.el.removeAllListeners();
15069         if(removeEl === true){
15070             this.el.update("");
15071             this.el.remove();
15072         }
15073         Roo.DialogManager.unregister(this);
15074     },
15075
15076     // private
15077     startMove : function(){
15078         if(this.proxyDrag){
15079             this.proxy.show();
15080         }
15081         if(this.constraintoviewport !== false){
15082             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15083         }
15084     },
15085
15086     // private
15087     endMove : function(){
15088         if(!this.proxyDrag){
15089             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15090         }else{
15091             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15092             this.proxy.hide();
15093         }
15094         this.refreshSize();
15095         this.adjustAssets();
15096         this.focus();
15097         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15098     },
15099
15100     /**
15101      * Brings this dialog to the front of any other visible dialogs
15102      * @return {Roo.BasicDialog} this
15103      */
15104     toFront : function(){
15105         Roo.DialogManager.bringToFront(this);
15106         return this;
15107     },
15108
15109     /**
15110      * Sends this dialog to the back (under) of any other visible dialogs
15111      * @return {Roo.BasicDialog} this
15112      */
15113     toBack : function(){
15114         Roo.DialogManager.sendToBack(this);
15115         return this;
15116     },
15117
15118     /**
15119      * Centers this dialog in the viewport
15120      * @return {Roo.BasicDialog} this
15121      */
15122     center : function(){
15123         var xy = this.el.getCenterXY(true);
15124         this.moveTo(xy[0], xy[1]);
15125         return this;
15126     },
15127
15128     /**
15129      * Moves the dialog's top-left corner to the specified point
15130      * @param {Number} x
15131      * @param {Number} y
15132      * @return {Roo.BasicDialog} this
15133      */
15134     moveTo : function(x, y){
15135         this.xy = [x,y];
15136         if(this.isVisible()){
15137             this.el.setXY(this.xy);
15138             this.adjustAssets();
15139         }
15140         return this;
15141     },
15142
15143     /**
15144      * Aligns the dialog to the specified element
15145      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15146      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15147      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15148      * @return {Roo.BasicDialog} this
15149      */
15150     alignTo : function(element, position, offsets){
15151         this.xy = this.el.getAlignToXY(element, position, offsets);
15152         if(this.isVisible()){
15153             this.el.setXY(this.xy);
15154             this.adjustAssets();
15155         }
15156         return this;
15157     },
15158
15159     /**
15160      * Anchors an element to another element and realigns it when the window is resized.
15161      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15162      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15163      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15164      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15165      * is a number, it is used as the buffer delay (defaults to 50ms).
15166      * @return {Roo.BasicDialog} this
15167      */
15168     anchorTo : function(el, alignment, offsets, monitorScroll){
15169         var action = function(){
15170             this.alignTo(el, alignment, offsets);
15171         };
15172         Roo.EventManager.onWindowResize(action, this);
15173         var tm = typeof monitorScroll;
15174         if(tm != 'undefined'){
15175             Roo.EventManager.on(window, 'scroll', action, this,
15176                 {buffer: tm == 'number' ? monitorScroll : 50});
15177         }
15178         action.call(this);
15179         return this;
15180     },
15181
15182     /**
15183      * Returns true if the dialog is visible
15184      * @return {Boolean}
15185      */
15186     isVisible : function(){
15187         return this.el.isVisible();
15188     },
15189
15190     // private
15191     animHide : function(callback){
15192         var b = Roo.get(this.animateTarget).getBox();
15193         this.proxy.show();
15194         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15195         this.el.hide();
15196         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15197                     this.hideEl.createDelegate(this, [callback]));
15198     },
15199
15200     /**
15201      * Hides the dialog.
15202      * @param {Function} callback (optional) Function to call when the dialog is hidden
15203      * @return {Roo.BasicDialog} this
15204      */
15205     hide : function(callback){
15206         if (this.fireEvent("beforehide", this) === false){
15207             return;
15208         }
15209         if(this.shadow){
15210             this.shadow.hide();
15211         }
15212         if(this.shim) {
15213           this.shim.hide();
15214         }
15215         // sometimes animateTarget seems to get set.. causing problems...
15216         // this just double checks..
15217         if(this.animateTarget && Roo.get(this.animateTarget)) {
15218            this.animHide(callback);
15219         }else{
15220             this.el.hide();
15221             this.hideEl(callback);
15222         }
15223         return this;
15224     },
15225
15226     // private
15227     hideEl : function(callback){
15228         this.proxy.hide();
15229         if(this.modal){
15230             this.mask.hide();
15231             Roo.get(document.body).removeClass("x-body-masked");
15232         }
15233         this.fireEvent("hide", this);
15234         if(typeof callback == "function"){
15235             callback();
15236         }
15237     },
15238
15239     // private
15240     hideAction : function(){
15241         this.setLeft("-10000px");
15242         this.setTop("-10000px");
15243         this.setStyle("visibility", "hidden");
15244     },
15245
15246     // private
15247     refreshSize : function(){
15248         this.size = this.el.getSize();
15249         this.xy = this.el.getXY();
15250         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15251     },
15252
15253     // private
15254     // z-index is managed by the DialogManager and may be overwritten at any time
15255     setZIndex : function(index){
15256         if(this.modal){
15257             this.mask.setStyle("z-index", index);
15258         }
15259         if(this.shim){
15260             this.shim.setStyle("z-index", ++index);
15261         }
15262         if(this.shadow){
15263             this.shadow.setZIndex(++index);
15264         }
15265         this.el.setStyle("z-index", ++index);
15266         if(this.proxy){
15267             this.proxy.setStyle("z-index", ++index);
15268         }
15269         if(this.resizer){
15270             this.resizer.proxy.setStyle("z-index", ++index);
15271         }
15272
15273         this.lastZIndex = index;
15274     },
15275
15276     /**
15277      * Returns the element for this dialog
15278      * @return {Roo.Element} The underlying dialog Element
15279      */
15280     getEl : function(){
15281         return this.el;
15282     }
15283 });
15284
15285 /**
15286  * @class Roo.DialogManager
15287  * Provides global access to BasicDialogs that have been created and
15288  * support for z-indexing (layering) multiple open dialogs.
15289  */
15290 Roo.DialogManager = function(){
15291     var list = {};
15292     var accessList = [];
15293     var front = null;
15294
15295     // private
15296     var sortDialogs = function(d1, d2){
15297         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15298     };
15299
15300     // private
15301     var orderDialogs = function(){
15302         accessList.sort(sortDialogs);
15303         var seed = Roo.DialogManager.zseed;
15304         for(var i = 0, len = accessList.length; i < len; i++){
15305             var dlg = accessList[i];
15306             if(dlg){
15307                 dlg.setZIndex(seed + (i*10));
15308             }
15309         }
15310     };
15311
15312     return {
15313         /**
15314          * The starting z-index for BasicDialogs (defaults to 9000)
15315          * @type Number The z-index value
15316          */
15317         zseed : 9000,
15318
15319         // private
15320         register : function(dlg){
15321             list[dlg.id] = dlg;
15322             accessList.push(dlg);
15323         },
15324
15325         // private
15326         unregister : function(dlg){
15327             delete list[dlg.id];
15328             var i=0;
15329             var len=0;
15330             if(!accessList.indexOf){
15331                 for(  i = 0, len = accessList.length; i < len; i++){
15332                     if(accessList[i] == dlg){
15333                         accessList.splice(i, 1);
15334                         return;
15335                     }
15336                 }
15337             }else{
15338                  i = accessList.indexOf(dlg);
15339                 if(i != -1){
15340                     accessList.splice(i, 1);
15341                 }
15342             }
15343         },
15344
15345         /**
15346          * Gets a registered dialog by id
15347          * @param {String/Object} id The id of the dialog or a dialog
15348          * @return {Roo.BasicDialog} this
15349          */
15350         get : function(id){
15351             return typeof id == "object" ? id : list[id];
15352         },
15353
15354         /**
15355          * Brings the specified dialog to the front
15356          * @param {String/Object} dlg The id of the dialog or a dialog
15357          * @return {Roo.BasicDialog} this
15358          */
15359         bringToFront : function(dlg){
15360             dlg = this.get(dlg);
15361             if(dlg != front){
15362                 front = dlg;
15363                 dlg._lastAccess = new Date().getTime();
15364                 orderDialogs();
15365             }
15366             return dlg;
15367         },
15368
15369         /**
15370          * Sends the specified dialog to the back
15371          * @param {String/Object} dlg The id of the dialog or a dialog
15372          * @return {Roo.BasicDialog} this
15373          */
15374         sendToBack : function(dlg){
15375             dlg = this.get(dlg);
15376             dlg._lastAccess = -(new Date().getTime());
15377             orderDialogs();
15378             return dlg;
15379         },
15380
15381         /**
15382          * Hides all dialogs
15383          */
15384         hideAll : function(){
15385             for(var id in list){
15386                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15387                     list[id].hide();
15388                 }
15389             }
15390         }
15391     };
15392 }();
15393
15394 /**
15395  * @class Roo.LayoutDialog
15396  * @extends Roo.BasicDialog
15397  * Dialog which provides adjustments for working with a layout in a Dialog.
15398  * Add your necessary layout config options to the dialog's config.<br>
15399  * Example usage (including a nested layout):
15400  * <pre><code>
15401 if(!dialog){
15402     dialog = new Roo.LayoutDialog("download-dlg", {
15403         modal: true,
15404         width:600,
15405         height:450,
15406         shadow:true,
15407         minWidth:500,
15408         minHeight:350,
15409         autoTabs:true,
15410         proxyDrag:true,
15411         // layout config merges with the dialog config
15412         center:{
15413             tabPosition: "top",
15414             alwaysShowTabs: true
15415         }
15416     });
15417     dialog.addKeyListener(27, dialog.hide, dialog);
15418     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15419     dialog.addButton("Build It!", this.getDownload, this);
15420
15421     // we can even add nested layouts
15422     var innerLayout = new Roo.BorderLayout("dl-inner", {
15423         east: {
15424             initialSize: 200,
15425             autoScroll:true,
15426             split:true
15427         },
15428         center: {
15429             autoScroll:true
15430         }
15431     });
15432     innerLayout.beginUpdate();
15433     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15434     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15435     innerLayout.endUpdate(true);
15436
15437     var layout = dialog.getLayout();
15438     layout.beginUpdate();
15439     layout.add("center", new Roo.ContentPanel("standard-panel",
15440                         {title: "Download the Source", fitToFrame:true}));
15441     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15442                {title: "Build your own roo.js"}));
15443     layout.getRegion("center").showPanel(sp);
15444     layout.endUpdate();
15445 }
15446 </code></pre>
15447     * @constructor
15448     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15449     * @param {Object} config configuration options
15450   */
15451 Roo.LayoutDialog = function(el, cfg){
15452     
15453     var config=  cfg;
15454     if (typeof(cfg) == 'undefined') {
15455         config = Roo.apply({}, el);
15456         // not sure why we use documentElement here.. - it should always be body.
15457         // IE7 borks horribly if we use documentElement.
15458         // webkit also does not like documentElement - it creates a body element...
15459         el = Roo.get( document.body || document.documentElement ).createChild();
15460         //config.autoCreate = true;
15461     }
15462     
15463     
15464     config.autoTabs = false;
15465     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15466     this.body.setStyle({overflow:"hidden", position:"relative"});
15467     this.layout = new Roo.BorderLayout(this.body.dom, config);
15468     this.layout.monitorWindowResize = false;
15469     this.el.addClass("x-dlg-auto-layout");
15470     // fix case when center region overwrites center function
15471     this.center = Roo.BasicDialog.prototype.center;
15472     this.on("show", this.layout.layout, this.layout, true);
15473     if (config.items) {
15474         var xitems = config.items;
15475         delete config.items;
15476         Roo.each(xitems, this.addxtype, this);
15477     }
15478     
15479     
15480 };
15481 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15482     /**
15483      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15484      * @deprecated
15485      */
15486     endUpdate : function(){
15487         this.layout.endUpdate();
15488     },
15489
15490     /**
15491      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15492      *  @deprecated
15493      */
15494     beginUpdate : function(){
15495         this.layout.beginUpdate();
15496     },
15497
15498     /**
15499      * Get the BorderLayout for this dialog
15500      * @return {Roo.BorderLayout}
15501      */
15502     getLayout : function(){
15503         return this.layout;
15504     },
15505
15506     showEl : function(){
15507         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15508         if(Roo.isIE7){
15509             this.layout.layout();
15510         }
15511     },
15512
15513     // private
15514     // Use the syncHeightBeforeShow config option to control this automatically
15515     syncBodyHeight : function(){
15516         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15517         if(this.layout){this.layout.layout();}
15518     },
15519     
15520       /**
15521      * Add an xtype element (actually adds to the layout.)
15522      * @return {Object} xdata xtype object data.
15523      */
15524     
15525     addxtype : function(c) {
15526         return this.layout.addxtype(c);
15527     }
15528 });/*
15529  * Based on:
15530  * Ext JS Library 1.1.1
15531  * Copyright(c) 2006-2007, Ext JS, LLC.
15532  *
15533  * Originally Released Under LGPL - original licence link has changed is not relivant.
15534  *
15535  * Fork - LGPL
15536  * <script type="text/javascript">
15537  */
15538  
15539 /**
15540  * @class Roo.MessageBox
15541  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15542  * Example usage:
15543  *<pre><code>
15544 // Basic alert:
15545 Roo.Msg.alert('Status', 'Changes saved successfully.');
15546
15547 // Prompt for user data:
15548 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15549     if (btn == 'ok'){
15550         // process text value...
15551     }
15552 });
15553
15554 // Show a dialog using config options:
15555 Roo.Msg.show({
15556    title:'Save Changes?',
15557    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15558    buttons: Roo.Msg.YESNOCANCEL,
15559    fn: processResult,
15560    animEl: 'elId'
15561 });
15562 </code></pre>
15563  * @singleton
15564  */
15565 Roo.MessageBox = function(){
15566     var dlg, opt, mask, waitTimer;
15567     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15568     var buttons, activeTextEl, bwidth;
15569
15570     // private
15571     var handleButton = function(button){
15572         dlg.hide();
15573         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15574     };
15575
15576     // private
15577     var handleHide = function(){
15578         if(opt && opt.cls){
15579             dlg.el.removeClass(opt.cls);
15580         }
15581         if(waitTimer){
15582             Roo.TaskMgr.stop(waitTimer);
15583             waitTimer = null;
15584         }
15585     };
15586
15587     // private
15588     var updateButtons = function(b){
15589         var width = 0;
15590         if(!b){
15591             buttons["ok"].hide();
15592             buttons["cancel"].hide();
15593             buttons["yes"].hide();
15594             buttons["no"].hide();
15595             dlg.footer.dom.style.display = 'none';
15596             return width;
15597         }
15598         dlg.footer.dom.style.display = '';
15599         for(var k in buttons){
15600             if(typeof buttons[k] != "function"){
15601                 if(b[k]){
15602                     buttons[k].show();
15603                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15604                     width += buttons[k].el.getWidth()+15;
15605                 }else{
15606                     buttons[k].hide();
15607                 }
15608             }
15609         }
15610         return width;
15611     };
15612
15613     // private
15614     var handleEsc = function(d, k, e){
15615         if(opt && opt.closable !== false){
15616             dlg.hide();
15617         }
15618         if(e){
15619             e.stopEvent();
15620         }
15621     };
15622
15623     return {
15624         /**
15625          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15626          * @return {Roo.BasicDialog} The BasicDialog element
15627          */
15628         getDialog : function(){
15629            if(!dlg){
15630                 dlg = new Roo.BasicDialog("x-msg-box", {
15631                     autoCreate : true,
15632                     shadow: true,
15633                     draggable: true,
15634                     resizable:false,
15635                     constraintoviewport:false,
15636                     fixedcenter:true,
15637                     collapsible : false,
15638                     shim:true,
15639                     modal: true,
15640                     width:400, height:100,
15641                     buttonAlign:"center",
15642                     closeClick : function(){
15643                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15644                             handleButton("no");
15645                         }else{
15646                             handleButton("cancel");
15647                         }
15648                     }
15649                 });
15650                 dlg.on("hide", handleHide);
15651                 mask = dlg.mask;
15652                 dlg.addKeyListener(27, handleEsc);
15653                 buttons = {};
15654                 var bt = this.buttonText;
15655                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15656                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15657                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15658                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15659                 bodyEl = dlg.body.createChild({
15660
15661                     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>'
15662                 });
15663                 msgEl = bodyEl.dom.firstChild;
15664                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15665                 textboxEl.enableDisplayMode();
15666                 textboxEl.addKeyListener([10,13], function(){
15667                     if(dlg.isVisible() && opt && opt.buttons){
15668                         if(opt.buttons.ok){
15669                             handleButton("ok");
15670                         }else if(opt.buttons.yes){
15671                             handleButton("yes");
15672                         }
15673                     }
15674                 });
15675                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15676                 textareaEl.enableDisplayMode();
15677                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15678                 progressEl.enableDisplayMode();
15679                 var pf = progressEl.dom.firstChild;
15680                 if (pf) {
15681                     pp = Roo.get(pf.firstChild);
15682                     pp.setHeight(pf.offsetHeight);
15683                 }
15684                 
15685             }
15686             return dlg;
15687         },
15688
15689         /**
15690          * Updates the message box body text
15691          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15692          * the XHTML-compliant non-breaking space character '&amp;#160;')
15693          * @return {Roo.MessageBox} This message box
15694          */
15695         updateText : function(text){
15696             if(!dlg.isVisible() && !opt.width){
15697                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15698             }
15699             msgEl.innerHTML = text || '&#160;';
15700       
15701             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15702             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15703             var w = Math.max(
15704                     Math.min(opt.width || cw , this.maxWidth), 
15705                     Math.max(opt.minWidth || this.minWidth, bwidth)
15706             );
15707             if(opt.prompt){
15708                 activeTextEl.setWidth(w);
15709             }
15710             if(dlg.isVisible()){
15711                 dlg.fixedcenter = false;
15712             }
15713             // to big, make it scroll. = But as usual stupid IE does not support
15714             // !important..
15715             
15716             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15717                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15718                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15719             } else {
15720                 bodyEl.dom.style.height = '';
15721                 bodyEl.dom.style.overflowY = '';
15722             }
15723             if (cw > w) {
15724                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15725             } else {
15726                 bodyEl.dom.style.overflowX = '';
15727             }
15728             
15729             dlg.setContentSize(w, bodyEl.getHeight());
15730             if(dlg.isVisible()){
15731                 dlg.fixedcenter = true;
15732             }
15733             return this;
15734         },
15735
15736         /**
15737          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15738          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15739          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15740          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15741          * @return {Roo.MessageBox} This message box
15742          */
15743         updateProgress : function(value, text){
15744             if(text){
15745                 this.updateText(text);
15746             }
15747             if (pp) { // weird bug on my firefox - for some reason this is not defined
15748                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15749             }
15750             return this;
15751         },        
15752
15753         /**
15754          * Returns true if the message box is currently displayed
15755          * @return {Boolean} True if the message box is visible, else false
15756          */
15757         isVisible : function(){
15758             return dlg && dlg.isVisible();  
15759         },
15760
15761         /**
15762          * Hides the message box if it is displayed
15763          */
15764         hide : function(){
15765             if(this.isVisible()){
15766                 dlg.hide();
15767             }  
15768         },
15769
15770         /**
15771          * Displays a new message box, or reinitializes an existing message box, based on the config options
15772          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15773          * The following config object properties are supported:
15774          * <pre>
15775 Property    Type             Description
15776 ----------  ---------------  ------------------------------------------------------------------------------------
15777 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15778                                    closes (defaults to undefined)
15779 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15780                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15781 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15782                                    progress and wait dialogs will ignore this property and always hide the
15783                                    close button as they can only be closed programmatically.
15784 cls               String           A custom CSS class to apply to the message box element
15785 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15786                                    displayed (defaults to 75)
15787 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15788                                    function will be btn (the name of the button that was clicked, if applicable,
15789                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15790                                    Progress and wait dialogs will ignore this option since they do not respond to
15791                                    user actions and can only be closed programmatically, so any required function
15792                                    should be called by the same code after it closes the dialog.
15793 icon              String           A CSS class that provides a background image to be used as an icon for
15794                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15795 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15796 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15797 modal             Boolean          False to allow user interaction with the page while the message box is
15798                                    displayed (defaults to true)
15799 msg               String           A string that will replace the existing message box body text (defaults
15800                                    to the XHTML-compliant non-breaking space character '&#160;')
15801 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15802 progress          Boolean          True to display a progress bar (defaults to false)
15803 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15804 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15805 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15806 title             String           The title text
15807 value             String           The string value to set into the active textbox element if displayed
15808 wait              Boolean          True to display a progress bar (defaults to false)
15809 width             Number           The width of the dialog in pixels
15810 </pre>
15811          *
15812          * Example usage:
15813          * <pre><code>
15814 Roo.Msg.show({
15815    title: 'Address',
15816    msg: 'Please enter your address:',
15817    width: 300,
15818    buttons: Roo.MessageBox.OKCANCEL,
15819    multiline: true,
15820    fn: saveAddress,
15821    animEl: 'addAddressBtn'
15822 });
15823 </code></pre>
15824          * @param {Object} config Configuration options
15825          * @return {Roo.MessageBox} This message box
15826          */
15827         show : function(options)
15828         {
15829             
15830             // this causes nightmares if you show one dialog after another
15831             // especially on callbacks..
15832              
15833             if(this.isVisible()){
15834                 
15835                 this.hide();
15836                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15837                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15838                 Roo.log("New Dialog Message:" +  options.msg )
15839                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15840                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15841                 
15842             }
15843             var d = this.getDialog();
15844             opt = options;
15845             d.setTitle(opt.title || "&#160;");
15846             d.close.setDisplayed(opt.closable !== false);
15847             activeTextEl = textboxEl;
15848             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15849             if(opt.prompt){
15850                 if(opt.multiline){
15851                     textboxEl.hide();
15852                     textareaEl.show();
15853                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15854                         opt.multiline : this.defaultTextHeight);
15855                     activeTextEl = textareaEl;
15856                 }else{
15857                     textboxEl.show();
15858                     textareaEl.hide();
15859                 }
15860             }else{
15861                 textboxEl.hide();
15862                 textareaEl.hide();
15863             }
15864             progressEl.setDisplayed(opt.progress === true);
15865             this.updateProgress(0);
15866             activeTextEl.dom.value = opt.value || "";
15867             if(opt.prompt){
15868                 dlg.setDefaultButton(activeTextEl);
15869             }else{
15870                 var bs = opt.buttons;
15871                 var db = null;
15872                 if(bs && bs.ok){
15873                     db = buttons["ok"];
15874                 }else if(bs && bs.yes){
15875                     db = buttons["yes"];
15876                 }
15877                 dlg.setDefaultButton(db);
15878             }
15879             bwidth = updateButtons(opt.buttons);
15880             this.updateText(opt.msg);
15881             if(opt.cls){
15882                 d.el.addClass(opt.cls);
15883             }
15884             d.proxyDrag = opt.proxyDrag === true;
15885             d.modal = opt.modal !== false;
15886             d.mask = opt.modal !== false ? mask : false;
15887             if(!d.isVisible()){
15888                 // force it to the end of the z-index stack so it gets a cursor in FF
15889                 document.body.appendChild(dlg.el.dom);
15890                 d.animateTarget = null;
15891                 d.show(options.animEl);
15892             }
15893             return this;
15894         },
15895
15896         /**
15897          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15898          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15899          * and closing the message box when the process is complete.
15900          * @param {String} title The title bar text
15901          * @param {String} msg The message box body text
15902          * @return {Roo.MessageBox} This message box
15903          */
15904         progress : function(title, msg){
15905             this.show({
15906                 title : title,
15907                 msg : msg,
15908                 buttons: false,
15909                 progress:true,
15910                 closable:false,
15911                 minWidth: this.minProgressWidth,
15912                 modal : true
15913             });
15914             return this;
15915         },
15916
15917         /**
15918          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15919          * If a callback function is passed it will be called after the user clicks the button, and the
15920          * id of the button that was clicked will be passed as the only parameter to the callback
15921          * (could also be the top-right close button).
15922          * @param {String} title The title bar text
15923          * @param {String} msg The message box body text
15924          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15925          * @param {Object} scope (optional) The scope of the callback function
15926          * @return {Roo.MessageBox} This message box
15927          */
15928         alert : function(title, msg, fn, scope){
15929             this.show({
15930                 title : title,
15931                 msg : msg,
15932                 buttons: this.OK,
15933                 fn: fn,
15934                 scope : scope,
15935                 modal : true
15936             });
15937             return this;
15938         },
15939
15940         /**
15941          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15942          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15943          * You are responsible for closing the message box when the process is complete.
15944          * @param {String} msg The message box body text
15945          * @param {String} title (optional) The title bar text
15946          * @return {Roo.MessageBox} This message box
15947          */
15948         wait : function(msg, title){
15949             this.show({
15950                 title : title,
15951                 msg : msg,
15952                 buttons: false,
15953                 closable:false,
15954                 progress:true,
15955                 modal:true,
15956                 width:300,
15957                 wait:true
15958             });
15959             waitTimer = Roo.TaskMgr.start({
15960                 run: function(i){
15961                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15962                 },
15963                 interval: 1000
15964             });
15965             return this;
15966         },
15967
15968         /**
15969          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15970          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15971          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15972          * @param {String} title The title bar text
15973          * @param {String} msg The message box body text
15974          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15975          * @param {Object} scope (optional) The scope of the callback function
15976          * @return {Roo.MessageBox} This message box
15977          */
15978         confirm : function(title, msg, fn, scope){
15979             this.show({
15980                 title : title,
15981                 msg : msg,
15982                 buttons: this.YESNO,
15983                 fn: fn,
15984                 scope : scope,
15985                 modal : true
15986             });
15987             return this;
15988         },
15989
15990         /**
15991          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15992          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15993          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15994          * (could also be the top-right close button) and the text that was entered will be passed as the two
15995          * parameters to the callback.
15996          * @param {String} title The title bar text
15997          * @param {String} msg The message box body text
15998          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15999          * @param {Object} scope (optional) The scope of the callback function
16000          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16001          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16002          * @return {Roo.MessageBox} This message box
16003          */
16004         prompt : function(title, msg, fn, scope, multiline){
16005             this.show({
16006                 title : title,
16007                 msg : msg,
16008                 buttons: this.OKCANCEL,
16009                 fn: fn,
16010                 minWidth:250,
16011                 scope : scope,
16012                 prompt:true,
16013                 multiline: multiline,
16014                 modal : true
16015             });
16016             return this;
16017         },
16018
16019         /**
16020          * Button config that displays a single OK button
16021          * @type Object
16022          */
16023         OK : {ok:true},
16024         /**
16025          * Button config that displays Yes and No buttons
16026          * @type Object
16027          */
16028         YESNO : {yes:true, no:true},
16029         /**
16030          * Button config that displays OK and Cancel buttons
16031          * @type Object
16032          */
16033         OKCANCEL : {ok:true, cancel:true},
16034         /**
16035          * Button config that displays Yes, No and Cancel buttons
16036          * @type Object
16037          */
16038         YESNOCANCEL : {yes:true, no:true, cancel:true},
16039
16040         /**
16041          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16042          * @type Number
16043          */
16044         defaultTextHeight : 75,
16045         /**
16046          * The maximum width in pixels of the message box (defaults to 600)
16047          * @type Number
16048          */
16049         maxWidth : 600,
16050         /**
16051          * The minimum width in pixels of the message box (defaults to 100)
16052          * @type Number
16053          */
16054         minWidth : 100,
16055         /**
16056          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16057          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16058          * @type Number
16059          */
16060         minProgressWidth : 250,
16061         /**
16062          * An object containing the default button text strings that can be overriden for localized language support.
16063          * Supported properties are: ok, cancel, yes and no.
16064          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16065          * @type Object
16066          */
16067         buttonText : {
16068             ok : "OK",
16069             cancel : "Cancel",
16070             yes : "Yes",
16071             no : "No"
16072         }
16073     };
16074 }();
16075
16076 /**
16077  * Shorthand for {@link Roo.MessageBox}
16078  */
16079 Roo.Msg = Roo.MessageBox;/*
16080  * Based on:
16081  * Ext JS Library 1.1.1
16082  * Copyright(c) 2006-2007, Ext JS, LLC.
16083  *
16084  * Originally Released Under LGPL - original licence link has changed is not relivant.
16085  *
16086  * Fork - LGPL
16087  * <script type="text/javascript">
16088  */
16089 /**
16090  * @class Roo.QuickTips
16091  * Provides attractive and customizable tooltips for any element.
16092  * @singleton
16093  */
16094 Roo.QuickTips = function(){
16095     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16096     var ce, bd, xy, dd;
16097     var visible = false, disabled = true, inited = false;
16098     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16099     
16100     var onOver = function(e){
16101         if(disabled){
16102             return;
16103         }
16104         var t = e.getTarget();
16105         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16106             return;
16107         }
16108         if(ce && t == ce.el){
16109             clearTimeout(hideProc);
16110             return;
16111         }
16112         if(t && tagEls[t.id]){
16113             tagEls[t.id].el = t;
16114             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16115             return;
16116         }
16117         var ttp, et = Roo.fly(t);
16118         var ns = cfg.namespace;
16119         if(tm.interceptTitles && t.title){
16120             ttp = t.title;
16121             t.qtip = ttp;
16122             t.removeAttribute("title");
16123             e.preventDefault();
16124         }else{
16125             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16126         }
16127         if(ttp){
16128             showProc = show.defer(tm.showDelay, tm, [{
16129                 el: t, 
16130                 text: ttp, 
16131                 width: et.getAttributeNS(ns, cfg.width),
16132                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16133                 title: et.getAttributeNS(ns, cfg.title),
16134                     cls: et.getAttributeNS(ns, cfg.cls)
16135             }]);
16136         }
16137     };
16138     
16139     var onOut = function(e){
16140         clearTimeout(showProc);
16141         var t = e.getTarget();
16142         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16143             hideProc = setTimeout(hide, tm.hideDelay);
16144         }
16145     };
16146     
16147     var onMove = function(e){
16148         if(disabled){
16149             return;
16150         }
16151         xy = e.getXY();
16152         xy[1] += 18;
16153         if(tm.trackMouse && ce){
16154             el.setXY(xy);
16155         }
16156     };
16157     
16158     var onDown = function(e){
16159         clearTimeout(showProc);
16160         clearTimeout(hideProc);
16161         if(!e.within(el)){
16162             if(tm.hideOnClick){
16163                 hide();
16164                 tm.disable();
16165                 tm.enable.defer(100, tm);
16166             }
16167         }
16168     };
16169     
16170     var getPad = function(){
16171         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16172     };
16173
16174     var show = function(o){
16175         if(disabled){
16176             return;
16177         }
16178         clearTimeout(dismissProc);
16179         ce = o;
16180         if(removeCls){ // in case manually hidden
16181             el.removeClass(removeCls);
16182             removeCls = null;
16183         }
16184         if(ce.cls){
16185             el.addClass(ce.cls);
16186             removeCls = ce.cls;
16187         }
16188         if(ce.title){
16189             tipTitle.update(ce.title);
16190             tipTitle.show();
16191         }else{
16192             tipTitle.update('');
16193             tipTitle.hide();
16194         }
16195         el.dom.style.width  = tm.maxWidth+'px';
16196         //tipBody.dom.style.width = '';
16197         tipBodyText.update(o.text);
16198         var p = getPad(), w = ce.width;
16199         if(!w){
16200             var td = tipBodyText.dom;
16201             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16202             if(aw > tm.maxWidth){
16203                 w = tm.maxWidth;
16204             }else if(aw < tm.minWidth){
16205                 w = tm.minWidth;
16206             }else{
16207                 w = aw;
16208             }
16209         }
16210         //tipBody.setWidth(w);
16211         el.setWidth(parseInt(w, 10) + p);
16212         if(ce.autoHide === false){
16213             close.setDisplayed(true);
16214             if(dd){
16215                 dd.unlock();
16216             }
16217         }else{
16218             close.setDisplayed(false);
16219             if(dd){
16220                 dd.lock();
16221             }
16222         }
16223         if(xy){
16224             el.avoidY = xy[1]-18;
16225             el.setXY(xy);
16226         }
16227         if(tm.animate){
16228             el.setOpacity(.1);
16229             el.setStyle("visibility", "visible");
16230             el.fadeIn({callback: afterShow});
16231         }else{
16232             afterShow();
16233         }
16234     };
16235     
16236     var afterShow = function(){
16237         if(ce){
16238             el.show();
16239             esc.enable();
16240             if(tm.autoDismiss && ce.autoHide !== false){
16241                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16242             }
16243         }
16244     };
16245     
16246     var hide = function(noanim){
16247         clearTimeout(dismissProc);
16248         clearTimeout(hideProc);
16249         ce = null;
16250         if(el.isVisible()){
16251             esc.disable();
16252             if(noanim !== true && tm.animate){
16253                 el.fadeOut({callback: afterHide});
16254             }else{
16255                 afterHide();
16256             } 
16257         }
16258     };
16259     
16260     var afterHide = function(){
16261         el.hide();
16262         if(removeCls){
16263             el.removeClass(removeCls);
16264             removeCls = null;
16265         }
16266     };
16267     
16268     return {
16269         /**
16270         * @cfg {Number} minWidth
16271         * The minimum width of the quick tip (defaults to 40)
16272         */
16273        minWidth : 40,
16274         /**
16275         * @cfg {Number} maxWidth
16276         * The maximum width of the quick tip (defaults to 300)
16277         */
16278        maxWidth : 300,
16279         /**
16280         * @cfg {Boolean} interceptTitles
16281         * True to automatically use the element's DOM title value if available (defaults to false)
16282         */
16283        interceptTitles : false,
16284         /**
16285         * @cfg {Boolean} trackMouse
16286         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16287         */
16288        trackMouse : false,
16289         /**
16290         * @cfg {Boolean} hideOnClick
16291         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16292         */
16293        hideOnClick : true,
16294         /**
16295         * @cfg {Number} showDelay
16296         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16297         */
16298        showDelay : 500,
16299         /**
16300         * @cfg {Number} hideDelay
16301         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16302         */
16303        hideDelay : 200,
16304         /**
16305         * @cfg {Boolean} autoHide
16306         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16307         * Used in conjunction with hideDelay.
16308         */
16309        autoHide : true,
16310         /**
16311         * @cfg {Boolean}
16312         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16313         * (defaults to true).  Used in conjunction with autoDismissDelay.
16314         */
16315        autoDismiss : true,
16316         /**
16317         * @cfg {Number}
16318         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16319         */
16320        autoDismissDelay : 5000,
16321        /**
16322         * @cfg {Boolean} animate
16323         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16324         */
16325        animate : false,
16326
16327        /**
16328         * @cfg {String} title
16329         * Title text to display (defaults to '').  This can be any valid HTML markup.
16330         */
16331         title: '',
16332        /**
16333         * @cfg {String} text
16334         * Body text to display (defaults to '').  This can be any valid HTML markup.
16335         */
16336         text : '',
16337        /**
16338         * @cfg {String} cls
16339         * A CSS class to apply to the base quick tip element (defaults to '').
16340         */
16341         cls : '',
16342        /**
16343         * @cfg {Number} width
16344         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16345         * minWidth or maxWidth.
16346         */
16347         width : null,
16348
16349     /**
16350      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16351      * or display QuickTips in a page.
16352      */
16353        init : function(){
16354           tm = Roo.QuickTips;
16355           cfg = tm.tagConfig;
16356           if(!inited){
16357               if(!Roo.isReady){ // allow calling of init() before onReady
16358                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16359                   return;
16360               }
16361               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16362               el.fxDefaults = {stopFx: true};
16363               // maximum custom styling
16364               //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>');
16365               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>');              
16366               tipTitle = el.child('h3');
16367               tipTitle.enableDisplayMode("block");
16368               tipBody = el.child('div.x-tip-bd');
16369               tipBodyText = el.child('div.x-tip-bd-inner');
16370               //bdLeft = el.child('div.x-tip-bd-left');
16371               //bdRight = el.child('div.x-tip-bd-right');
16372               close = el.child('div.x-tip-close');
16373               close.enableDisplayMode("block");
16374               close.on("click", hide);
16375               var d = Roo.get(document);
16376               d.on("mousedown", onDown);
16377               d.on("mouseover", onOver);
16378               d.on("mouseout", onOut);
16379               d.on("mousemove", onMove);
16380               esc = d.addKeyListener(27, hide);
16381               esc.disable();
16382               if(Roo.dd.DD){
16383                   dd = el.initDD("default", null, {
16384                       onDrag : function(){
16385                           el.sync();  
16386                       }
16387                   });
16388                   dd.setHandleElId(tipTitle.id);
16389                   dd.lock();
16390               }
16391               inited = true;
16392           }
16393           this.enable(); 
16394        },
16395
16396     /**
16397      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16398      * are supported:
16399      * <pre>
16400 Property    Type                   Description
16401 ----------  ---------------------  ------------------------------------------------------------------------
16402 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16403      * </ul>
16404      * @param {Object} config The config object
16405      */
16406        register : function(config){
16407            var cs = config instanceof Array ? config : arguments;
16408            for(var i = 0, len = cs.length; i < len; i++) {
16409                var c = cs[i];
16410                var target = c.target;
16411                if(target){
16412                    if(target instanceof Array){
16413                        for(var j = 0, jlen = target.length; j < jlen; j++){
16414                            tagEls[target[j]] = c;
16415                        }
16416                    }else{
16417                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16418                    }
16419                }
16420            }
16421        },
16422
16423     /**
16424      * Removes this quick tip from its element and destroys it.
16425      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16426      */
16427        unregister : function(el){
16428            delete tagEls[Roo.id(el)];
16429        },
16430
16431     /**
16432      * Enable this quick tip.
16433      */
16434        enable : function(){
16435            if(inited && disabled){
16436                locks.pop();
16437                if(locks.length < 1){
16438                    disabled = false;
16439                }
16440            }
16441        },
16442
16443     /**
16444      * Disable this quick tip.
16445      */
16446        disable : function(){
16447           disabled = true;
16448           clearTimeout(showProc);
16449           clearTimeout(hideProc);
16450           clearTimeout(dismissProc);
16451           if(ce){
16452               hide(true);
16453           }
16454           locks.push(1);
16455        },
16456
16457     /**
16458      * Returns true if the quick tip is enabled, else false.
16459      */
16460        isEnabled : function(){
16461             return !disabled;
16462        },
16463
16464         // private
16465        tagConfig : {
16466            namespace : "ext",
16467            attribute : "qtip",
16468            width : "width",
16469            target : "target",
16470            title : "qtitle",
16471            hide : "hide",
16472            cls : "qclass"
16473        }
16474    };
16475 }();
16476
16477 // backwards compat
16478 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16479  * Based on:
16480  * Ext JS Library 1.1.1
16481  * Copyright(c) 2006-2007, Ext JS, LLC.
16482  *
16483  * Originally Released Under LGPL - original licence link has changed is not relivant.
16484  *
16485  * Fork - LGPL
16486  * <script type="text/javascript">
16487  */
16488  
16489
16490 /**
16491  * @class Roo.tree.TreePanel
16492  * @extends Roo.data.Tree
16493
16494  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16495  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16496  * @cfg {Boolean} enableDD true to enable drag and drop
16497  * @cfg {Boolean} enableDrag true to enable just drag
16498  * @cfg {Boolean} enableDrop true to enable just drop
16499  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16500  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16501  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16502  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16503  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16504  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16505  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16506  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16507  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16508  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16509  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16510  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16511  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16512  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16513  * @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>
16514  * @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>
16515  * 
16516  * @constructor
16517  * @param {String/HTMLElement/Element} el The container element
16518  * @param {Object} config
16519  */
16520 Roo.tree.TreePanel = function(el, config){
16521     var root = false;
16522     var loader = false;
16523     if (config.root) {
16524         root = config.root;
16525         delete config.root;
16526     }
16527     if (config.loader) {
16528         loader = config.loader;
16529         delete config.loader;
16530     }
16531     
16532     Roo.apply(this, config);
16533     Roo.tree.TreePanel.superclass.constructor.call(this);
16534     this.el = Roo.get(el);
16535     this.el.addClass('x-tree');
16536     //console.log(root);
16537     if (root) {
16538         this.setRootNode( Roo.factory(root, Roo.tree));
16539     }
16540     if (loader) {
16541         this.loader = Roo.factory(loader, Roo.tree);
16542     }
16543    /**
16544     * Read-only. The id of the container element becomes this TreePanel's id.
16545     */
16546     this.id = this.el.id;
16547     this.addEvents({
16548         /**
16549         * @event beforeload
16550         * Fires before a node is loaded, return false to cancel
16551         * @param {Node} node The node being loaded
16552         */
16553         "beforeload" : true,
16554         /**
16555         * @event load
16556         * Fires when a node is loaded
16557         * @param {Node} node The node that was loaded
16558         */
16559         "load" : true,
16560         /**
16561         * @event textchange
16562         * Fires when the text for a node is changed
16563         * @param {Node} node The node
16564         * @param {String} text The new text
16565         * @param {String} oldText The old text
16566         */
16567         "textchange" : true,
16568         /**
16569         * @event beforeexpand
16570         * Fires before a node is expanded, return false to cancel.
16571         * @param {Node} node The node
16572         * @param {Boolean} deep
16573         * @param {Boolean} anim
16574         */
16575         "beforeexpand" : true,
16576         /**
16577         * @event beforecollapse
16578         * Fires before a node is collapsed, return false to cancel.
16579         * @param {Node} node The node
16580         * @param {Boolean} deep
16581         * @param {Boolean} anim
16582         */
16583         "beforecollapse" : true,
16584         /**
16585         * @event expand
16586         * Fires when a node is expanded
16587         * @param {Node} node The node
16588         */
16589         "expand" : true,
16590         /**
16591         * @event disabledchange
16592         * Fires when the disabled status of a node changes
16593         * @param {Node} node The node
16594         * @param {Boolean} disabled
16595         */
16596         "disabledchange" : true,
16597         /**
16598         * @event collapse
16599         * Fires when a node is collapsed
16600         * @param {Node} node The node
16601         */
16602         "collapse" : true,
16603         /**
16604         * @event beforeclick
16605         * Fires before click processing on a node. Return false to cancel the default action.
16606         * @param {Node} node The node
16607         * @param {Roo.EventObject} e The event object
16608         */
16609         "beforeclick":true,
16610         /**
16611         * @event checkchange
16612         * Fires when a node with a checkbox's checked property changes
16613         * @param {Node} this This node
16614         * @param {Boolean} checked
16615         */
16616         "checkchange":true,
16617         /**
16618         * @event click
16619         * Fires when a node is clicked
16620         * @param {Node} node The node
16621         * @param {Roo.EventObject} e The event object
16622         */
16623         "click":true,
16624         /**
16625         * @event dblclick
16626         * Fires when a node is double clicked
16627         * @param {Node} node The node
16628         * @param {Roo.EventObject} e The event object
16629         */
16630         "dblclick":true,
16631         /**
16632         * @event contextmenu
16633         * Fires when a node is right clicked
16634         * @param {Node} node The node
16635         * @param {Roo.EventObject} e The event object
16636         */
16637         "contextmenu":true,
16638         /**
16639         * @event beforechildrenrendered
16640         * Fires right before the child nodes for a node are rendered
16641         * @param {Node} node The node
16642         */
16643         "beforechildrenrendered":true,
16644         /**
16645         * @event startdrag
16646         * Fires when a node starts being dragged
16647         * @param {Roo.tree.TreePanel} this
16648         * @param {Roo.tree.TreeNode} node
16649         * @param {event} e The raw browser event
16650         */ 
16651        "startdrag" : true,
16652        /**
16653         * @event enddrag
16654         * Fires when a drag operation is complete
16655         * @param {Roo.tree.TreePanel} this
16656         * @param {Roo.tree.TreeNode} node
16657         * @param {event} e The raw browser event
16658         */
16659        "enddrag" : true,
16660        /**
16661         * @event dragdrop
16662         * Fires when a dragged node is dropped on a valid DD target
16663         * @param {Roo.tree.TreePanel} this
16664         * @param {Roo.tree.TreeNode} node
16665         * @param {DD} dd The dd it was dropped on
16666         * @param {event} e The raw browser event
16667         */
16668        "dragdrop" : true,
16669        /**
16670         * @event beforenodedrop
16671         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16672         * passed to handlers has the following properties:<br />
16673         * <ul style="padding:5px;padding-left:16px;">
16674         * <li>tree - The TreePanel</li>
16675         * <li>target - The node being targeted for the drop</li>
16676         * <li>data - The drag data from the drag source</li>
16677         * <li>point - The point of the drop - append, above or below</li>
16678         * <li>source - The drag source</li>
16679         * <li>rawEvent - Raw mouse event</li>
16680         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16681         * to be inserted by setting them on this object.</li>
16682         * <li>cancel - Set this to true to cancel the drop.</li>
16683         * </ul>
16684         * @param {Object} dropEvent
16685         */
16686        "beforenodedrop" : true,
16687        /**
16688         * @event nodedrop
16689         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16690         * passed to handlers has the following properties:<br />
16691         * <ul style="padding:5px;padding-left:16px;">
16692         * <li>tree - The TreePanel</li>
16693         * <li>target - The node being targeted for the drop</li>
16694         * <li>data - The drag data from the drag source</li>
16695         * <li>point - The point of the drop - append, above or below</li>
16696         * <li>source - The drag source</li>
16697         * <li>rawEvent - Raw mouse event</li>
16698         * <li>dropNode - Dropped node(s).</li>
16699         * </ul>
16700         * @param {Object} dropEvent
16701         */
16702        "nodedrop" : true,
16703         /**
16704         * @event nodedragover
16705         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16706         * passed to handlers has the following properties:<br />
16707         * <ul style="padding:5px;padding-left:16px;">
16708         * <li>tree - The TreePanel</li>
16709         * <li>target - The node being targeted for the drop</li>
16710         * <li>data - The drag data from the drag source</li>
16711         * <li>point - The point of the drop - append, above or below</li>
16712         * <li>source - The drag source</li>
16713         * <li>rawEvent - Raw mouse event</li>
16714         * <li>dropNode - Drop node(s) provided by the source.</li>
16715         * <li>cancel - Set this to true to signal drop not allowed.</li>
16716         * </ul>
16717         * @param {Object} dragOverEvent
16718         */
16719        "nodedragover" : true
16720         
16721     });
16722     if(this.singleExpand){
16723        this.on("beforeexpand", this.restrictExpand, this);
16724     }
16725     if (this.editor) {
16726         this.editor.tree = this;
16727         this.editor = Roo.factory(this.editor, Roo.tree);
16728     }
16729     
16730     if (this.selModel) {
16731         this.selModel = Roo.factory(this.selModel, Roo.tree);
16732     }
16733    
16734 };
16735 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16736     rootVisible : true,
16737     animate: Roo.enableFx,
16738     lines : true,
16739     enableDD : false,
16740     hlDrop : Roo.enableFx,
16741   
16742     renderer: false,
16743     
16744     rendererTip: false,
16745     // private
16746     restrictExpand : function(node){
16747         var p = node.parentNode;
16748         if(p){
16749             if(p.expandedChild && p.expandedChild.parentNode == p){
16750                 p.expandedChild.collapse();
16751             }
16752             p.expandedChild = node;
16753         }
16754     },
16755
16756     // private override
16757     setRootNode : function(node){
16758         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16759         if(!this.rootVisible){
16760             node.ui = new Roo.tree.RootTreeNodeUI(node);
16761         }
16762         return node;
16763     },
16764
16765     /**
16766      * Returns the container element for this TreePanel
16767      */
16768     getEl : function(){
16769         return this.el;
16770     },
16771
16772     /**
16773      * Returns the default TreeLoader for this TreePanel
16774      */
16775     getLoader : function(){
16776         return this.loader;
16777     },
16778
16779     /**
16780      * Expand all nodes
16781      */
16782     expandAll : function(){
16783         this.root.expand(true);
16784     },
16785
16786     /**
16787      * Collapse all nodes
16788      */
16789     collapseAll : function(){
16790         this.root.collapse(true);
16791     },
16792
16793     /**
16794      * Returns the selection model used by this TreePanel
16795      */
16796     getSelectionModel : function(){
16797         if(!this.selModel){
16798             this.selModel = new Roo.tree.DefaultSelectionModel();
16799         }
16800         return this.selModel;
16801     },
16802
16803     /**
16804      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16805      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16806      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16807      * @return {Array}
16808      */
16809     getChecked : function(a, startNode){
16810         startNode = startNode || this.root;
16811         var r = [];
16812         var f = function(){
16813             if(this.attributes.checked){
16814                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16815             }
16816         }
16817         startNode.cascade(f);
16818         return r;
16819     },
16820
16821     /**
16822      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16823      * @param {String} path
16824      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16825      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16826      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16827      */
16828     expandPath : function(path, attr, callback){
16829         attr = attr || "id";
16830         var keys = path.split(this.pathSeparator);
16831         var curNode = this.root;
16832         if(curNode.attributes[attr] != keys[1]){ // invalid root
16833             if(callback){
16834                 callback(false, null);
16835             }
16836             return;
16837         }
16838         var index = 1;
16839         var f = function(){
16840             if(++index == keys.length){
16841                 if(callback){
16842                     callback(true, curNode);
16843                 }
16844                 return;
16845             }
16846             var c = curNode.findChild(attr, keys[index]);
16847             if(!c){
16848                 if(callback){
16849                     callback(false, curNode);
16850                 }
16851                 return;
16852             }
16853             curNode = c;
16854             c.expand(false, false, f);
16855         };
16856         curNode.expand(false, false, f);
16857     },
16858
16859     /**
16860      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16861      * @param {String} path
16862      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16863      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16864      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16865      */
16866     selectPath : function(path, attr, callback){
16867         attr = attr || "id";
16868         var keys = path.split(this.pathSeparator);
16869         var v = keys.pop();
16870         if(keys.length > 0){
16871             var f = function(success, node){
16872                 if(success && node){
16873                     var n = node.findChild(attr, v);
16874                     if(n){
16875                         n.select();
16876                         if(callback){
16877                             callback(true, n);
16878                         }
16879                     }else if(callback){
16880                         callback(false, n);
16881                     }
16882                 }else{
16883                     if(callback){
16884                         callback(false, n);
16885                     }
16886                 }
16887             };
16888             this.expandPath(keys.join(this.pathSeparator), attr, f);
16889         }else{
16890             this.root.select();
16891             if(callback){
16892                 callback(true, this.root);
16893             }
16894         }
16895     },
16896
16897     getTreeEl : function(){
16898         return this.el;
16899     },
16900
16901     /**
16902      * Trigger rendering of this TreePanel
16903      */
16904     render : function(){
16905         if (this.innerCt) {
16906             return this; // stop it rendering more than once!!
16907         }
16908         
16909         this.innerCt = this.el.createChild({tag:"ul",
16910                cls:"x-tree-root-ct " +
16911                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16912
16913         if(this.containerScroll){
16914             Roo.dd.ScrollManager.register(this.el);
16915         }
16916         if((this.enableDD || this.enableDrop) && !this.dropZone){
16917            /**
16918             * The dropZone used by this tree if drop is enabled
16919             * @type Roo.tree.TreeDropZone
16920             */
16921              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16922                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16923            });
16924         }
16925         if((this.enableDD || this.enableDrag) && !this.dragZone){
16926            /**
16927             * The dragZone used by this tree if drag is enabled
16928             * @type Roo.tree.TreeDragZone
16929             */
16930             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16931                ddGroup: this.ddGroup || "TreeDD",
16932                scroll: this.ddScroll
16933            });
16934         }
16935         this.getSelectionModel().init(this);
16936         if (!this.root) {
16937             Roo.log("ROOT not set in tree");
16938             return this;
16939         }
16940         this.root.render();
16941         if(!this.rootVisible){
16942             this.root.renderChildren();
16943         }
16944         return this;
16945     }
16946 });/*
16947  * Based on:
16948  * Ext JS Library 1.1.1
16949  * Copyright(c) 2006-2007, Ext JS, LLC.
16950  *
16951  * Originally Released Under LGPL - original licence link has changed is not relivant.
16952  *
16953  * Fork - LGPL
16954  * <script type="text/javascript">
16955  */
16956  
16957
16958 /**
16959  * @class Roo.tree.DefaultSelectionModel
16960  * @extends Roo.util.Observable
16961  * The default single selection for a TreePanel.
16962  * @param {Object} cfg Configuration
16963  */
16964 Roo.tree.DefaultSelectionModel = function(cfg){
16965    this.selNode = null;
16966    
16967    
16968    
16969    this.addEvents({
16970        /**
16971         * @event selectionchange
16972         * Fires when the selected node changes
16973         * @param {DefaultSelectionModel} this
16974         * @param {TreeNode} node the new selection
16975         */
16976        "selectionchange" : true,
16977
16978        /**
16979         * @event beforeselect
16980         * Fires before the selected node changes, return false to cancel the change
16981         * @param {DefaultSelectionModel} this
16982         * @param {TreeNode} node the new selection
16983         * @param {TreeNode} node the old selection
16984         */
16985        "beforeselect" : true
16986    });
16987    
16988     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16989 };
16990
16991 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16992     init : function(tree){
16993         this.tree = tree;
16994         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16995         tree.on("click", this.onNodeClick, this);
16996     },
16997     
16998     onNodeClick : function(node, e){
16999         if (e.ctrlKey && this.selNode == node)  {
17000             this.unselect(node);
17001             return;
17002         }
17003         this.select(node);
17004     },
17005     
17006     /**
17007      * Select a node.
17008      * @param {TreeNode} node The node to select
17009      * @return {TreeNode} The selected node
17010      */
17011     select : function(node){
17012         var last = this.selNode;
17013         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17014             if(last){
17015                 last.ui.onSelectedChange(false);
17016             }
17017             this.selNode = node;
17018             node.ui.onSelectedChange(true);
17019             this.fireEvent("selectionchange", this, node, last);
17020         }
17021         return node;
17022     },
17023     
17024     /**
17025      * Deselect a node.
17026      * @param {TreeNode} node The node to unselect
17027      */
17028     unselect : function(node){
17029         if(this.selNode == node){
17030             this.clearSelections();
17031         }    
17032     },
17033     
17034     /**
17035      * Clear all selections
17036      */
17037     clearSelections : function(){
17038         var n = this.selNode;
17039         if(n){
17040             n.ui.onSelectedChange(false);
17041             this.selNode = null;
17042             this.fireEvent("selectionchange", this, null);
17043         }
17044         return n;
17045     },
17046     
17047     /**
17048      * Get the selected node
17049      * @return {TreeNode} The selected node
17050      */
17051     getSelectedNode : function(){
17052         return this.selNode;    
17053     },
17054     
17055     /**
17056      * Returns true if the node is selected
17057      * @param {TreeNode} node The node to check
17058      * @return {Boolean}
17059      */
17060     isSelected : function(node){
17061         return this.selNode == node;  
17062     },
17063
17064     /**
17065      * Selects the node above the selected node in the tree, intelligently walking the nodes
17066      * @return TreeNode The new selection
17067      */
17068     selectPrevious : function(){
17069         var s = this.selNode || this.lastSelNode;
17070         if(!s){
17071             return null;
17072         }
17073         var ps = s.previousSibling;
17074         if(ps){
17075             if(!ps.isExpanded() || ps.childNodes.length < 1){
17076                 return this.select(ps);
17077             } else{
17078                 var lc = ps.lastChild;
17079                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17080                     lc = lc.lastChild;
17081                 }
17082                 return this.select(lc);
17083             }
17084         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17085             return this.select(s.parentNode);
17086         }
17087         return null;
17088     },
17089
17090     /**
17091      * Selects the node above the selected node in the tree, intelligently walking the nodes
17092      * @return TreeNode The new selection
17093      */
17094     selectNext : function(){
17095         var s = this.selNode || this.lastSelNode;
17096         if(!s){
17097             return null;
17098         }
17099         if(s.firstChild && s.isExpanded()){
17100              return this.select(s.firstChild);
17101          }else if(s.nextSibling){
17102              return this.select(s.nextSibling);
17103          }else if(s.parentNode){
17104             var newS = null;
17105             s.parentNode.bubble(function(){
17106                 if(this.nextSibling){
17107                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17108                     return false;
17109                 }
17110             });
17111             return newS;
17112          }
17113         return null;
17114     },
17115
17116     onKeyDown : function(e){
17117         var s = this.selNode || this.lastSelNode;
17118         // undesirable, but required
17119         var sm = this;
17120         if(!s){
17121             return;
17122         }
17123         var k = e.getKey();
17124         switch(k){
17125              case e.DOWN:
17126                  e.stopEvent();
17127                  this.selectNext();
17128              break;
17129              case e.UP:
17130                  e.stopEvent();
17131                  this.selectPrevious();
17132              break;
17133              case e.RIGHT:
17134                  e.preventDefault();
17135                  if(s.hasChildNodes()){
17136                      if(!s.isExpanded()){
17137                          s.expand();
17138                      }else if(s.firstChild){
17139                          this.select(s.firstChild, e);
17140                      }
17141                  }
17142              break;
17143              case e.LEFT:
17144                  e.preventDefault();
17145                  if(s.hasChildNodes() && s.isExpanded()){
17146                      s.collapse();
17147                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17148                      this.select(s.parentNode, e);
17149                  }
17150              break;
17151         };
17152     }
17153 });
17154
17155 /**
17156  * @class Roo.tree.MultiSelectionModel
17157  * @extends Roo.util.Observable
17158  * Multi selection for a TreePanel.
17159  * @param {Object} cfg Configuration
17160  */
17161 Roo.tree.MultiSelectionModel = function(){
17162    this.selNodes = [];
17163    this.selMap = {};
17164    this.addEvents({
17165        /**
17166         * @event selectionchange
17167         * Fires when the selected nodes change
17168         * @param {MultiSelectionModel} this
17169         * @param {Array} nodes Array of the selected nodes
17170         */
17171        "selectionchange" : true
17172    });
17173    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17174    
17175 };
17176
17177 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17178     init : function(tree){
17179         this.tree = tree;
17180         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17181         tree.on("click", this.onNodeClick, this);
17182     },
17183     
17184     onNodeClick : function(node, e){
17185         this.select(node, e, e.ctrlKey);
17186     },
17187     
17188     /**
17189      * Select a node.
17190      * @param {TreeNode} node The node to select
17191      * @param {EventObject} e (optional) An event associated with the selection
17192      * @param {Boolean} keepExisting True to retain existing selections
17193      * @return {TreeNode} The selected node
17194      */
17195     select : function(node, e, keepExisting){
17196         if(keepExisting !== true){
17197             this.clearSelections(true);
17198         }
17199         if(this.isSelected(node)){
17200             this.lastSelNode = node;
17201             return node;
17202         }
17203         this.selNodes.push(node);
17204         this.selMap[node.id] = node;
17205         this.lastSelNode = node;
17206         node.ui.onSelectedChange(true);
17207         this.fireEvent("selectionchange", this, this.selNodes);
17208         return node;
17209     },
17210     
17211     /**
17212      * Deselect a node.
17213      * @param {TreeNode} node The node to unselect
17214      */
17215     unselect : function(node){
17216         if(this.selMap[node.id]){
17217             node.ui.onSelectedChange(false);
17218             var sn = this.selNodes;
17219             var index = -1;
17220             if(sn.indexOf){
17221                 index = sn.indexOf(node);
17222             }else{
17223                 for(var i = 0, len = sn.length; i < len; i++){
17224                     if(sn[i] == node){
17225                         index = i;
17226                         break;
17227                     }
17228                 }
17229             }
17230             if(index != -1){
17231                 this.selNodes.splice(index, 1);
17232             }
17233             delete this.selMap[node.id];
17234             this.fireEvent("selectionchange", this, this.selNodes);
17235         }
17236     },
17237     
17238     /**
17239      * Clear all selections
17240      */
17241     clearSelections : function(suppressEvent){
17242         var sn = this.selNodes;
17243         if(sn.length > 0){
17244             for(var i = 0, len = sn.length; i < len; i++){
17245                 sn[i].ui.onSelectedChange(false);
17246             }
17247             this.selNodes = [];
17248             this.selMap = {};
17249             if(suppressEvent !== true){
17250                 this.fireEvent("selectionchange", this, this.selNodes);
17251             }
17252         }
17253     },
17254     
17255     /**
17256      * Returns true if the node is selected
17257      * @param {TreeNode} node The node to check
17258      * @return {Boolean}
17259      */
17260     isSelected : function(node){
17261         return this.selMap[node.id] ? true : false;  
17262     },
17263     
17264     /**
17265      * Returns an array of the selected nodes
17266      * @return {Array}
17267      */
17268     getSelectedNodes : function(){
17269         return this.selNodes;    
17270     },
17271
17272     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17273
17274     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17275
17276     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17277 });/*
17278  * Based on:
17279  * Ext JS Library 1.1.1
17280  * Copyright(c) 2006-2007, Ext JS, LLC.
17281  *
17282  * Originally Released Under LGPL - original licence link has changed is not relivant.
17283  *
17284  * Fork - LGPL
17285  * <script type="text/javascript">
17286  */
17287  
17288 /**
17289  * @class Roo.tree.TreeNode
17290  * @extends Roo.data.Node
17291  * @cfg {String} text The text for this node
17292  * @cfg {Boolean} expanded true to start the node expanded
17293  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17294  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17295  * @cfg {Boolean} disabled true to start the node disabled
17296  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17297  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17298  * @cfg {String} cls A css class to be added to the node
17299  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17300  * @cfg {String} href URL of the link used for the node (defaults to #)
17301  * @cfg {String} hrefTarget target frame for the link
17302  * @cfg {String} qtip An Ext QuickTip for the node
17303  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17304  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17305  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17306  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17307  * (defaults to undefined with no checkbox rendered)
17308  * @constructor
17309  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17310  */
17311 Roo.tree.TreeNode = function(attributes){
17312     attributes = attributes || {};
17313     if(typeof attributes == "string"){
17314         attributes = {text: attributes};
17315     }
17316     this.childrenRendered = false;
17317     this.rendered = false;
17318     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17319     this.expanded = attributes.expanded === true;
17320     this.isTarget = attributes.isTarget !== false;
17321     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17322     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17323
17324     /**
17325      * Read-only. The text for this node. To change it use setText().
17326      * @type String
17327      */
17328     this.text = attributes.text;
17329     /**
17330      * True if this node is disabled.
17331      * @type Boolean
17332      */
17333     this.disabled = attributes.disabled === true;
17334
17335     this.addEvents({
17336         /**
17337         * @event textchange
17338         * Fires when the text for this node is changed
17339         * @param {Node} this This node
17340         * @param {String} text The new text
17341         * @param {String} oldText The old text
17342         */
17343         "textchange" : true,
17344         /**
17345         * @event beforeexpand
17346         * Fires before this node is expanded, return false to cancel.
17347         * @param {Node} this This node
17348         * @param {Boolean} deep
17349         * @param {Boolean} anim
17350         */
17351         "beforeexpand" : true,
17352         /**
17353         * @event beforecollapse
17354         * Fires before this node is collapsed, return false to cancel.
17355         * @param {Node} this This node
17356         * @param {Boolean} deep
17357         * @param {Boolean} anim
17358         */
17359         "beforecollapse" : true,
17360         /**
17361         * @event expand
17362         * Fires when this node is expanded
17363         * @param {Node} this This node
17364         */
17365         "expand" : true,
17366         /**
17367         * @event disabledchange
17368         * Fires when the disabled status of this node changes
17369         * @param {Node} this This node
17370         * @param {Boolean} disabled
17371         */
17372         "disabledchange" : true,
17373         /**
17374         * @event collapse
17375         * Fires when this node is collapsed
17376         * @param {Node} this This node
17377         */
17378         "collapse" : true,
17379         /**
17380         * @event beforeclick
17381         * Fires before click processing. Return false to cancel the default action.
17382         * @param {Node} this This node
17383         * @param {Roo.EventObject} e The event object
17384         */
17385         "beforeclick":true,
17386         /**
17387         * @event checkchange
17388         * Fires when a node with a checkbox's checked property changes
17389         * @param {Node} this This node
17390         * @param {Boolean} checked
17391         */
17392         "checkchange":true,
17393         /**
17394         * @event click
17395         * Fires when this node is clicked
17396         * @param {Node} this This node
17397         * @param {Roo.EventObject} e The event object
17398         */
17399         "click":true,
17400         /**
17401         * @event dblclick
17402         * Fires when this node is double clicked
17403         * @param {Node} this This node
17404         * @param {Roo.EventObject} e The event object
17405         */
17406         "dblclick":true,
17407         /**
17408         * @event contextmenu
17409         * Fires when this node is right clicked
17410         * @param {Node} this This node
17411         * @param {Roo.EventObject} e The event object
17412         */
17413         "contextmenu":true,
17414         /**
17415         * @event beforechildrenrendered
17416         * Fires right before the child nodes for this node are rendered
17417         * @param {Node} this This node
17418         */
17419         "beforechildrenrendered":true
17420     });
17421
17422     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17423
17424     /**
17425      * Read-only. The UI for this node
17426      * @type TreeNodeUI
17427      */
17428     this.ui = new uiClass(this);
17429     
17430     // finally support items[]
17431     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17432         return;
17433     }
17434     
17435     
17436     Roo.each(this.attributes.items, function(c) {
17437         this.appendChild(Roo.factory(c,Roo.Tree));
17438     }, this);
17439     delete this.attributes.items;
17440     
17441     
17442     
17443 };
17444 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17445     preventHScroll: true,
17446     /**
17447      * Returns true if this node is expanded
17448      * @return {Boolean}
17449      */
17450     isExpanded : function(){
17451         return this.expanded;
17452     },
17453
17454     /**
17455      * Returns the UI object for this node
17456      * @return {TreeNodeUI}
17457      */
17458     getUI : function(){
17459         return this.ui;
17460     },
17461
17462     // private override
17463     setFirstChild : function(node){
17464         var of = this.firstChild;
17465         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17466         if(this.childrenRendered && of && node != of){
17467             of.renderIndent(true, true);
17468         }
17469         if(this.rendered){
17470             this.renderIndent(true, true);
17471         }
17472     },
17473
17474     // private override
17475     setLastChild : function(node){
17476         var ol = this.lastChild;
17477         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17478         if(this.childrenRendered && ol && node != ol){
17479             ol.renderIndent(true, true);
17480         }
17481         if(this.rendered){
17482             this.renderIndent(true, true);
17483         }
17484     },
17485
17486     // these methods are overridden to provide lazy rendering support
17487     // private override
17488     appendChild : function()
17489     {
17490         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17491         if(node && this.childrenRendered){
17492             node.render();
17493         }
17494         this.ui.updateExpandIcon();
17495         return node;
17496     },
17497
17498     // private override
17499     removeChild : function(node){
17500         this.ownerTree.getSelectionModel().unselect(node);
17501         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17502         // if it's been rendered remove dom node
17503         if(this.childrenRendered){
17504             node.ui.remove();
17505         }
17506         if(this.childNodes.length < 1){
17507             this.collapse(false, false);
17508         }else{
17509             this.ui.updateExpandIcon();
17510         }
17511         if(!this.firstChild) {
17512             this.childrenRendered = false;
17513         }
17514         return node;
17515     },
17516
17517     // private override
17518     insertBefore : function(node, refNode){
17519         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17520         if(newNode && refNode && this.childrenRendered){
17521             node.render();
17522         }
17523         this.ui.updateExpandIcon();
17524         return newNode;
17525     },
17526
17527     /**
17528      * Sets the text for this node
17529      * @param {String} text
17530      */
17531     setText : function(text){
17532         var oldText = this.text;
17533         this.text = text;
17534         this.attributes.text = text;
17535         if(this.rendered){ // event without subscribing
17536             this.ui.onTextChange(this, text, oldText);
17537         }
17538         this.fireEvent("textchange", this, text, oldText);
17539     },
17540
17541     /**
17542      * Triggers selection of this node
17543      */
17544     select : function(){
17545         this.getOwnerTree().getSelectionModel().select(this);
17546     },
17547
17548     /**
17549      * Triggers deselection of this node
17550      */
17551     unselect : function(){
17552         this.getOwnerTree().getSelectionModel().unselect(this);
17553     },
17554
17555     /**
17556      * Returns true if this node is selected
17557      * @return {Boolean}
17558      */
17559     isSelected : function(){
17560         return this.getOwnerTree().getSelectionModel().isSelected(this);
17561     },
17562
17563     /**
17564      * Expand this node.
17565      * @param {Boolean} deep (optional) True to expand all children as well
17566      * @param {Boolean} anim (optional) false to cancel the default animation
17567      * @param {Function} callback (optional) A callback to be called when
17568      * expanding this node completes (does not wait for deep expand to complete).
17569      * Called with 1 parameter, this node.
17570      */
17571     expand : function(deep, anim, callback){
17572         if(!this.expanded){
17573             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17574                 return;
17575             }
17576             if(!this.childrenRendered){
17577                 this.renderChildren();
17578             }
17579             this.expanded = true;
17580             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17581                 this.ui.animExpand(function(){
17582                     this.fireEvent("expand", this);
17583                     if(typeof callback == "function"){
17584                         callback(this);
17585                     }
17586                     if(deep === true){
17587                         this.expandChildNodes(true);
17588                     }
17589                 }.createDelegate(this));
17590                 return;
17591             }else{
17592                 this.ui.expand();
17593                 this.fireEvent("expand", this);
17594                 if(typeof callback == "function"){
17595                     callback(this);
17596                 }
17597             }
17598         }else{
17599            if(typeof callback == "function"){
17600                callback(this);
17601            }
17602         }
17603         if(deep === true){
17604             this.expandChildNodes(true);
17605         }
17606     },
17607
17608     isHiddenRoot : function(){
17609         return this.isRoot && !this.getOwnerTree().rootVisible;
17610     },
17611
17612     /**
17613      * Collapse this node.
17614      * @param {Boolean} deep (optional) True to collapse all children as well
17615      * @param {Boolean} anim (optional) false to cancel the default animation
17616      */
17617     collapse : function(deep, anim){
17618         if(this.expanded && !this.isHiddenRoot()){
17619             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17620                 return;
17621             }
17622             this.expanded = false;
17623             if((this.getOwnerTree().animate && anim !== false) || anim){
17624                 this.ui.animCollapse(function(){
17625                     this.fireEvent("collapse", this);
17626                     if(deep === true){
17627                         this.collapseChildNodes(true);
17628                     }
17629                 }.createDelegate(this));
17630                 return;
17631             }else{
17632                 this.ui.collapse();
17633                 this.fireEvent("collapse", this);
17634             }
17635         }
17636         if(deep === true){
17637             var cs = this.childNodes;
17638             for(var i = 0, len = cs.length; i < len; i++) {
17639                 cs[i].collapse(true, false);
17640             }
17641         }
17642     },
17643
17644     // private
17645     delayedExpand : function(delay){
17646         if(!this.expandProcId){
17647             this.expandProcId = this.expand.defer(delay, this);
17648         }
17649     },
17650
17651     // private
17652     cancelExpand : function(){
17653         if(this.expandProcId){
17654             clearTimeout(this.expandProcId);
17655         }
17656         this.expandProcId = false;
17657     },
17658
17659     /**
17660      * Toggles expanded/collapsed state of the node
17661      */
17662     toggle : function(){
17663         if(this.expanded){
17664             this.collapse();
17665         }else{
17666             this.expand();
17667         }
17668     },
17669
17670     /**
17671      * Ensures all parent nodes are expanded
17672      */
17673     ensureVisible : function(callback){
17674         var tree = this.getOwnerTree();
17675         tree.expandPath(this.parentNode.getPath(), false, function(){
17676             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17677             Roo.callback(callback);
17678         }.createDelegate(this));
17679     },
17680
17681     /**
17682      * Expand all child nodes
17683      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17684      */
17685     expandChildNodes : function(deep){
17686         var cs = this.childNodes;
17687         for(var i = 0, len = cs.length; i < len; i++) {
17688                 cs[i].expand(deep);
17689         }
17690     },
17691
17692     /**
17693      * Collapse all child nodes
17694      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17695      */
17696     collapseChildNodes : function(deep){
17697         var cs = this.childNodes;
17698         for(var i = 0, len = cs.length; i < len; i++) {
17699                 cs[i].collapse(deep);
17700         }
17701     },
17702
17703     /**
17704      * Disables this node
17705      */
17706     disable : function(){
17707         this.disabled = true;
17708         this.unselect();
17709         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17710             this.ui.onDisableChange(this, true);
17711         }
17712         this.fireEvent("disabledchange", this, true);
17713     },
17714
17715     /**
17716      * Enables this node
17717      */
17718     enable : function(){
17719         this.disabled = false;
17720         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17721             this.ui.onDisableChange(this, false);
17722         }
17723         this.fireEvent("disabledchange", this, false);
17724     },
17725
17726     // private
17727     renderChildren : function(suppressEvent){
17728         if(suppressEvent !== false){
17729             this.fireEvent("beforechildrenrendered", this);
17730         }
17731         var cs = this.childNodes;
17732         for(var i = 0, len = cs.length; i < len; i++){
17733             cs[i].render(true);
17734         }
17735         this.childrenRendered = true;
17736     },
17737
17738     // private
17739     sort : function(fn, scope){
17740         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17741         if(this.childrenRendered){
17742             var cs = this.childNodes;
17743             for(var i = 0, len = cs.length; i < len; i++){
17744                 cs[i].render(true);
17745             }
17746         }
17747     },
17748
17749     // private
17750     render : function(bulkRender){
17751         this.ui.render(bulkRender);
17752         if(!this.rendered){
17753             this.rendered = true;
17754             if(this.expanded){
17755                 this.expanded = false;
17756                 this.expand(false, false);
17757             }
17758         }
17759     },
17760
17761     // private
17762     renderIndent : function(deep, refresh){
17763         if(refresh){
17764             this.ui.childIndent = null;
17765         }
17766         this.ui.renderIndent();
17767         if(deep === true && this.childrenRendered){
17768             var cs = this.childNodes;
17769             for(var i = 0, len = cs.length; i < len; i++){
17770                 cs[i].renderIndent(true, refresh);
17771             }
17772         }
17773     }
17774 });/*
17775  * Based on:
17776  * Ext JS Library 1.1.1
17777  * Copyright(c) 2006-2007, Ext JS, LLC.
17778  *
17779  * Originally Released Under LGPL - original licence link has changed is not relivant.
17780  *
17781  * Fork - LGPL
17782  * <script type="text/javascript">
17783  */
17784  
17785 /**
17786  * @class Roo.tree.AsyncTreeNode
17787  * @extends Roo.tree.TreeNode
17788  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17789  * @constructor
17790  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17791  */
17792  Roo.tree.AsyncTreeNode = function(config){
17793     this.loaded = false;
17794     this.loading = false;
17795     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17796     /**
17797     * @event beforeload
17798     * Fires before this node is loaded, return false to cancel
17799     * @param {Node} this This node
17800     */
17801     this.addEvents({'beforeload':true, 'load': true});
17802     /**
17803     * @event load
17804     * Fires when this node is loaded
17805     * @param {Node} this This node
17806     */
17807     /**
17808      * The loader used by this node (defaults to using the tree's defined loader)
17809      * @type TreeLoader
17810      * @property loader
17811      */
17812 };
17813 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17814     expand : function(deep, anim, callback){
17815         if(this.loading){ // if an async load is already running, waiting til it's done
17816             var timer;
17817             var f = function(){
17818                 if(!this.loading){ // done loading
17819                     clearInterval(timer);
17820                     this.expand(deep, anim, callback);
17821                 }
17822             }.createDelegate(this);
17823             timer = setInterval(f, 200);
17824             return;
17825         }
17826         if(!this.loaded){
17827             if(this.fireEvent("beforeload", this) === false){
17828                 return;
17829             }
17830             this.loading = true;
17831             this.ui.beforeLoad(this);
17832             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17833             if(loader){
17834                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17835                 return;
17836             }
17837         }
17838         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17839     },
17840     
17841     /**
17842      * Returns true if this node is currently loading
17843      * @return {Boolean}
17844      */
17845     isLoading : function(){
17846         return this.loading;  
17847     },
17848     
17849     loadComplete : function(deep, anim, callback){
17850         this.loading = false;
17851         this.loaded = true;
17852         this.ui.afterLoad(this);
17853         this.fireEvent("load", this);
17854         this.expand(deep, anim, callback);
17855     },
17856     
17857     /**
17858      * Returns true if this node has been loaded
17859      * @return {Boolean}
17860      */
17861     isLoaded : function(){
17862         return this.loaded;
17863     },
17864     
17865     hasChildNodes : function(){
17866         if(!this.isLeaf() && !this.loaded){
17867             return true;
17868         }else{
17869             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17870         }
17871     },
17872
17873     /**
17874      * Trigger a reload for this node
17875      * @param {Function} callback
17876      */
17877     reload : function(callback){
17878         this.collapse(false, false);
17879         while(this.firstChild){
17880             this.removeChild(this.firstChild);
17881         }
17882         this.childrenRendered = false;
17883         this.loaded = false;
17884         if(this.isHiddenRoot()){
17885             this.expanded = false;
17886         }
17887         this.expand(false, false, callback);
17888     }
17889 });/*
17890  * Based on:
17891  * Ext JS Library 1.1.1
17892  * Copyright(c) 2006-2007, Ext JS, LLC.
17893  *
17894  * Originally Released Under LGPL - original licence link has changed is not relivant.
17895  *
17896  * Fork - LGPL
17897  * <script type="text/javascript">
17898  */
17899  
17900 /**
17901  * @class Roo.tree.TreeNodeUI
17902  * @constructor
17903  * @param {Object} node The node to render
17904  * The TreeNode UI implementation is separate from the
17905  * tree implementation. Unless you are customizing the tree UI,
17906  * you should never have to use this directly.
17907  */
17908 Roo.tree.TreeNodeUI = function(node){
17909     this.node = node;
17910     this.rendered = false;
17911     this.animating = false;
17912     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17913 };
17914
17915 Roo.tree.TreeNodeUI.prototype = {
17916     removeChild : function(node){
17917         if(this.rendered){
17918             this.ctNode.removeChild(node.ui.getEl());
17919         }
17920     },
17921
17922     beforeLoad : function(){
17923          this.addClass("x-tree-node-loading");
17924     },
17925
17926     afterLoad : function(){
17927          this.removeClass("x-tree-node-loading");
17928     },
17929
17930     onTextChange : function(node, text, oldText){
17931         if(this.rendered){
17932             this.textNode.innerHTML = text;
17933         }
17934     },
17935
17936     onDisableChange : function(node, state){
17937         this.disabled = state;
17938         if(state){
17939             this.addClass("x-tree-node-disabled");
17940         }else{
17941             this.removeClass("x-tree-node-disabled");
17942         }
17943     },
17944
17945     onSelectedChange : function(state){
17946         if(state){
17947             this.focus();
17948             this.addClass("x-tree-selected");
17949         }else{
17950             //this.blur();
17951             this.removeClass("x-tree-selected");
17952         }
17953     },
17954
17955     onMove : function(tree, node, oldParent, newParent, index, refNode){
17956         this.childIndent = null;
17957         if(this.rendered){
17958             var targetNode = newParent.ui.getContainer();
17959             if(!targetNode){//target not rendered
17960                 this.holder = document.createElement("div");
17961                 this.holder.appendChild(this.wrap);
17962                 return;
17963             }
17964             var insertBefore = refNode ? refNode.ui.getEl() : null;
17965             if(insertBefore){
17966                 targetNode.insertBefore(this.wrap, insertBefore);
17967             }else{
17968                 targetNode.appendChild(this.wrap);
17969             }
17970             this.node.renderIndent(true);
17971         }
17972     },
17973
17974     addClass : function(cls){
17975         if(this.elNode){
17976             Roo.fly(this.elNode).addClass(cls);
17977         }
17978     },
17979
17980     removeClass : function(cls){
17981         if(this.elNode){
17982             Roo.fly(this.elNode).removeClass(cls);
17983         }
17984     },
17985
17986     remove : function(){
17987         if(this.rendered){
17988             this.holder = document.createElement("div");
17989             this.holder.appendChild(this.wrap);
17990         }
17991     },
17992
17993     fireEvent : function(){
17994         return this.node.fireEvent.apply(this.node, arguments);
17995     },
17996
17997     initEvents : function(){
17998         this.node.on("move", this.onMove, this);
17999         var E = Roo.EventManager;
18000         var a = this.anchor;
18001
18002         var el = Roo.fly(a, '_treeui');
18003
18004         if(Roo.isOpera){ // opera render bug ignores the CSS
18005             el.setStyle("text-decoration", "none");
18006         }
18007
18008         el.on("click", this.onClick, this);
18009         el.on("dblclick", this.onDblClick, this);
18010
18011         if(this.checkbox){
18012             Roo.EventManager.on(this.checkbox,
18013                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18014         }
18015
18016         el.on("contextmenu", this.onContextMenu, this);
18017
18018         var icon = Roo.fly(this.iconNode);
18019         icon.on("click", this.onClick, this);
18020         icon.on("dblclick", this.onDblClick, this);
18021         icon.on("contextmenu", this.onContextMenu, this);
18022         E.on(this.ecNode, "click", this.ecClick, this, true);
18023
18024         if(this.node.disabled){
18025             this.addClass("x-tree-node-disabled");
18026         }
18027         if(this.node.hidden){
18028             this.addClass("x-tree-node-disabled");
18029         }
18030         var ot = this.node.getOwnerTree();
18031         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18032         if(dd && (!this.node.isRoot || ot.rootVisible)){
18033             Roo.dd.Registry.register(this.elNode, {
18034                 node: this.node,
18035                 handles: this.getDDHandles(),
18036                 isHandle: false
18037             });
18038         }
18039     },
18040
18041     getDDHandles : function(){
18042         return [this.iconNode, this.textNode];
18043     },
18044
18045     hide : function(){
18046         if(this.rendered){
18047             this.wrap.style.display = "none";
18048         }
18049     },
18050
18051     show : function(){
18052         if(this.rendered){
18053             this.wrap.style.display = "";
18054         }
18055     },
18056
18057     onContextMenu : function(e){
18058         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18059             e.preventDefault();
18060             this.focus();
18061             this.fireEvent("contextmenu", this.node, e);
18062         }
18063     },
18064
18065     onClick : function(e){
18066         if(this.dropping){
18067             e.stopEvent();
18068             return;
18069         }
18070         if(this.fireEvent("beforeclick", this.node, e) !== false){
18071             if(!this.disabled && this.node.attributes.href){
18072                 this.fireEvent("click", this.node, e);
18073                 return;
18074             }
18075             e.preventDefault();
18076             if(this.disabled){
18077                 return;
18078             }
18079
18080             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18081                 this.node.toggle();
18082             }
18083
18084             this.fireEvent("click", this.node, e);
18085         }else{
18086             e.stopEvent();
18087         }
18088     },
18089
18090     onDblClick : function(e){
18091         e.preventDefault();
18092         if(this.disabled){
18093             return;
18094         }
18095         if(this.checkbox){
18096             this.toggleCheck();
18097         }
18098         if(!this.animating && this.node.hasChildNodes()){
18099             this.node.toggle();
18100         }
18101         this.fireEvent("dblclick", this.node, e);
18102     },
18103
18104     onCheckChange : function(){
18105         var checked = this.checkbox.checked;
18106         this.node.attributes.checked = checked;
18107         this.fireEvent('checkchange', this.node, checked);
18108     },
18109
18110     ecClick : function(e){
18111         if(!this.animating && this.node.hasChildNodes()){
18112             this.node.toggle();
18113         }
18114     },
18115
18116     startDrop : function(){
18117         this.dropping = true;
18118     },
18119
18120     // delayed drop so the click event doesn't get fired on a drop
18121     endDrop : function(){
18122        setTimeout(function(){
18123            this.dropping = false;
18124        }.createDelegate(this), 50);
18125     },
18126
18127     expand : function(){
18128         this.updateExpandIcon();
18129         this.ctNode.style.display = "";
18130     },
18131
18132     focus : function(){
18133         if(!this.node.preventHScroll){
18134             try{this.anchor.focus();
18135             }catch(e){}
18136         }else if(!Roo.isIE){
18137             try{
18138                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18139                 var l = noscroll.scrollLeft;
18140                 this.anchor.focus();
18141                 noscroll.scrollLeft = l;
18142             }catch(e){}
18143         }
18144     },
18145
18146     toggleCheck : function(value){
18147         var cb = this.checkbox;
18148         if(cb){
18149             cb.checked = (value === undefined ? !cb.checked : value);
18150         }
18151     },
18152
18153     blur : function(){
18154         try{
18155             this.anchor.blur();
18156         }catch(e){}
18157     },
18158
18159     animExpand : function(callback){
18160         var ct = Roo.get(this.ctNode);
18161         ct.stopFx();
18162         if(!this.node.hasChildNodes()){
18163             this.updateExpandIcon();
18164             this.ctNode.style.display = "";
18165             Roo.callback(callback);
18166             return;
18167         }
18168         this.animating = true;
18169         this.updateExpandIcon();
18170
18171         ct.slideIn('t', {
18172            callback : function(){
18173                this.animating = false;
18174                Roo.callback(callback);
18175             },
18176             scope: this,
18177             duration: this.node.ownerTree.duration || .25
18178         });
18179     },
18180
18181     highlight : function(){
18182         var tree = this.node.getOwnerTree();
18183         Roo.fly(this.wrap).highlight(
18184             tree.hlColor || "C3DAF9",
18185             {endColor: tree.hlBaseColor}
18186         );
18187     },
18188
18189     collapse : function(){
18190         this.updateExpandIcon();
18191         this.ctNode.style.display = "none";
18192     },
18193
18194     animCollapse : function(callback){
18195         var ct = Roo.get(this.ctNode);
18196         ct.enableDisplayMode('block');
18197         ct.stopFx();
18198
18199         this.animating = true;
18200         this.updateExpandIcon();
18201
18202         ct.slideOut('t', {
18203             callback : function(){
18204                this.animating = false;
18205                Roo.callback(callback);
18206             },
18207             scope: this,
18208             duration: this.node.ownerTree.duration || .25
18209         });
18210     },
18211
18212     getContainer : function(){
18213         return this.ctNode;
18214     },
18215
18216     getEl : function(){
18217         return this.wrap;
18218     },
18219
18220     appendDDGhost : function(ghostNode){
18221         ghostNode.appendChild(this.elNode.cloneNode(true));
18222     },
18223
18224     getDDRepairXY : function(){
18225         return Roo.lib.Dom.getXY(this.iconNode);
18226     },
18227
18228     onRender : function(){
18229         this.render();
18230     },
18231
18232     render : function(bulkRender){
18233         var n = this.node, a = n.attributes;
18234         var targetNode = n.parentNode ?
18235               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18236
18237         if(!this.rendered){
18238             this.rendered = true;
18239
18240             this.renderElements(n, a, targetNode, bulkRender);
18241
18242             if(a.qtip){
18243                if(this.textNode.setAttributeNS){
18244                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18245                    if(a.qtipTitle){
18246                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18247                    }
18248                }else{
18249                    this.textNode.setAttribute("ext:qtip", a.qtip);
18250                    if(a.qtipTitle){
18251                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18252                    }
18253                }
18254             }else if(a.qtipCfg){
18255                 a.qtipCfg.target = Roo.id(this.textNode);
18256                 Roo.QuickTips.register(a.qtipCfg);
18257             }
18258             this.initEvents();
18259             if(!this.node.expanded){
18260                 this.updateExpandIcon();
18261             }
18262         }else{
18263             if(bulkRender === true) {
18264                 targetNode.appendChild(this.wrap);
18265             }
18266         }
18267     },
18268
18269     renderElements : function(n, a, targetNode, bulkRender)
18270     {
18271         // add some indent caching, this helps performance when rendering a large tree
18272         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18273         var t = n.getOwnerTree();
18274         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18275         if (typeof(n.attributes.html) != 'undefined') {
18276             txt = n.attributes.html;
18277         }
18278         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18279         var cb = typeof a.checked == 'boolean';
18280         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18281         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18282             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18283             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18284             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18285             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18286             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18287              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18288                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18289             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18290             "</li>"];
18291
18292         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18293             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18294                                 n.nextSibling.ui.getEl(), buf.join(""));
18295         }else{
18296             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18297         }
18298
18299         this.elNode = this.wrap.childNodes[0];
18300         this.ctNode = this.wrap.childNodes[1];
18301         var cs = this.elNode.childNodes;
18302         this.indentNode = cs[0];
18303         this.ecNode = cs[1];
18304         this.iconNode = cs[2];
18305         var index = 3;
18306         if(cb){
18307             this.checkbox = cs[3];
18308             index++;
18309         }
18310         this.anchor = cs[index];
18311         this.textNode = cs[index].firstChild;
18312     },
18313
18314     getAnchor : function(){
18315         return this.anchor;
18316     },
18317
18318     getTextEl : function(){
18319         return this.textNode;
18320     },
18321
18322     getIconEl : function(){
18323         return this.iconNode;
18324     },
18325
18326     isChecked : function(){
18327         return this.checkbox ? this.checkbox.checked : false;
18328     },
18329
18330     updateExpandIcon : function(){
18331         if(this.rendered){
18332             var n = this.node, c1, c2;
18333             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18334             var hasChild = n.hasChildNodes();
18335             if(hasChild){
18336                 if(n.expanded){
18337                     cls += "-minus";
18338                     c1 = "x-tree-node-collapsed";
18339                     c2 = "x-tree-node-expanded";
18340                 }else{
18341                     cls += "-plus";
18342                     c1 = "x-tree-node-expanded";
18343                     c2 = "x-tree-node-collapsed";
18344                 }
18345                 if(this.wasLeaf){
18346                     this.removeClass("x-tree-node-leaf");
18347                     this.wasLeaf = false;
18348                 }
18349                 if(this.c1 != c1 || this.c2 != c2){
18350                     Roo.fly(this.elNode).replaceClass(c1, c2);
18351                     this.c1 = c1; this.c2 = c2;
18352                 }
18353             }else{
18354                 // this changes non-leafs into leafs if they have no children.
18355                 // it's not very rational behaviour..
18356                 
18357                 if(!this.wasLeaf && this.node.leaf){
18358                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18359                     delete this.c1;
18360                     delete this.c2;
18361                     this.wasLeaf = true;
18362                 }
18363             }
18364             var ecc = "x-tree-ec-icon "+cls;
18365             if(this.ecc != ecc){
18366                 this.ecNode.className = ecc;
18367                 this.ecc = ecc;
18368             }
18369         }
18370     },
18371
18372     getChildIndent : function(){
18373         if(!this.childIndent){
18374             var buf = [];
18375             var p = this.node;
18376             while(p){
18377                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18378                     if(!p.isLast()) {
18379                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18380                     } else {
18381                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18382                     }
18383                 }
18384                 p = p.parentNode;
18385             }
18386             this.childIndent = buf.join("");
18387         }
18388         return this.childIndent;
18389     },
18390
18391     renderIndent : function(){
18392         if(this.rendered){
18393             var indent = "";
18394             var p = this.node.parentNode;
18395             if(p){
18396                 indent = p.ui.getChildIndent();
18397             }
18398             if(this.indentMarkup != indent){ // don't rerender if not required
18399                 this.indentNode.innerHTML = indent;
18400                 this.indentMarkup = indent;
18401             }
18402             this.updateExpandIcon();
18403         }
18404     }
18405 };
18406
18407 Roo.tree.RootTreeNodeUI = function(){
18408     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18409 };
18410 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18411     render : function(){
18412         if(!this.rendered){
18413             var targetNode = this.node.ownerTree.innerCt.dom;
18414             this.node.expanded = true;
18415             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18416             this.wrap = this.ctNode = targetNode.firstChild;
18417         }
18418     },
18419     collapse : function(){
18420     },
18421     expand : function(){
18422     }
18423 });/*
18424  * Based on:
18425  * Ext JS Library 1.1.1
18426  * Copyright(c) 2006-2007, Ext JS, LLC.
18427  *
18428  * Originally Released Under LGPL - original licence link has changed is not relivant.
18429  *
18430  * Fork - LGPL
18431  * <script type="text/javascript">
18432  */
18433 /**
18434  * @class Roo.tree.TreeLoader
18435  * @extends Roo.util.Observable
18436  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18437  * nodes from a specified URL. The response must be a javascript Array definition
18438  * who's elements are node definition objects. eg:
18439  * <pre><code>
18440 {  success : true,
18441    data :      [
18442    
18443     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18444     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18445     ]
18446 }
18447
18448
18449 </code></pre>
18450  * <br><br>
18451  * The old style respose with just an array is still supported, but not recommended.
18452  * <br><br>
18453  *
18454  * A server request is sent, and child nodes are loaded only when a node is expanded.
18455  * The loading node's id is passed to the server under the parameter name "node" to
18456  * enable the server to produce the correct child nodes.
18457  * <br><br>
18458  * To pass extra parameters, an event handler may be attached to the "beforeload"
18459  * event, and the parameters specified in the TreeLoader's baseParams property:
18460  * <pre><code>
18461     myTreeLoader.on("beforeload", function(treeLoader, node) {
18462         this.baseParams.category = node.attributes.category;
18463     }, this);
18464 </code></pre><
18465  * This would pass an HTTP parameter called "category" to the server containing
18466  * the value of the Node's "category" attribute.
18467  * @constructor
18468  * Creates a new Treeloader.
18469  * @param {Object} config A config object containing config properties.
18470  */
18471 Roo.tree.TreeLoader = function(config){
18472     this.baseParams = {};
18473     this.requestMethod = "POST";
18474     Roo.apply(this, config);
18475
18476     this.addEvents({
18477     
18478         /**
18479          * @event beforeload
18480          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18481          * @param {Object} This TreeLoader object.
18482          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18483          * @param {Object} callback The callback function specified in the {@link #load} call.
18484          */
18485         beforeload : true,
18486         /**
18487          * @event load
18488          * Fires when the node has been successfuly loaded.
18489          * @param {Object} This TreeLoader object.
18490          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18491          * @param {Object} response The response object containing the data from the server.
18492          */
18493         load : true,
18494         /**
18495          * @event loadexception
18496          * Fires if the network request failed.
18497          * @param {Object} This TreeLoader object.
18498          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18499          * @param {Object} response The response object containing the data from the server.
18500          */
18501         loadexception : true,
18502         /**
18503          * @event create
18504          * Fires before a node is created, enabling you to return custom Node types 
18505          * @param {Object} This TreeLoader object.
18506          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18507          */
18508         create : true
18509     });
18510
18511     Roo.tree.TreeLoader.superclass.constructor.call(this);
18512 };
18513
18514 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18515     /**
18516     * @cfg {String} dataUrl The URL from which to request a Json string which
18517     * specifies an array of node definition object representing the child nodes
18518     * to be loaded.
18519     */
18520     /**
18521     * @cfg {String} requestMethod either GET or POST
18522     * defaults to POST (due to BC)
18523     * to be loaded.
18524     */
18525     /**
18526     * @cfg {Object} baseParams (optional) An object containing properties which
18527     * specify HTTP parameters to be passed to each request for child nodes.
18528     */
18529     /**
18530     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18531     * created by this loader. If the attributes sent by the server have an attribute in this object,
18532     * they take priority.
18533     */
18534     /**
18535     * @cfg {Object} uiProviders (optional) An object containing properties which
18536     * 
18537     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18538     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18539     * <i>uiProvider</i> attribute of a returned child node is a string rather
18540     * than a reference to a TreeNodeUI implementation, this that string value
18541     * is used as a property name in the uiProviders object. You can define the provider named
18542     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18543     */
18544     uiProviders : {},
18545
18546     /**
18547     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18548     * child nodes before loading.
18549     */
18550     clearOnLoad : true,
18551
18552     /**
18553     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18554     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18555     * Grid query { data : [ .....] }
18556     */
18557     
18558     root : false,
18559      /**
18560     * @cfg {String} queryParam (optional) 
18561     * Name of the query as it will be passed on the querystring (defaults to 'node')
18562     * eg. the request will be ?node=[id]
18563     */
18564     
18565     
18566     queryParam: false,
18567     
18568     /**
18569      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18570      * This is called automatically when a node is expanded, but may be used to reload
18571      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18572      * @param {Roo.tree.TreeNode} node
18573      * @param {Function} callback
18574      */
18575     load : function(node, callback){
18576         if(this.clearOnLoad){
18577             while(node.firstChild){
18578                 node.removeChild(node.firstChild);
18579             }
18580         }
18581         if(node.attributes.children){ // preloaded json children
18582             var cs = node.attributes.children;
18583             for(var i = 0, len = cs.length; i < len; i++){
18584                 node.appendChild(this.createNode(cs[i]));
18585             }
18586             if(typeof callback == "function"){
18587                 callback();
18588             }
18589         }else if(this.dataUrl){
18590             this.requestData(node, callback);
18591         }
18592     },
18593
18594     getParams: function(node){
18595         var buf = [], bp = this.baseParams;
18596         for(var key in bp){
18597             if(typeof bp[key] != "function"){
18598                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18599             }
18600         }
18601         var n = this.queryParam === false ? 'node' : this.queryParam;
18602         buf.push(n + "=", encodeURIComponent(node.id));
18603         return buf.join("");
18604     },
18605
18606     requestData : function(node, callback){
18607         if(this.fireEvent("beforeload", this, node, callback) !== false){
18608             this.transId = Roo.Ajax.request({
18609                 method:this.requestMethod,
18610                 url: this.dataUrl||this.url,
18611                 success: this.handleResponse,
18612                 failure: this.handleFailure,
18613                 scope: this,
18614                 argument: {callback: callback, node: node},
18615                 params: this.getParams(node)
18616             });
18617         }else{
18618             // if the load is cancelled, make sure we notify
18619             // the node that we are done
18620             if(typeof callback == "function"){
18621                 callback();
18622             }
18623         }
18624     },
18625
18626     isLoading : function(){
18627         return this.transId ? true : false;
18628     },
18629
18630     abort : function(){
18631         if(this.isLoading()){
18632             Roo.Ajax.abort(this.transId);
18633         }
18634     },
18635
18636     // private
18637     createNode : function(attr)
18638     {
18639         // apply baseAttrs, nice idea Corey!
18640         if(this.baseAttrs){
18641             Roo.applyIf(attr, this.baseAttrs);
18642         }
18643         if(this.applyLoader !== false){
18644             attr.loader = this;
18645         }
18646         // uiProvider = depreciated..
18647         
18648         if(typeof(attr.uiProvider) == 'string'){
18649            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18650                 /**  eval:var:attr */ eval(attr.uiProvider);
18651         }
18652         if(typeof(this.uiProviders['default']) != 'undefined') {
18653             attr.uiProvider = this.uiProviders['default'];
18654         }
18655         
18656         this.fireEvent('create', this, attr);
18657         
18658         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18659         return(attr.leaf ?
18660                         new Roo.tree.TreeNode(attr) :
18661                         new Roo.tree.AsyncTreeNode(attr));
18662     },
18663
18664     processResponse : function(response, node, callback)
18665     {
18666         var json = response.responseText;
18667         try {
18668             
18669             var o = Roo.decode(json);
18670             
18671             if (this.root === false && typeof(o.success) != undefined) {
18672                 this.root = 'data'; // the default behaviour for list like data..
18673                 }
18674                 
18675             if (this.root !== false &&  !o.success) {
18676                 // it's a failure condition.
18677                 var a = response.argument;
18678                 this.fireEvent("loadexception", this, a.node, response);
18679                 Roo.log("Load failed - should have a handler really");
18680                 return;
18681             }
18682             
18683             
18684             
18685             if (this.root !== false) {
18686                  o = o[this.root];
18687             }
18688             
18689             for(var i = 0, len = o.length; i < len; i++){
18690                 var n = this.createNode(o[i]);
18691                 if(n){
18692                     node.appendChild(n);
18693                 }
18694             }
18695             if(typeof callback == "function"){
18696                 callback(this, node);
18697             }
18698         }catch(e){
18699             this.handleFailure(response);
18700         }
18701     },
18702
18703     handleResponse : function(response){
18704         this.transId = false;
18705         var a = response.argument;
18706         this.processResponse(response, a.node, a.callback);
18707         this.fireEvent("load", this, a.node, response);
18708     },
18709
18710     handleFailure : function(response)
18711     {
18712         // should handle failure better..
18713         this.transId = false;
18714         var a = response.argument;
18715         this.fireEvent("loadexception", this, a.node, response);
18716         if(typeof a.callback == "function"){
18717             a.callback(this, a.node);
18718         }
18719     }
18720 });/*
18721  * Based on:
18722  * Ext JS Library 1.1.1
18723  * Copyright(c) 2006-2007, Ext JS, LLC.
18724  *
18725  * Originally Released Under LGPL - original licence link has changed is not relivant.
18726  *
18727  * Fork - LGPL
18728  * <script type="text/javascript">
18729  */
18730
18731 /**
18732 * @class Roo.tree.TreeFilter
18733 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18734 * @param {TreePanel} tree
18735 * @param {Object} config (optional)
18736  */
18737 Roo.tree.TreeFilter = function(tree, config){
18738     this.tree = tree;
18739     this.filtered = {};
18740     Roo.apply(this, config);
18741 };
18742
18743 Roo.tree.TreeFilter.prototype = {
18744     clearBlank:false,
18745     reverse:false,
18746     autoClear:false,
18747     remove:false,
18748
18749      /**
18750      * Filter the data by a specific attribute.
18751      * @param {String/RegExp} value Either string that the attribute value
18752      * should start with or a RegExp to test against the attribute
18753      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18754      * @param {TreeNode} startNode (optional) The node to start the filter at.
18755      */
18756     filter : function(value, attr, startNode){
18757         attr = attr || "text";
18758         var f;
18759         if(typeof value == "string"){
18760             var vlen = value.length;
18761             // auto clear empty filter
18762             if(vlen == 0 && this.clearBlank){
18763                 this.clear();
18764                 return;
18765             }
18766             value = value.toLowerCase();
18767             f = function(n){
18768                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18769             };
18770         }else if(value.exec){ // regex?
18771             f = function(n){
18772                 return value.test(n.attributes[attr]);
18773             };
18774         }else{
18775             throw 'Illegal filter type, must be string or regex';
18776         }
18777         this.filterBy(f, null, startNode);
18778         },
18779
18780     /**
18781      * Filter by a function. The passed function will be called with each
18782      * node in the tree (or from the startNode). If the function returns true, the node is kept
18783      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18784      * @param {Function} fn The filter function
18785      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18786      */
18787     filterBy : function(fn, scope, startNode){
18788         startNode = startNode || this.tree.root;
18789         if(this.autoClear){
18790             this.clear();
18791         }
18792         var af = this.filtered, rv = this.reverse;
18793         var f = function(n){
18794             if(n == startNode){
18795                 return true;
18796             }
18797             if(af[n.id]){
18798                 return false;
18799             }
18800             var m = fn.call(scope || n, n);
18801             if(!m || rv){
18802                 af[n.id] = n;
18803                 n.ui.hide();
18804                 return false;
18805             }
18806             return true;
18807         };
18808         startNode.cascade(f);
18809         if(this.remove){
18810            for(var id in af){
18811                if(typeof id != "function"){
18812                    var n = af[id];
18813                    if(n && n.parentNode){
18814                        n.parentNode.removeChild(n);
18815                    }
18816                }
18817            }
18818         }
18819     },
18820
18821     /**
18822      * Clears the current filter. Note: with the "remove" option
18823      * set a filter cannot be cleared.
18824      */
18825     clear : function(){
18826         var t = this.tree;
18827         var af = this.filtered;
18828         for(var id in af){
18829             if(typeof id != "function"){
18830                 var n = af[id];
18831                 if(n){
18832                     n.ui.show();
18833                 }
18834             }
18835         }
18836         this.filtered = {};
18837     }
18838 };
18839 /*
18840  * Based on:
18841  * Ext JS Library 1.1.1
18842  * Copyright(c) 2006-2007, Ext JS, LLC.
18843  *
18844  * Originally Released Under LGPL - original licence link has changed is not relivant.
18845  *
18846  * Fork - LGPL
18847  * <script type="text/javascript">
18848  */
18849  
18850
18851 /**
18852  * @class Roo.tree.TreeSorter
18853  * Provides sorting of nodes in a TreePanel
18854  * 
18855  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18856  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18857  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18858  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18859  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18860  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18861  * @constructor
18862  * @param {TreePanel} tree
18863  * @param {Object} config
18864  */
18865 Roo.tree.TreeSorter = function(tree, config){
18866     Roo.apply(this, config);
18867     tree.on("beforechildrenrendered", this.doSort, this);
18868     tree.on("append", this.updateSort, this);
18869     tree.on("insert", this.updateSort, this);
18870     
18871     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18872     var p = this.property || "text";
18873     var sortType = this.sortType;
18874     var fs = this.folderSort;
18875     var cs = this.caseSensitive === true;
18876     var leafAttr = this.leafAttr || 'leaf';
18877
18878     this.sortFn = function(n1, n2){
18879         if(fs){
18880             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18881                 return 1;
18882             }
18883             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18884                 return -1;
18885             }
18886         }
18887         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18888         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18889         if(v1 < v2){
18890                         return dsc ? +1 : -1;
18891                 }else if(v1 > v2){
18892                         return dsc ? -1 : +1;
18893         }else{
18894                 return 0;
18895         }
18896     };
18897 };
18898
18899 Roo.tree.TreeSorter.prototype = {
18900     doSort : function(node){
18901         node.sort(this.sortFn);
18902     },
18903     
18904     compareNodes : function(n1, n2){
18905         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18906     },
18907     
18908     updateSort : function(tree, node){
18909         if(node.childrenRendered){
18910             this.doSort.defer(1, this, [node]);
18911         }
18912     }
18913 };/*
18914  * Based on:
18915  * Ext JS Library 1.1.1
18916  * Copyright(c) 2006-2007, Ext JS, LLC.
18917  *
18918  * Originally Released Under LGPL - original licence link has changed is not relivant.
18919  *
18920  * Fork - LGPL
18921  * <script type="text/javascript">
18922  */
18923
18924 if(Roo.dd.DropZone){
18925     
18926 Roo.tree.TreeDropZone = function(tree, config){
18927     this.allowParentInsert = false;
18928     this.allowContainerDrop = false;
18929     this.appendOnly = false;
18930     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18931     this.tree = tree;
18932     this.lastInsertClass = "x-tree-no-status";
18933     this.dragOverData = {};
18934 };
18935
18936 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18937     ddGroup : "TreeDD",
18938     
18939     expandDelay : 1000,
18940     
18941     expandNode : function(node){
18942         if(node.hasChildNodes() && !node.isExpanded()){
18943             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18944         }
18945     },
18946     
18947     queueExpand : function(node){
18948         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18949     },
18950     
18951     cancelExpand : function(){
18952         if(this.expandProcId){
18953             clearTimeout(this.expandProcId);
18954             this.expandProcId = false;
18955         }
18956     },
18957     
18958     isValidDropPoint : function(n, pt, dd, e, data){
18959         if(!n || !data){ return false; }
18960         var targetNode = n.node;
18961         var dropNode = data.node;
18962         // default drop rules
18963         if(!(targetNode && targetNode.isTarget && pt)){
18964             return false;
18965         }
18966         if(pt == "append" && targetNode.allowChildren === false){
18967             return false;
18968         }
18969         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18970             return false;
18971         }
18972         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18973             return false;
18974         }
18975         // reuse the object
18976         var overEvent = this.dragOverData;
18977         overEvent.tree = this.tree;
18978         overEvent.target = targetNode;
18979         overEvent.data = data;
18980         overEvent.point = pt;
18981         overEvent.source = dd;
18982         overEvent.rawEvent = e;
18983         overEvent.dropNode = dropNode;
18984         overEvent.cancel = false;  
18985         var result = this.tree.fireEvent("nodedragover", overEvent);
18986         return overEvent.cancel === false && result !== false;
18987     },
18988     
18989     getDropPoint : function(e, n, dd)
18990     {
18991         var tn = n.node;
18992         if(tn.isRoot){
18993             return tn.allowChildren !== false ? "append" : false; // always append for root
18994         }
18995         var dragEl = n.ddel;
18996         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18997         var y = Roo.lib.Event.getPageY(e);
18998         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18999         
19000         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19001         var noAppend = tn.allowChildren === false;
19002         if(this.appendOnly || tn.parentNode.allowChildren === false){
19003             return noAppend ? false : "append";
19004         }
19005         var noBelow = false;
19006         if(!this.allowParentInsert){
19007             noBelow = tn.hasChildNodes() && tn.isExpanded();
19008         }
19009         var q = (b - t) / (noAppend ? 2 : 3);
19010         if(y >= t && y < (t + q)){
19011             return "above";
19012         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19013             return "below";
19014         }else{
19015             return "append";
19016         }
19017     },
19018     
19019     onNodeEnter : function(n, dd, e, data)
19020     {
19021         this.cancelExpand();
19022     },
19023     
19024     onNodeOver : function(n, dd, e, data)
19025     {
19026        
19027         var pt = this.getDropPoint(e, n, dd);
19028         var node = n.node;
19029         
19030         // auto node expand check
19031         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19032             this.queueExpand(node);
19033         }else if(pt != "append"){
19034             this.cancelExpand();
19035         }
19036         
19037         // set the insert point style on the target node
19038         var returnCls = this.dropNotAllowed;
19039         if(this.isValidDropPoint(n, pt, dd, e, data)){
19040            if(pt){
19041                var el = n.ddel;
19042                var cls;
19043                if(pt == "above"){
19044                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19045                    cls = "x-tree-drag-insert-above";
19046                }else if(pt == "below"){
19047                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19048                    cls = "x-tree-drag-insert-below";
19049                }else{
19050                    returnCls = "x-tree-drop-ok-append";
19051                    cls = "x-tree-drag-append";
19052                }
19053                if(this.lastInsertClass != cls){
19054                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19055                    this.lastInsertClass = cls;
19056                }
19057            }
19058        }
19059        return returnCls;
19060     },
19061     
19062     onNodeOut : function(n, dd, e, data){
19063         
19064         this.cancelExpand();
19065         this.removeDropIndicators(n);
19066     },
19067     
19068     onNodeDrop : function(n, dd, e, data){
19069         var point = this.getDropPoint(e, n, dd);
19070         var targetNode = n.node;
19071         targetNode.ui.startDrop();
19072         if(!this.isValidDropPoint(n, point, dd, e, data)){
19073             targetNode.ui.endDrop();
19074             return false;
19075         }
19076         // first try to find the drop node
19077         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19078         var dropEvent = {
19079             tree : this.tree,
19080             target: targetNode,
19081             data: data,
19082             point: point,
19083             source: dd,
19084             rawEvent: e,
19085             dropNode: dropNode,
19086             cancel: !dropNode   
19087         };
19088         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19089         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19090             targetNode.ui.endDrop();
19091             return false;
19092         }
19093         // allow target changing
19094         targetNode = dropEvent.target;
19095         if(point == "append" && !targetNode.isExpanded()){
19096             targetNode.expand(false, null, function(){
19097                 this.completeDrop(dropEvent);
19098             }.createDelegate(this));
19099         }else{
19100             this.completeDrop(dropEvent);
19101         }
19102         return true;
19103     },
19104     
19105     completeDrop : function(de){
19106         var ns = de.dropNode, p = de.point, t = de.target;
19107         if(!(ns instanceof Array)){
19108             ns = [ns];
19109         }
19110         var n;
19111         for(var i = 0, len = ns.length; i < len; i++){
19112             n = ns[i];
19113             if(p == "above"){
19114                 t.parentNode.insertBefore(n, t);
19115             }else if(p == "below"){
19116                 t.parentNode.insertBefore(n, t.nextSibling);
19117             }else{
19118                 t.appendChild(n);
19119             }
19120         }
19121         n.ui.focus();
19122         if(this.tree.hlDrop){
19123             n.ui.highlight();
19124         }
19125         t.ui.endDrop();
19126         this.tree.fireEvent("nodedrop", de);
19127     },
19128     
19129     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19130         if(this.tree.hlDrop){
19131             dropNode.ui.focus();
19132             dropNode.ui.highlight();
19133         }
19134         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19135     },
19136     
19137     getTree : function(){
19138         return this.tree;
19139     },
19140     
19141     removeDropIndicators : function(n){
19142         if(n && n.ddel){
19143             var el = n.ddel;
19144             Roo.fly(el).removeClass([
19145                     "x-tree-drag-insert-above",
19146                     "x-tree-drag-insert-below",
19147                     "x-tree-drag-append"]);
19148             this.lastInsertClass = "_noclass";
19149         }
19150     },
19151     
19152     beforeDragDrop : function(target, e, id){
19153         this.cancelExpand();
19154         return true;
19155     },
19156     
19157     afterRepair : function(data){
19158         if(data && Roo.enableFx){
19159             data.node.ui.highlight();
19160         }
19161         this.hideProxy();
19162     }
19163     
19164 });
19165
19166 }
19167 /*
19168  * Based on:
19169  * Ext JS Library 1.1.1
19170  * Copyright(c) 2006-2007, Ext JS, LLC.
19171  *
19172  * Originally Released Under LGPL - original licence link has changed is not relivant.
19173  *
19174  * Fork - LGPL
19175  * <script type="text/javascript">
19176  */
19177  
19178
19179 if(Roo.dd.DragZone){
19180 Roo.tree.TreeDragZone = function(tree, config){
19181     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19182     this.tree = tree;
19183 };
19184
19185 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19186     ddGroup : "TreeDD",
19187    
19188     onBeforeDrag : function(data, e){
19189         var n = data.node;
19190         return n && n.draggable && !n.disabled;
19191     },
19192      
19193     
19194     onInitDrag : function(e){
19195         var data = this.dragData;
19196         this.tree.getSelectionModel().select(data.node);
19197         this.proxy.update("");
19198         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19199         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19200     },
19201     
19202     getRepairXY : function(e, data){
19203         return data.node.ui.getDDRepairXY();
19204     },
19205     
19206     onEndDrag : function(data, e){
19207         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19208         if (this.scroller !== false) {
19209             Roo.log('clear scroller');
19210             window.clearInterval(this.scroller);
19211             this.scroller =false;
19212             
19213         }
19214         
19215     },
19216     
19217     onValidDrop : function(dd, e, id){
19218         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19219         this.hideProxy();
19220     },
19221     
19222     beforeInvalidDrop : function(e, id){
19223         // this scrolls the original position back into view
19224         var sm = this.tree.getSelectionModel();
19225         sm.clearSelections();
19226         sm.select(this.dragData.node);
19227     },
19228     autoScroll: function(x, y, h, w) {
19229         Roo.log("drop zone - autoscroll called");
19230         
19231         Roo.log(this.scroll ? "scroll=y": "scroll=m" );
19232         if (this.scroll) {
19233             // The client height
19234             var clientH = Roo.lib.Dom.getViewWidth();
19235
19236             // The client width
19237             var clientW = Roo.lib.Dom.getViewHeight();
19238
19239             // The amt scrolled down
19240             var st = this.DDM.getScrollTop();
19241
19242             // The amt scrolled right
19243             var sl = this.DDM.getScrollLeft();
19244
19245             // Location of the bottom of the element
19246             var bot = h + y;
19247
19248             // Location of the right of the element
19249             var right = w + x;
19250
19251             // The distance from the cursor to the bottom of the visible area,
19252             // adjusted so that we don't scroll if the cursor is beyond the
19253             // element drag constraints
19254             var toBot = (clientH + st - y - this.deltaY);
19255
19256             // The distance from the cursor to the right of the visible area
19257             var toRight = (clientW + sl - x - this.deltaX);
19258
19259
19260             // How close to the edge the cursor must be before we scroll
19261             // var thresh = (document.all) ? 100 : 40;
19262             var thresh = 40;
19263
19264             // How many pixels to scroll per autoscroll op.  This helps to reduce
19265             // clunky scrolling. IE is more sensitive about this ... it needs this
19266             // value to be higher.
19267             var scrAmt = (document.all) ? 80 : 30;
19268
19269             // Scroll down if we are near the bottom of the visible page and the
19270             // obj extends below the crease
19271             if ( bot > clientH && toBot < thresh ) {
19272                 window.scrollTo(sl, st + scrAmt);
19273             }
19274
19275             // Scroll up if the window is scrolled down and the top of the object
19276             // goes above the top border
19277             if ( y < st && st > 0 && y - st < thresh ) {
19278                 window.scrollTo(sl, st - scrAmt);
19279             }
19280
19281             // Scroll right if the obj is beyond the right border and the cursor is
19282             // near the border.
19283             if ( right > clientW && toRight < thresh ) {
19284                 window.scrollTo(sl + scrAmt, st);
19285             }
19286
19287             // Scroll left if the window has been scrolled to the right and the obj
19288             // extends past the left border
19289             if ( x < sl && sl > 0 && x - sl < thresh ) {
19290                 window.scrollTo(sl - scrAmt, st);
19291             }
19292         }
19293     }
19294 });
19295 }/*
19296  * Based on:
19297  * Ext JS Library 1.1.1
19298  * Copyright(c) 2006-2007, Ext JS, LLC.
19299  *
19300  * Originally Released Under LGPL - original licence link has changed is not relivant.
19301  *
19302  * Fork - LGPL
19303  * <script type="text/javascript">
19304  */
19305 /**
19306  * @class Roo.tree.TreeEditor
19307  * @extends Roo.Editor
19308  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19309  * as the editor field.
19310  * @constructor
19311  * @param {Object} config (used to be the tree panel.)
19312  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19313  * 
19314  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19315  * @cfg {Roo.form.TextField|Object} field The field configuration
19316  *
19317  * 
19318  */
19319 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19320     var tree = config;
19321     var field;
19322     if (oldconfig) { // old style..
19323         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19324     } else {
19325         // new style..
19326         tree = config.tree;
19327         config.field = config.field  || {};
19328         config.field.xtype = 'TextField';
19329         field = Roo.factory(config.field, Roo.form);
19330     }
19331     config = config || {};
19332     
19333     
19334     this.addEvents({
19335         /**
19336          * @event beforenodeedit
19337          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19338          * false from the handler of this event.
19339          * @param {Editor} this
19340          * @param {Roo.tree.Node} node 
19341          */
19342         "beforenodeedit" : true
19343     });
19344     
19345     //Roo.log(config);
19346     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19347
19348     this.tree = tree;
19349
19350     tree.on('beforeclick', this.beforeNodeClick, this);
19351     tree.getTreeEl().on('mousedown', this.hide, this);
19352     this.on('complete', this.updateNode, this);
19353     this.on('beforestartedit', this.fitToTree, this);
19354     this.on('startedit', this.bindScroll, this, {delay:10});
19355     this.on('specialkey', this.onSpecialKey, this);
19356 };
19357
19358 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19359     /**
19360      * @cfg {String} alignment
19361      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19362      */
19363     alignment: "l-l",
19364     // inherit
19365     autoSize: false,
19366     /**
19367      * @cfg {Boolean} hideEl
19368      * True to hide the bound element while the editor is displayed (defaults to false)
19369      */
19370     hideEl : false,
19371     /**
19372      * @cfg {String} cls
19373      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19374      */
19375     cls: "x-small-editor x-tree-editor",
19376     /**
19377      * @cfg {Boolean} shim
19378      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19379      */
19380     shim:false,
19381     // inherit
19382     shadow:"frame",
19383     /**
19384      * @cfg {Number} maxWidth
19385      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19386      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19387      * scroll and client offsets into account prior to each edit.
19388      */
19389     maxWidth: 250,
19390
19391     editDelay : 350,
19392
19393     // private
19394     fitToTree : function(ed, el){
19395         var td = this.tree.getTreeEl().dom, nd = el.dom;
19396         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19397             td.scrollLeft = nd.offsetLeft;
19398         }
19399         var w = Math.min(
19400                 this.maxWidth,
19401                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19402         this.setSize(w, '');
19403         
19404         return this.fireEvent('beforenodeedit', this, this.editNode);
19405         
19406     },
19407
19408     // private
19409     triggerEdit : function(node){
19410         this.completeEdit();
19411         this.editNode = node;
19412         this.startEdit(node.ui.textNode, node.text);
19413     },
19414
19415     // private
19416     bindScroll : function(){
19417         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19418     },
19419
19420     // private
19421     beforeNodeClick : function(node, e){
19422         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19423         this.lastClick = new Date();
19424         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19425             e.stopEvent();
19426             this.triggerEdit(node);
19427             return false;
19428         }
19429         return true;
19430     },
19431
19432     // private
19433     updateNode : function(ed, value){
19434         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19435         this.editNode.setText(value);
19436     },
19437
19438     // private
19439     onHide : function(){
19440         Roo.tree.TreeEditor.superclass.onHide.call(this);
19441         if(this.editNode){
19442             this.editNode.ui.focus();
19443         }
19444     },
19445
19446     // private
19447     onSpecialKey : function(field, e){
19448         var k = e.getKey();
19449         if(k == e.ESC){
19450             e.stopEvent();
19451             this.cancelEdit();
19452         }else if(k == e.ENTER && !e.hasModifier()){
19453             e.stopEvent();
19454             this.completeEdit();
19455         }
19456     }
19457 });//<Script type="text/javascript">
19458 /*
19459  * Based on:
19460  * Ext JS Library 1.1.1
19461  * Copyright(c) 2006-2007, Ext JS, LLC.
19462  *
19463  * Originally Released Under LGPL - original licence link has changed is not relivant.
19464  *
19465  * Fork - LGPL
19466  * <script type="text/javascript">
19467  */
19468  
19469 /**
19470  * Not documented??? - probably should be...
19471  */
19472
19473 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19474     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19475     
19476     renderElements : function(n, a, targetNode, bulkRender){
19477         //consel.log("renderElements?");
19478         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19479
19480         var t = n.getOwnerTree();
19481         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19482         
19483         var cols = t.columns;
19484         var bw = t.borderWidth;
19485         var c = cols[0];
19486         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19487          var cb = typeof a.checked == "boolean";
19488         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19489         var colcls = 'x-t-' + tid + '-c0';
19490         var buf = [
19491             '<li class="x-tree-node">',
19492             
19493                 
19494                 '<div class="x-tree-node-el ', a.cls,'">',
19495                     // extran...
19496                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19497                 
19498                 
19499                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19500                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19501                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19502                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19503                            (a.iconCls ? ' '+a.iconCls : ''),
19504                            '" unselectable="on" />',
19505                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19506                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19507                              
19508                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19509                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19510                             '<span unselectable="on" qtip="' + tx + '">',
19511                              tx,
19512                              '</span></a>' ,
19513                     '</div>',
19514                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19515                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19516                  ];
19517         for(var i = 1, len = cols.length; i < len; i++){
19518             c = cols[i];
19519             colcls = 'x-t-' + tid + '-c' +i;
19520             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19521             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19522                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19523                       "</div>");
19524          }
19525          
19526          buf.push(
19527             '</a>',
19528             '<div class="x-clear"></div></div>',
19529             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19530             "</li>");
19531         
19532         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19533             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19534                                 n.nextSibling.ui.getEl(), buf.join(""));
19535         }else{
19536             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19537         }
19538         var el = this.wrap.firstChild;
19539         this.elRow = el;
19540         this.elNode = el.firstChild;
19541         this.ranchor = el.childNodes[1];
19542         this.ctNode = this.wrap.childNodes[1];
19543         var cs = el.firstChild.childNodes;
19544         this.indentNode = cs[0];
19545         this.ecNode = cs[1];
19546         this.iconNode = cs[2];
19547         var index = 3;
19548         if(cb){
19549             this.checkbox = cs[3];
19550             index++;
19551         }
19552         this.anchor = cs[index];
19553         
19554         this.textNode = cs[index].firstChild;
19555         
19556         //el.on("click", this.onClick, this);
19557         //el.on("dblclick", this.onDblClick, this);
19558         
19559         
19560        // console.log(this);
19561     },
19562     initEvents : function(){
19563         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19564         
19565             
19566         var a = this.ranchor;
19567
19568         var el = Roo.get(a);
19569
19570         if(Roo.isOpera){ // opera render bug ignores the CSS
19571             el.setStyle("text-decoration", "none");
19572         }
19573
19574         el.on("click", this.onClick, this);
19575         el.on("dblclick", this.onDblClick, this);
19576         el.on("contextmenu", this.onContextMenu, this);
19577         
19578     },
19579     
19580     /*onSelectedChange : function(state){
19581         if(state){
19582             this.focus();
19583             this.addClass("x-tree-selected");
19584         }else{
19585             //this.blur();
19586             this.removeClass("x-tree-selected");
19587         }
19588     },*/
19589     addClass : function(cls){
19590         if(this.elRow){
19591             Roo.fly(this.elRow).addClass(cls);
19592         }
19593         
19594     },
19595     
19596     
19597     removeClass : function(cls){
19598         if(this.elRow){
19599             Roo.fly(this.elRow).removeClass(cls);
19600         }
19601     }
19602
19603     
19604     
19605 });//<Script type="text/javascript">
19606
19607 /*
19608  * Based on:
19609  * Ext JS Library 1.1.1
19610  * Copyright(c) 2006-2007, Ext JS, LLC.
19611  *
19612  * Originally Released Under LGPL - original licence link has changed is not relivant.
19613  *
19614  * Fork - LGPL
19615  * <script type="text/javascript">
19616  */
19617  
19618
19619 /**
19620  * @class Roo.tree.ColumnTree
19621  * @extends Roo.data.TreePanel
19622  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19623  * @cfg {int} borderWidth  compined right/left border allowance
19624  * @constructor
19625  * @param {String/HTMLElement/Element} el The container element
19626  * @param {Object} config
19627  */
19628 Roo.tree.ColumnTree =  function(el, config)
19629 {
19630    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19631    this.addEvents({
19632         /**
19633         * @event resize
19634         * Fire this event on a container when it resizes
19635         * @param {int} w Width
19636         * @param {int} h Height
19637         */
19638        "resize" : true
19639     });
19640     this.on('resize', this.onResize, this);
19641 };
19642
19643 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19644     //lines:false,
19645     
19646     
19647     borderWidth: Roo.isBorderBox ? 0 : 2, 
19648     headEls : false,
19649     
19650     render : function(){
19651         // add the header.....
19652        
19653         Roo.tree.ColumnTree.superclass.render.apply(this);
19654         
19655         this.el.addClass('x-column-tree');
19656         
19657         this.headers = this.el.createChild(
19658             {cls:'x-tree-headers'},this.innerCt.dom);
19659    
19660         var cols = this.columns, c;
19661         var totalWidth = 0;
19662         this.headEls = [];
19663         var  len = cols.length;
19664         for(var i = 0; i < len; i++){
19665              c = cols[i];
19666              totalWidth += c.width;
19667             this.headEls.push(this.headers.createChild({
19668                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19669                  cn: {
19670                      cls:'x-tree-hd-text',
19671                      html: c.header
19672                  },
19673                  style:'width:'+(c.width-this.borderWidth)+'px;'
19674              }));
19675         }
19676         this.headers.createChild({cls:'x-clear'});
19677         // prevent floats from wrapping when clipped
19678         this.headers.setWidth(totalWidth);
19679         //this.innerCt.setWidth(totalWidth);
19680         this.innerCt.setStyle({ overflow: 'auto' });
19681         this.onResize(this.width, this.height);
19682              
19683         
19684     },
19685     onResize : function(w,h)
19686     {
19687         this.height = h;
19688         this.width = w;
19689         // resize cols..
19690         this.innerCt.setWidth(this.width);
19691         this.innerCt.setHeight(this.height-20);
19692         
19693         // headers...
19694         var cols = this.columns, c;
19695         var totalWidth = 0;
19696         var expEl = false;
19697         var len = cols.length;
19698         for(var i = 0; i < len; i++){
19699             c = cols[i];
19700             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19701                 // it's the expander..
19702                 expEl  = this.headEls[i];
19703                 continue;
19704             }
19705             totalWidth += c.width;
19706             
19707         }
19708         if (expEl) {
19709             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19710         }
19711         this.headers.setWidth(w-20);
19712
19713         
19714         
19715         
19716     }
19717 });
19718 /*
19719  * Based on:
19720  * Ext JS Library 1.1.1
19721  * Copyright(c) 2006-2007, Ext JS, LLC.
19722  *
19723  * Originally Released Under LGPL - original licence link has changed is not relivant.
19724  *
19725  * Fork - LGPL
19726  * <script type="text/javascript">
19727  */
19728  
19729 /**
19730  * @class Roo.menu.Menu
19731  * @extends Roo.util.Observable
19732  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19733  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19734  * @constructor
19735  * Creates a new Menu
19736  * @param {Object} config Configuration options
19737  */
19738 Roo.menu.Menu = function(config){
19739     Roo.apply(this, config);
19740     this.id = this.id || Roo.id();
19741     this.addEvents({
19742         /**
19743          * @event beforeshow
19744          * Fires before this menu is displayed
19745          * @param {Roo.menu.Menu} this
19746          */
19747         beforeshow : true,
19748         /**
19749          * @event beforehide
19750          * Fires before this menu is hidden
19751          * @param {Roo.menu.Menu} this
19752          */
19753         beforehide : true,
19754         /**
19755          * @event show
19756          * Fires after this menu is displayed
19757          * @param {Roo.menu.Menu} this
19758          */
19759         show : true,
19760         /**
19761          * @event hide
19762          * Fires after this menu is hidden
19763          * @param {Roo.menu.Menu} this
19764          */
19765         hide : true,
19766         /**
19767          * @event click
19768          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19769          * @param {Roo.menu.Menu} this
19770          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19771          * @param {Roo.EventObject} e
19772          */
19773         click : true,
19774         /**
19775          * @event mouseover
19776          * Fires when the mouse is hovering over this menu
19777          * @param {Roo.menu.Menu} this
19778          * @param {Roo.EventObject} e
19779          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19780          */
19781         mouseover : true,
19782         /**
19783          * @event mouseout
19784          * Fires when the mouse exits this menu
19785          * @param {Roo.menu.Menu} this
19786          * @param {Roo.EventObject} e
19787          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19788          */
19789         mouseout : true,
19790         /**
19791          * @event itemclick
19792          * Fires when a menu item contained in this menu is clicked
19793          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19794          * @param {Roo.EventObject} e
19795          */
19796         itemclick: true
19797     });
19798     if (this.registerMenu) {
19799         Roo.menu.MenuMgr.register(this);
19800     }
19801     
19802     var mis = this.items;
19803     this.items = new Roo.util.MixedCollection();
19804     if(mis){
19805         this.add.apply(this, mis);
19806     }
19807 };
19808
19809 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19810     /**
19811      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19812      */
19813     minWidth : 120,
19814     /**
19815      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19816      * for bottom-right shadow (defaults to "sides")
19817      */
19818     shadow : "sides",
19819     /**
19820      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19821      * this menu (defaults to "tl-tr?")
19822      */
19823     subMenuAlign : "tl-tr?",
19824     /**
19825      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19826      * relative to its element of origin (defaults to "tl-bl?")
19827      */
19828     defaultAlign : "tl-bl?",
19829     /**
19830      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19831      */
19832     allowOtherMenus : false,
19833     /**
19834      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19835      */
19836     registerMenu : true,
19837
19838     hidden:true,
19839
19840     // private
19841     render : function(){
19842         if(this.el){
19843             return;
19844         }
19845         var el = this.el = new Roo.Layer({
19846             cls: "x-menu",
19847             shadow:this.shadow,
19848             constrain: false,
19849             parentEl: this.parentEl || document.body,
19850             zindex:15000
19851         });
19852
19853         this.keyNav = new Roo.menu.MenuNav(this);
19854
19855         if(this.plain){
19856             el.addClass("x-menu-plain");
19857         }
19858         if(this.cls){
19859             el.addClass(this.cls);
19860         }
19861         // generic focus element
19862         this.focusEl = el.createChild({
19863             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19864         });
19865         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19866         ul.on("click", this.onClick, this);
19867         ul.on("mouseover", this.onMouseOver, this);
19868         ul.on("mouseout", this.onMouseOut, this);
19869         this.items.each(function(item){
19870             var li = document.createElement("li");
19871             li.className = "x-menu-list-item";
19872             ul.dom.appendChild(li);
19873             item.render(li, this);
19874         }, this);
19875         this.ul = ul;
19876         this.autoWidth();
19877     },
19878
19879     // private
19880     autoWidth : function(){
19881         var el = this.el, ul = this.ul;
19882         if(!el){
19883             return;
19884         }
19885         var w = this.width;
19886         if(w){
19887             el.setWidth(w);
19888         }else if(Roo.isIE){
19889             el.setWidth(this.minWidth);
19890             var t = el.dom.offsetWidth; // force recalc
19891             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19892         }
19893     },
19894
19895     // private
19896     delayAutoWidth : function(){
19897         if(this.rendered){
19898             if(!this.awTask){
19899                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19900             }
19901             this.awTask.delay(20);
19902         }
19903     },
19904
19905     // private
19906     findTargetItem : function(e){
19907         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19908         if(t && t.menuItemId){
19909             return this.items.get(t.menuItemId);
19910         }
19911     },
19912
19913     // private
19914     onClick : function(e){
19915         var t;
19916         if(t = this.findTargetItem(e)){
19917             t.onClick(e);
19918             this.fireEvent("click", this, t, e);
19919         }
19920     },
19921
19922     // private
19923     setActiveItem : function(item, autoExpand){
19924         if(item != this.activeItem){
19925             if(this.activeItem){
19926                 this.activeItem.deactivate();
19927             }
19928             this.activeItem = item;
19929             item.activate(autoExpand);
19930         }else if(autoExpand){
19931             item.expandMenu();
19932         }
19933     },
19934
19935     // private
19936     tryActivate : function(start, step){
19937         var items = this.items;
19938         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19939             var item = items.get(i);
19940             if(!item.disabled && item.canActivate){
19941                 this.setActiveItem(item, false);
19942                 return item;
19943             }
19944         }
19945         return false;
19946     },
19947
19948     // private
19949     onMouseOver : function(e){
19950         var t;
19951         if(t = this.findTargetItem(e)){
19952             if(t.canActivate && !t.disabled){
19953                 this.setActiveItem(t, true);
19954             }
19955         }
19956         this.fireEvent("mouseover", this, e, t);
19957     },
19958
19959     // private
19960     onMouseOut : function(e){
19961         var t;
19962         if(t = this.findTargetItem(e)){
19963             if(t == this.activeItem && t.shouldDeactivate(e)){
19964                 this.activeItem.deactivate();
19965                 delete this.activeItem;
19966             }
19967         }
19968         this.fireEvent("mouseout", this, e, t);
19969     },
19970
19971     /**
19972      * Read-only.  Returns true if the menu is currently displayed, else false.
19973      * @type Boolean
19974      */
19975     isVisible : function(){
19976         return this.el && !this.hidden;
19977     },
19978
19979     /**
19980      * Displays this menu relative to another element
19981      * @param {String/HTMLElement/Roo.Element} element The element to align to
19982      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19983      * the element (defaults to this.defaultAlign)
19984      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19985      */
19986     show : function(el, pos, parentMenu){
19987         this.parentMenu = parentMenu;
19988         if(!this.el){
19989             this.render();
19990         }
19991         this.fireEvent("beforeshow", this);
19992         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19993     },
19994
19995     /**
19996      * Displays this menu at a specific xy position
19997      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19998      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19999      */
20000     showAt : function(xy, parentMenu, /* private: */_e){
20001         this.parentMenu = parentMenu;
20002         if(!this.el){
20003             this.render();
20004         }
20005         if(_e !== false){
20006             this.fireEvent("beforeshow", this);
20007             xy = this.el.adjustForConstraints(xy);
20008         }
20009         this.el.setXY(xy);
20010         this.el.show();
20011         this.hidden = false;
20012         this.focus();
20013         this.fireEvent("show", this);
20014     },
20015
20016     focus : function(){
20017         if(!this.hidden){
20018             this.doFocus.defer(50, this);
20019         }
20020     },
20021
20022     doFocus : function(){
20023         if(!this.hidden){
20024             this.focusEl.focus();
20025         }
20026     },
20027
20028     /**
20029      * Hides this menu and optionally all parent menus
20030      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20031      */
20032     hide : function(deep){
20033         if(this.el && this.isVisible()){
20034             this.fireEvent("beforehide", this);
20035             if(this.activeItem){
20036                 this.activeItem.deactivate();
20037                 this.activeItem = null;
20038             }
20039             this.el.hide();
20040             this.hidden = true;
20041             this.fireEvent("hide", this);
20042         }
20043         if(deep === true && this.parentMenu){
20044             this.parentMenu.hide(true);
20045         }
20046     },
20047
20048     /**
20049      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20050      * Any of the following are valid:
20051      * <ul>
20052      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20053      * <li>An HTMLElement object which will be converted to a menu item</li>
20054      * <li>A menu item config object that will be created as a new menu item</li>
20055      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20056      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20057      * </ul>
20058      * Usage:
20059      * <pre><code>
20060 // Create the menu
20061 var menu = new Roo.menu.Menu();
20062
20063 // Create a menu item to add by reference
20064 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20065
20066 // Add a bunch of items at once using different methods.
20067 // Only the last item added will be returned.
20068 var item = menu.add(
20069     menuItem,                // add existing item by ref
20070     'Dynamic Item',          // new TextItem
20071     '-',                     // new separator
20072     { text: 'Config Item' }  // new item by config
20073 );
20074 </code></pre>
20075      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20076      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20077      */
20078     add : function(){
20079         var a = arguments, l = a.length, item;
20080         for(var i = 0; i < l; i++){
20081             var el = a[i];
20082             if ((typeof(el) == "object") && el.xtype && el.xns) {
20083                 el = Roo.factory(el, Roo.menu);
20084             }
20085             
20086             if(el.render){ // some kind of Item
20087                 item = this.addItem(el);
20088             }else if(typeof el == "string"){ // string
20089                 if(el == "separator" || el == "-"){
20090                     item = this.addSeparator();
20091                 }else{
20092                     item = this.addText(el);
20093                 }
20094             }else if(el.tagName || el.el){ // element
20095                 item = this.addElement(el);
20096             }else if(typeof el == "object"){ // must be menu item config?
20097                 item = this.addMenuItem(el);
20098             }
20099         }
20100         return item;
20101     },
20102
20103     /**
20104      * Returns this menu's underlying {@link Roo.Element} object
20105      * @return {Roo.Element} The element
20106      */
20107     getEl : function(){
20108         if(!this.el){
20109             this.render();
20110         }
20111         return this.el;
20112     },
20113
20114     /**
20115      * Adds a separator bar to the menu
20116      * @return {Roo.menu.Item} The menu item that was added
20117      */
20118     addSeparator : function(){
20119         return this.addItem(new Roo.menu.Separator());
20120     },
20121
20122     /**
20123      * Adds an {@link Roo.Element} object to the menu
20124      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20125      * @return {Roo.menu.Item} The menu item that was added
20126      */
20127     addElement : function(el){
20128         return this.addItem(new Roo.menu.BaseItem(el));
20129     },
20130
20131     /**
20132      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20133      * @param {Roo.menu.Item} item The menu item to add
20134      * @return {Roo.menu.Item} The menu item that was added
20135      */
20136     addItem : function(item){
20137         this.items.add(item);
20138         if(this.ul){
20139             var li = document.createElement("li");
20140             li.className = "x-menu-list-item";
20141             this.ul.dom.appendChild(li);
20142             item.render(li, this);
20143             this.delayAutoWidth();
20144         }
20145         return item;
20146     },
20147
20148     /**
20149      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20150      * @param {Object} config A MenuItem config object
20151      * @return {Roo.menu.Item} The menu item that was added
20152      */
20153     addMenuItem : function(config){
20154         if(!(config instanceof Roo.menu.Item)){
20155             if(typeof config.checked == "boolean"){ // must be check menu item config?
20156                 config = new Roo.menu.CheckItem(config);
20157             }else{
20158                 config = new Roo.menu.Item(config);
20159             }
20160         }
20161         return this.addItem(config);
20162     },
20163
20164     /**
20165      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20166      * @param {String} text The text to display in the menu item
20167      * @return {Roo.menu.Item} The menu item that was added
20168      */
20169     addText : function(text){
20170         return this.addItem(new Roo.menu.TextItem({ text : text }));
20171     },
20172
20173     /**
20174      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20175      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20176      * @param {Roo.menu.Item} item The menu item to add
20177      * @return {Roo.menu.Item} The menu item that was added
20178      */
20179     insert : function(index, item){
20180         this.items.insert(index, item);
20181         if(this.ul){
20182             var li = document.createElement("li");
20183             li.className = "x-menu-list-item";
20184             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20185             item.render(li, this);
20186             this.delayAutoWidth();
20187         }
20188         return item;
20189     },
20190
20191     /**
20192      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20193      * @param {Roo.menu.Item} item The menu item to remove
20194      */
20195     remove : function(item){
20196         this.items.removeKey(item.id);
20197         item.destroy();
20198     },
20199
20200     /**
20201      * Removes and destroys all items in the menu
20202      */
20203     removeAll : function(){
20204         var f;
20205         while(f = this.items.first()){
20206             this.remove(f);
20207         }
20208     }
20209 });
20210
20211 // MenuNav is a private utility class used internally by the Menu
20212 Roo.menu.MenuNav = function(menu){
20213     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20214     this.scope = this.menu = menu;
20215 };
20216
20217 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20218     doRelay : function(e, h){
20219         var k = e.getKey();
20220         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20221             this.menu.tryActivate(0, 1);
20222             return false;
20223         }
20224         return h.call(this.scope || this, e, this.menu);
20225     },
20226
20227     up : function(e, m){
20228         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20229             m.tryActivate(m.items.length-1, -1);
20230         }
20231     },
20232
20233     down : function(e, m){
20234         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20235             m.tryActivate(0, 1);
20236         }
20237     },
20238
20239     right : function(e, m){
20240         if(m.activeItem){
20241             m.activeItem.expandMenu(true);
20242         }
20243     },
20244
20245     left : function(e, m){
20246         m.hide();
20247         if(m.parentMenu && m.parentMenu.activeItem){
20248             m.parentMenu.activeItem.activate();
20249         }
20250     },
20251
20252     enter : function(e, m){
20253         if(m.activeItem){
20254             e.stopPropagation();
20255             m.activeItem.onClick(e);
20256             m.fireEvent("click", this, m.activeItem);
20257             return true;
20258         }
20259     }
20260 });/*
20261  * Based on:
20262  * Ext JS Library 1.1.1
20263  * Copyright(c) 2006-2007, Ext JS, LLC.
20264  *
20265  * Originally Released Under LGPL - original licence link has changed is not relivant.
20266  *
20267  * Fork - LGPL
20268  * <script type="text/javascript">
20269  */
20270  
20271 /**
20272  * @class Roo.menu.MenuMgr
20273  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20274  * @singleton
20275  */
20276 Roo.menu.MenuMgr = function(){
20277    var menus, active, groups = {}, attached = false, lastShow = new Date();
20278
20279    // private - called when first menu is created
20280    function init(){
20281        menus = {};
20282        active = new Roo.util.MixedCollection();
20283        Roo.get(document).addKeyListener(27, function(){
20284            if(active.length > 0){
20285                hideAll();
20286            }
20287        });
20288    }
20289
20290    // private
20291    function hideAll(){
20292        if(active && active.length > 0){
20293            var c = active.clone();
20294            c.each(function(m){
20295                m.hide();
20296            });
20297        }
20298    }
20299
20300    // private
20301    function onHide(m){
20302        active.remove(m);
20303        if(active.length < 1){
20304            Roo.get(document).un("mousedown", onMouseDown);
20305            attached = false;
20306        }
20307    }
20308
20309    // private
20310    function onShow(m){
20311        var last = active.last();
20312        lastShow = new Date();
20313        active.add(m);
20314        if(!attached){
20315            Roo.get(document).on("mousedown", onMouseDown);
20316            attached = true;
20317        }
20318        if(m.parentMenu){
20319           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20320           m.parentMenu.activeChild = m;
20321        }else if(last && last.isVisible()){
20322           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20323        }
20324    }
20325
20326    // private
20327    function onBeforeHide(m){
20328        if(m.activeChild){
20329            m.activeChild.hide();
20330        }
20331        if(m.autoHideTimer){
20332            clearTimeout(m.autoHideTimer);
20333            delete m.autoHideTimer;
20334        }
20335    }
20336
20337    // private
20338    function onBeforeShow(m){
20339        var pm = m.parentMenu;
20340        if(!pm && !m.allowOtherMenus){
20341            hideAll();
20342        }else if(pm && pm.activeChild && active != m){
20343            pm.activeChild.hide();
20344        }
20345    }
20346
20347    // private
20348    function onMouseDown(e){
20349        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20350            hideAll();
20351        }
20352    }
20353
20354    // private
20355    function onBeforeCheck(mi, state){
20356        if(state){
20357            var g = groups[mi.group];
20358            for(var i = 0, l = g.length; i < l; i++){
20359                if(g[i] != mi){
20360                    g[i].setChecked(false);
20361                }
20362            }
20363        }
20364    }
20365
20366    return {
20367
20368        /**
20369         * Hides all menus that are currently visible
20370         */
20371        hideAll : function(){
20372             hideAll();  
20373        },
20374
20375        // private
20376        register : function(menu){
20377            if(!menus){
20378                init();
20379            }
20380            menus[menu.id] = menu;
20381            menu.on("beforehide", onBeforeHide);
20382            menu.on("hide", onHide);
20383            menu.on("beforeshow", onBeforeShow);
20384            menu.on("show", onShow);
20385            var g = menu.group;
20386            if(g && menu.events["checkchange"]){
20387                if(!groups[g]){
20388                    groups[g] = [];
20389                }
20390                groups[g].push(menu);
20391                menu.on("checkchange", onCheck);
20392            }
20393        },
20394
20395         /**
20396          * Returns a {@link Roo.menu.Menu} object
20397          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20398          * be used to generate and return a new Menu instance.
20399          */
20400        get : function(menu){
20401            if(typeof menu == "string"){ // menu id
20402                return menus[menu];
20403            }else if(menu.events){  // menu instance
20404                return menu;
20405            }else if(typeof menu.length == 'number'){ // array of menu items?
20406                return new Roo.menu.Menu({items:menu});
20407            }else{ // otherwise, must be a config
20408                return new Roo.menu.Menu(menu);
20409            }
20410        },
20411
20412        // private
20413        unregister : function(menu){
20414            delete menus[menu.id];
20415            menu.un("beforehide", onBeforeHide);
20416            menu.un("hide", onHide);
20417            menu.un("beforeshow", onBeforeShow);
20418            menu.un("show", onShow);
20419            var g = menu.group;
20420            if(g && menu.events["checkchange"]){
20421                groups[g].remove(menu);
20422                menu.un("checkchange", onCheck);
20423            }
20424        },
20425
20426        // private
20427        registerCheckable : function(menuItem){
20428            var g = menuItem.group;
20429            if(g){
20430                if(!groups[g]){
20431                    groups[g] = [];
20432                }
20433                groups[g].push(menuItem);
20434                menuItem.on("beforecheckchange", onBeforeCheck);
20435            }
20436        },
20437
20438        // private
20439        unregisterCheckable : function(menuItem){
20440            var g = menuItem.group;
20441            if(g){
20442                groups[g].remove(menuItem);
20443                menuItem.un("beforecheckchange", onBeforeCheck);
20444            }
20445        }
20446    };
20447 }();/*
20448  * Based on:
20449  * Ext JS Library 1.1.1
20450  * Copyright(c) 2006-2007, Ext JS, LLC.
20451  *
20452  * Originally Released Under LGPL - original licence link has changed is not relivant.
20453  *
20454  * Fork - LGPL
20455  * <script type="text/javascript">
20456  */
20457  
20458
20459 /**
20460  * @class Roo.menu.BaseItem
20461  * @extends Roo.Component
20462  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20463  * management and base configuration options shared by all menu components.
20464  * @constructor
20465  * Creates a new BaseItem
20466  * @param {Object} config Configuration options
20467  */
20468 Roo.menu.BaseItem = function(config){
20469     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20470
20471     this.addEvents({
20472         /**
20473          * @event click
20474          * Fires when this item is clicked
20475          * @param {Roo.menu.BaseItem} this
20476          * @param {Roo.EventObject} e
20477          */
20478         click: true,
20479         /**
20480          * @event activate
20481          * Fires when this item is activated
20482          * @param {Roo.menu.BaseItem} this
20483          */
20484         activate : true,
20485         /**
20486          * @event deactivate
20487          * Fires when this item is deactivated
20488          * @param {Roo.menu.BaseItem} this
20489          */
20490         deactivate : true
20491     });
20492
20493     if(this.handler){
20494         this.on("click", this.handler, this.scope, true);
20495     }
20496 };
20497
20498 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20499     /**
20500      * @cfg {Function} handler
20501      * A function that will handle the click event of this menu item (defaults to undefined)
20502      */
20503     /**
20504      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20505      */
20506     canActivate : false,
20507     /**
20508      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20509      */
20510     activeClass : "x-menu-item-active",
20511     /**
20512      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20513      */
20514     hideOnClick : true,
20515     /**
20516      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20517      */
20518     hideDelay : 100,
20519
20520     // private
20521     ctype: "Roo.menu.BaseItem",
20522
20523     // private
20524     actionMode : "container",
20525
20526     // private
20527     render : function(container, parentMenu){
20528         this.parentMenu = parentMenu;
20529         Roo.menu.BaseItem.superclass.render.call(this, container);
20530         this.container.menuItemId = this.id;
20531     },
20532
20533     // private
20534     onRender : function(container, position){
20535         this.el = Roo.get(this.el);
20536         container.dom.appendChild(this.el.dom);
20537     },
20538
20539     // private
20540     onClick : function(e){
20541         if(!this.disabled && this.fireEvent("click", this, e) !== false
20542                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20543             this.handleClick(e);
20544         }else{
20545             e.stopEvent();
20546         }
20547     },
20548
20549     // private
20550     activate : function(){
20551         if(this.disabled){
20552             return false;
20553         }
20554         var li = this.container;
20555         li.addClass(this.activeClass);
20556         this.region = li.getRegion().adjust(2, 2, -2, -2);
20557         this.fireEvent("activate", this);
20558         return true;
20559     },
20560
20561     // private
20562     deactivate : function(){
20563         this.container.removeClass(this.activeClass);
20564         this.fireEvent("deactivate", this);
20565     },
20566
20567     // private
20568     shouldDeactivate : function(e){
20569         return !this.region || !this.region.contains(e.getPoint());
20570     },
20571
20572     // private
20573     handleClick : function(e){
20574         if(this.hideOnClick){
20575             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20576         }
20577     },
20578
20579     // private
20580     expandMenu : function(autoActivate){
20581         // do nothing
20582     },
20583
20584     // private
20585     hideMenu : function(){
20586         // do nothing
20587     }
20588 });/*
20589  * Based on:
20590  * Ext JS Library 1.1.1
20591  * Copyright(c) 2006-2007, Ext JS, LLC.
20592  *
20593  * Originally Released Under LGPL - original licence link has changed is not relivant.
20594  *
20595  * Fork - LGPL
20596  * <script type="text/javascript">
20597  */
20598  
20599 /**
20600  * @class Roo.menu.Adapter
20601  * @extends Roo.menu.BaseItem
20602  * 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.
20603  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20604  * @constructor
20605  * Creates a new Adapter
20606  * @param {Object} config Configuration options
20607  */
20608 Roo.menu.Adapter = function(component, config){
20609     Roo.menu.Adapter.superclass.constructor.call(this, config);
20610     this.component = component;
20611 };
20612 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20613     // private
20614     canActivate : true,
20615
20616     // private
20617     onRender : function(container, position){
20618         this.component.render(container);
20619         this.el = this.component.getEl();
20620     },
20621
20622     // private
20623     activate : function(){
20624         if(this.disabled){
20625             return false;
20626         }
20627         this.component.focus();
20628         this.fireEvent("activate", this);
20629         return true;
20630     },
20631
20632     // private
20633     deactivate : function(){
20634         this.fireEvent("deactivate", this);
20635     },
20636
20637     // private
20638     disable : function(){
20639         this.component.disable();
20640         Roo.menu.Adapter.superclass.disable.call(this);
20641     },
20642
20643     // private
20644     enable : function(){
20645         this.component.enable();
20646         Roo.menu.Adapter.superclass.enable.call(this);
20647     }
20648 });/*
20649  * Based on:
20650  * Ext JS Library 1.1.1
20651  * Copyright(c) 2006-2007, Ext JS, LLC.
20652  *
20653  * Originally Released Under LGPL - original licence link has changed is not relivant.
20654  *
20655  * Fork - LGPL
20656  * <script type="text/javascript">
20657  */
20658
20659 /**
20660  * @class Roo.menu.TextItem
20661  * @extends Roo.menu.BaseItem
20662  * Adds a static text string to a menu, usually used as either a heading or group separator.
20663  * Note: old style constructor with text is still supported.
20664  * 
20665  * @constructor
20666  * Creates a new TextItem
20667  * @param {Object} cfg Configuration
20668  */
20669 Roo.menu.TextItem = function(cfg){
20670     if (typeof(cfg) == 'string') {
20671         this.text = cfg;
20672     } else {
20673         Roo.apply(this,cfg);
20674     }
20675     
20676     Roo.menu.TextItem.superclass.constructor.call(this);
20677 };
20678
20679 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20680     /**
20681      * @cfg {Boolean} text Text to show on item.
20682      */
20683     text : '',
20684     
20685     /**
20686      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20687      */
20688     hideOnClick : false,
20689     /**
20690      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20691      */
20692     itemCls : "x-menu-text",
20693
20694     // private
20695     onRender : function(){
20696         var s = document.createElement("span");
20697         s.className = this.itemCls;
20698         s.innerHTML = this.text;
20699         this.el = s;
20700         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20701     }
20702 });/*
20703  * Based on:
20704  * Ext JS Library 1.1.1
20705  * Copyright(c) 2006-2007, Ext JS, LLC.
20706  *
20707  * Originally Released Under LGPL - original licence link has changed is not relivant.
20708  *
20709  * Fork - LGPL
20710  * <script type="text/javascript">
20711  */
20712
20713 /**
20714  * @class Roo.menu.Separator
20715  * @extends Roo.menu.BaseItem
20716  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20717  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20718  * @constructor
20719  * @param {Object} config Configuration options
20720  */
20721 Roo.menu.Separator = function(config){
20722     Roo.menu.Separator.superclass.constructor.call(this, config);
20723 };
20724
20725 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20726     /**
20727      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20728      */
20729     itemCls : "x-menu-sep",
20730     /**
20731      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20732      */
20733     hideOnClick : false,
20734
20735     // private
20736     onRender : function(li){
20737         var s = document.createElement("span");
20738         s.className = this.itemCls;
20739         s.innerHTML = "&#160;";
20740         this.el = s;
20741         li.addClass("x-menu-sep-li");
20742         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20743     }
20744 });/*
20745  * Based on:
20746  * Ext JS Library 1.1.1
20747  * Copyright(c) 2006-2007, Ext JS, LLC.
20748  *
20749  * Originally Released Under LGPL - original licence link has changed is not relivant.
20750  *
20751  * Fork - LGPL
20752  * <script type="text/javascript">
20753  */
20754 /**
20755  * @class Roo.menu.Item
20756  * @extends Roo.menu.BaseItem
20757  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20758  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20759  * activation and click handling.
20760  * @constructor
20761  * Creates a new Item
20762  * @param {Object} config Configuration options
20763  */
20764 Roo.menu.Item = function(config){
20765     Roo.menu.Item.superclass.constructor.call(this, config);
20766     if(this.menu){
20767         this.menu = Roo.menu.MenuMgr.get(this.menu);
20768     }
20769 };
20770 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20771     
20772     /**
20773      * @cfg {String} text
20774      * The text to show on the menu item.
20775      */
20776     text: '',
20777      /**
20778      * @cfg {String} HTML to render in menu
20779      * The text to show on the menu item (HTML version).
20780      */
20781     html: '',
20782     /**
20783      * @cfg {String} icon
20784      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20785      */
20786     icon: undefined,
20787     /**
20788      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20789      */
20790     itemCls : "x-menu-item",
20791     /**
20792      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20793      */
20794     canActivate : true,
20795     /**
20796      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20797      */
20798     showDelay: 200,
20799     // doc'd in BaseItem
20800     hideDelay: 200,
20801
20802     // private
20803     ctype: "Roo.menu.Item",
20804     
20805     // private
20806     onRender : function(container, position){
20807         var el = document.createElement("a");
20808         el.hideFocus = true;
20809         el.unselectable = "on";
20810         el.href = this.href || "#";
20811         if(this.hrefTarget){
20812             el.target = this.hrefTarget;
20813         }
20814         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20815         
20816         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20817         
20818         el.innerHTML = String.format(
20819                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20820                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20821         this.el = el;
20822         Roo.menu.Item.superclass.onRender.call(this, container, position);
20823     },
20824
20825     /**
20826      * Sets the text to display in this menu item
20827      * @param {String} text The text to display
20828      * @param {Boolean} isHTML true to indicate text is pure html.
20829      */
20830     setText : function(text, isHTML){
20831         if (isHTML) {
20832             this.html = text;
20833         } else {
20834             this.text = text;
20835             this.html = '';
20836         }
20837         if(this.rendered){
20838             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20839      
20840             this.el.update(String.format(
20841                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20842                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20843             this.parentMenu.autoWidth();
20844         }
20845     },
20846
20847     // private
20848     handleClick : function(e){
20849         if(!this.href){ // if no link defined, stop the event automatically
20850             e.stopEvent();
20851         }
20852         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20853     },
20854
20855     // private
20856     activate : function(autoExpand){
20857         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20858             this.focus();
20859             if(autoExpand){
20860                 this.expandMenu();
20861             }
20862         }
20863         return true;
20864     },
20865
20866     // private
20867     shouldDeactivate : function(e){
20868         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20869             if(this.menu && this.menu.isVisible()){
20870                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20871             }
20872             return true;
20873         }
20874         return false;
20875     },
20876
20877     // private
20878     deactivate : function(){
20879         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20880         this.hideMenu();
20881     },
20882
20883     // private
20884     expandMenu : function(autoActivate){
20885         if(!this.disabled && this.menu){
20886             clearTimeout(this.hideTimer);
20887             delete this.hideTimer;
20888             if(!this.menu.isVisible() && !this.showTimer){
20889                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20890             }else if (this.menu.isVisible() && autoActivate){
20891                 this.menu.tryActivate(0, 1);
20892             }
20893         }
20894     },
20895
20896     // private
20897     deferExpand : function(autoActivate){
20898         delete this.showTimer;
20899         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20900         if(autoActivate){
20901             this.menu.tryActivate(0, 1);
20902         }
20903     },
20904
20905     // private
20906     hideMenu : function(){
20907         clearTimeout(this.showTimer);
20908         delete this.showTimer;
20909         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20910             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20911         }
20912     },
20913
20914     // private
20915     deferHide : function(){
20916         delete this.hideTimer;
20917         this.menu.hide();
20918     }
20919 });/*
20920  * Based on:
20921  * Ext JS Library 1.1.1
20922  * Copyright(c) 2006-2007, Ext JS, LLC.
20923  *
20924  * Originally Released Under LGPL - original licence link has changed is not relivant.
20925  *
20926  * Fork - LGPL
20927  * <script type="text/javascript">
20928  */
20929  
20930 /**
20931  * @class Roo.menu.CheckItem
20932  * @extends Roo.menu.Item
20933  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20934  * @constructor
20935  * Creates a new CheckItem
20936  * @param {Object} config Configuration options
20937  */
20938 Roo.menu.CheckItem = function(config){
20939     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20940     this.addEvents({
20941         /**
20942          * @event beforecheckchange
20943          * Fires before the checked value is set, providing an opportunity to cancel if needed
20944          * @param {Roo.menu.CheckItem} this
20945          * @param {Boolean} checked The new checked value that will be set
20946          */
20947         "beforecheckchange" : true,
20948         /**
20949          * @event checkchange
20950          * Fires after the checked value has been set
20951          * @param {Roo.menu.CheckItem} this
20952          * @param {Boolean} checked The checked value that was set
20953          */
20954         "checkchange" : true
20955     });
20956     if(this.checkHandler){
20957         this.on('checkchange', this.checkHandler, this.scope);
20958     }
20959 };
20960 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20961     /**
20962      * @cfg {String} group
20963      * All check items with the same group name will automatically be grouped into a single-select
20964      * radio button group (defaults to '')
20965      */
20966     /**
20967      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20968      */
20969     itemCls : "x-menu-item x-menu-check-item",
20970     /**
20971      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20972      */
20973     groupClass : "x-menu-group-item",
20974
20975     /**
20976      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20977      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20978      * initialized with checked = true will be rendered as checked.
20979      */
20980     checked: false,
20981
20982     // private
20983     ctype: "Roo.menu.CheckItem",
20984
20985     // private
20986     onRender : function(c){
20987         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20988         if(this.group){
20989             this.el.addClass(this.groupClass);
20990         }
20991         Roo.menu.MenuMgr.registerCheckable(this);
20992         if(this.checked){
20993             this.checked = false;
20994             this.setChecked(true, true);
20995         }
20996     },
20997
20998     // private
20999     destroy : function(){
21000         if(this.rendered){
21001             Roo.menu.MenuMgr.unregisterCheckable(this);
21002         }
21003         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21004     },
21005
21006     /**
21007      * Set the checked state of this item
21008      * @param {Boolean} checked The new checked value
21009      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21010      */
21011     setChecked : function(state, suppressEvent){
21012         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21013             if(this.container){
21014                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21015             }
21016             this.checked = state;
21017             if(suppressEvent !== true){
21018                 this.fireEvent("checkchange", this, state);
21019             }
21020         }
21021     },
21022
21023     // private
21024     handleClick : function(e){
21025        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21026            this.setChecked(!this.checked);
21027        }
21028        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21029     }
21030 });/*
21031  * Based on:
21032  * Ext JS Library 1.1.1
21033  * Copyright(c) 2006-2007, Ext JS, LLC.
21034  *
21035  * Originally Released Under LGPL - original licence link has changed is not relivant.
21036  *
21037  * Fork - LGPL
21038  * <script type="text/javascript">
21039  */
21040  
21041 /**
21042  * @class Roo.menu.DateItem
21043  * @extends Roo.menu.Adapter
21044  * A menu item that wraps the {@link Roo.DatPicker} component.
21045  * @constructor
21046  * Creates a new DateItem
21047  * @param {Object} config Configuration options
21048  */
21049 Roo.menu.DateItem = function(config){
21050     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21051     /** The Roo.DatePicker object @type Roo.DatePicker */
21052     this.picker = this.component;
21053     this.addEvents({select: true});
21054     
21055     this.picker.on("render", function(picker){
21056         picker.getEl().swallowEvent("click");
21057         picker.container.addClass("x-menu-date-item");
21058     });
21059
21060     this.picker.on("select", this.onSelect, this);
21061 };
21062
21063 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21064     // private
21065     onSelect : function(picker, date){
21066         this.fireEvent("select", this, date, picker);
21067         Roo.menu.DateItem.superclass.handleClick.call(this);
21068     }
21069 });/*
21070  * Based on:
21071  * Ext JS Library 1.1.1
21072  * Copyright(c) 2006-2007, Ext JS, LLC.
21073  *
21074  * Originally Released Under LGPL - original licence link has changed is not relivant.
21075  *
21076  * Fork - LGPL
21077  * <script type="text/javascript">
21078  */
21079  
21080 /**
21081  * @class Roo.menu.ColorItem
21082  * @extends Roo.menu.Adapter
21083  * A menu item that wraps the {@link Roo.ColorPalette} component.
21084  * @constructor
21085  * Creates a new ColorItem
21086  * @param {Object} config Configuration options
21087  */
21088 Roo.menu.ColorItem = function(config){
21089     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21090     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21091     this.palette = this.component;
21092     this.relayEvents(this.palette, ["select"]);
21093     if(this.selectHandler){
21094         this.on('select', this.selectHandler, this.scope);
21095     }
21096 };
21097 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21098  * Based on:
21099  * Ext JS Library 1.1.1
21100  * Copyright(c) 2006-2007, Ext JS, LLC.
21101  *
21102  * Originally Released Under LGPL - original licence link has changed is not relivant.
21103  *
21104  * Fork - LGPL
21105  * <script type="text/javascript">
21106  */
21107  
21108
21109 /**
21110  * @class Roo.menu.DateMenu
21111  * @extends Roo.menu.Menu
21112  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21113  * @constructor
21114  * Creates a new DateMenu
21115  * @param {Object} config Configuration options
21116  */
21117 Roo.menu.DateMenu = function(config){
21118     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21119     this.plain = true;
21120     var di = new Roo.menu.DateItem(config);
21121     this.add(di);
21122     /**
21123      * The {@link Roo.DatePicker} instance for this DateMenu
21124      * @type DatePicker
21125      */
21126     this.picker = di.picker;
21127     /**
21128      * @event select
21129      * @param {DatePicker} picker
21130      * @param {Date} date
21131      */
21132     this.relayEvents(di, ["select"]);
21133
21134     this.on('beforeshow', function(){
21135         if(this.picker){
21136             this.picker.hideMonthPicker(true);
21137         }
21138     }, this);
21139 };
21140 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21141     cls:'x-date-menu'
21142 });/*
21143  * Based on:
21144  * Ext JS Library 1.1.1
21145  * Copyright(c) 2006-2007, Ext JS, LLC.
21146  *
21147  * Originally Released Under LGPL - original licence link has changed is not relivant.
21148  *
21149  * Fork - LGPL
21150  * <script type="text/javascript">
21151  */
21152  
21153
21154 /**
21155  * @class Roo.menu.ColorMenu
21156  * @extends Roo.menu.Menu
21157  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21158  * @constructor
21159  * Creates a new ColorMenu
21160  * @param {Object} config Configuration options
21161  */
21162 Roo.menu.ColorMenu = function(config){
21163     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21164     this.plain = true;
21165     var ci = new Roo.menu.ColorItem(config);
21166     this.add(ci);
21167     /**
21168      * The {@link Roo.ColorPalette} instance for this ColorMenu
21169      * @type ColorPalette
21170      */
21171     this.palette = ci.palette;
21172     /**
21173      * @event select
21174      * @param {ColorPalette} palette
21175      * @param {String} color
21176      */
21177     this.relayEvents(ci, ["select"]);
21178 };
21179 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21180  * Based on:
21181  * Ext JS Library 1.1.1
21182  * Copyright(c) 2006-2007, Ext JS, LLC.
21183  *
21184  * Originally Released Under LGPL - original licence link has changed is not relivant.
21185  *
21186  * Fork - LGPL
21187  * <script type="text/javascript">
21188  */
21189  
21190 /**
21191  * @class Roo.form.Field
21192  * @extends Roo.BoxComponent
21193  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21194  * @constructor
21195  * Creates a new Field
21196  * @param {Object} config Configuration options
21197  */
21198 Roo.form.Field = function(config){
21199     Roo.form.Field.superclass.constructor.call(this, config);
21200 };
21201
21202 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21203     /**
21204      * @cfg {String} fieldLabel Label to use when rendering a form.
21205      */
21206        /**
21207      * @cfg {String} qtip Mouse over tip
21208      */
21209      
21210     /**
21211      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21212      */
21213     invalidClass : "x-form-invalid",
21214     /**
21215      * @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")
21216      */
21217     invalidText : "The value in this field is invalid",
21218     /**
21219      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21220      */
21221     focusClass : "x-form-focus",
21222     /**
21223      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21224       automatic validation (defaults to "keyup").
21225      */
21226     validationEvent : "keyup",
21227     /**
21228      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21229      */
21230     validateOnBlur : true,
21231     /**
21232      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21233      */
21234     validationDelay : 250,
21235     /**
21236      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21237      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21238      */
21239     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21240     /**
21241      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21242      */
21243     fieldClass : "x-form-field",
21244     /**
21245      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21246      *<pre>
21247 Value         Description
21248 -----------   ----------------------------------------------------------------------
21249 qtip          Display a quick tip when the user hovers over the field
21250 title         Display a default browser title attribute popup
21251 under         Add a block div beneath the field containing the error text
21252 side          Add an error icon to the right of the field with a popup on hover
21253 [element id]  Add the error text directly to the innerHTML of the specified element
21254 </pre>
21255      */
21256     msgTarget : 'qtip',
21257     /**
21258      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21259      */
21260     msgFx : 'normal',
21261
21262     /**
21263      * @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.
21264      */
21265     readOnly : false,
21266
21267     /**
21268      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21269      */
21270     disabled : false,
21271
21272     /**
21273      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21274      */
21275     inputType : undefined,
21276     
21277     /**
21278      * @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).
21279          */
21280         tabIndex : undefined,
21281         
21282     // private
21283     isFormField : true,
21284
21285     // private
21286     hasFocus : false,
21287     /**
21288      * @property {Roo.Element} fieldEl
21289      * Element Containing the rendered Field (with label etc.)
21290      */
21291     /**
21292      * @cfg {Mixed} value A value to initialize this field with.
21293      */
21294     value : undefined,
21295
21296     /**
21297      * @cfg {String} name The field's HTML name attribute.
21298      */
21299     /**
21300      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21301      */
21302
21303         // private ??
21304         initComponent : function(){
21305         Roo.form.Field.superclass.initComponent.call(this);
21306         this.addEvents({
21307             /**
21308              * @event focus
21309              * Fires when this field receives input focus.
21310              * @param {Roo.form.Field} this
21311              */
21312             focus : true,
21313             /**
21314              * @event blur
21315              * Fires when this field loses input focus.
21316              * @param {Roo.form.Field} this
21317              */
21318             blur : true,
21319             /**
21320              * @event specialkey
21321              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21322              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21323              * @param {Roo.form.Field} this
21324              * @param {Roo.EventObject} e The event object
21325              */
21326             specialkey : true,
21327             /**
21328              * @event change
21329              * Fires just before the field blurs if the field value has changed.
21330              * @param {Roo.form.Field} this
21331              * @param {Mixed} newValue The new value
21332              * @param {Mixed} oldValue The original value
21333              */
21334             change : true,
21335             /**
21336              * @event invalid
21337              * Fires after the field has been marked as invalid.
21338              * @param {Roo.form.Field} this
21339              * @param {String} msg The validation message
21340              */
21341             invalid : true,
21342             /**
21343              * @event valid
21344              * Fires after the field has been validated with no errors.
21345              * @param {Roo.form.Field} this
21346              */
21347             valid : true,
21348              /**
21349              * @event keyup
21350              * Fires after the key up
21351              * @param {Roo.form.Field} this
21352              * @param {Roo.EventObject}  e The event Object
21353              */
21354             keyup : true
21355         });
21356     },
21357
21358     /**
21359      * Returns the name attribute of the field if available
21360      * @return {String} name The field name
21361      */
21362     getName: function(){
21363          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21364     },
21365
21366     // private
21367     onRender : function(ct, position){
21368         Roo.form.Field.superclass.onRender.call(this, ct, position);
21369         if(!this.el){
21370             var cfg = this.getAutoCreate();
21371             if(!cfg.name){
21372                 cfg.name = this.name || this.id;
21373             }
21374             if(this.inputType){
21375                 cfg.type = this.inputType;
21376             }
21377             this.el = ct.createChild(cfg, position);
21378         }
21379         var type = this.el.dom.type;
21380         if(type){
21381             if(type == 'password'){
21382                 type = 'text';
21383             }
21384             this.el.addClass('x-form-'+type);
21385         }
21386         if(this.readOnly){
21387             this.el.dom.readOnly = true;
21388         }
21389         if(this.tabIndex !== undefined){
21390             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21391         }
21392
21393         this.el.addClass([this.fieldClass, this.cls]);
21394         this.initValue();
21395     },
21396
21397     /**
21398      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21399      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21400      * @return {Roo.form.Field} this
21401      */
21402     applyTo : function(target){
21403         this.allowDomMove = false;
21404         this.el = Roo.get(target);
21405         this.render(this.el.dom.parentNode);
21406         return this;
21407     },
21408
21409     // private
21410     initValue : function(){
21411         if(this.value !== undefined){
21412             this.setValue(this.value);
21413         }else if(this.el.dom.value.length > 0){
21414             this.setValue(this.el.dom.value);
21415         }
21416     },
21417
21418     /**
21419      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21420      */
21421     isDirty : function() {
21422         if(this.disabled) {
21423             return false;
21424         }
21425         return String(this.getValue()) !== String(this.originalValue);
21426     },
21427
21428     // private
21429     afterRender : function(){
21430         Roo.form.Field.superclass.afterRender.call(this);
21431         this.initEvents();
21432     },
21433
21434     // private
21435     fireKey : function(e){
21436         //Roo.log('field ' + e.getKey());
21437         if(e.isNavKeyPress()){
21438             this.fireEvent("specialkey", this, e);
21439         }
21440     },
21441
21442     /**
21443      * Resets the current field value to the originally loaded value and clears any validation messages
21444      */
21445     reset : function(){
21446         this.setValue(this.originalValue);
21447         this.clearInvalid();
21448     },
21449
21450     // private
21451     initEvents : function(){
21452         // safari killled keypress - so keydown is now used..
21453         this.el.on("keydown" , this.fireKey,  this);
21454         this.el.on("focus", this.onFocus,  this);
21455         this.el.on("blur", this.onBlur,  this);
21456         this.el.relayEvent('keyup', this);
21457
21458         // reference to original value for reset
21459         this.originalValue = this.getValue();
21460     },
21461
21462     // private
21463     onFocus : function(){
21464         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21465             this.el.addClass(this.focusClass);
21466         }
21467         if(!this.hasFocus){
21468             this.hasFocus = true;
21469             this.startValue = this.getValue();
21470             this.fireEvent("focus", this);
21471         }
21472     },
21473
21474     beforeBlur : Roo.emptyFn,
21475
21476     // private
21477     onBlur : function(){
21478         this.beforeBlur();
21479         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21480             this.el.removeClass(this.focusClass);
21481         }
21482         this.hasFocus = false;
21483         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21484             this.validate();
21485         }
21486         var v = this.getValue();
21487         if(String(v) !== String(this.startValue)){
21488             this.fireEvent('change', this, v, this.startValue);
21489         }
21490         this.fireEvent("blur", this);
21491     },
21492
21493     /**
21494      * Returns whether or not the field value is currently valid
21495      * @param {Boolean} preventMark True to disable marking the field invalid
21496      * @return {Boolean} True if the value is valid, else false
21497      */
21498     isValid : function(preventMark){
21499         if(this.disabled){
21500             return true;
21501         }
21502         var restore = this.preventMark;
21503         this.preventMark = preventMark === true;
21504         var v = this.validateValue(this.processValue(this.getRawValue()));
21505         this.preventMark = restore;
21506         return v;
21507     },
21508
21509     /**
21510      * Validates the field value
21511      * @return {Boolean} True if the value is valid, else false
21512      */
21513     validate : function(){
21514         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21515             this.clearInvalid();
21516             return true;
21517         }
21518         return false;
21519     },
21520
21521     processValue : function(value){
21522         return value;
21523     },
21524
21525     // private
21526     // Subclasses should provide the validation implementation by overriding this
21527     validateValue : function(value){
21528         return true;
21529     },
21530
21531     /**
21532      * Mark this field as invalid
21533      * @param {String} msg The validation message
21534      */
21535     markInvalid : function(msg){
21536         if(!this.rendered || this.preventMark){ // not rendered
21537             return;
21538         }
21539         this.el.addClass(this.invalidClass);
21540         msg = msg || this.invalidText;
21541         switch(this.msgTarget){
21542             case 'qtip':
21543                 this.el.dom.qtip = msg;
21544                 this.el.dom.qclass = 'x-form-invalid-tip';
21545                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21546                     Roo.QuickTips.enable();
21547                 }
21548                 break;
21549             case 'title':
21550                 this.el.dom.title = msg;
21551                 break;
21552             case 'under':
21553                 if(!this.errorEl){
21554                     var elp = this.el.findParent('.x-form-element', 5, true);
21555                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21556                     this.errorEl.setWidth(elp.getWidth(true)-20);
21557                 }
21558                 this.errorEl.update(msg);
21559                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21560                 break;
21561             case 'side':
21562                 if(!this.errorIcon){
21563                     var elp = this.el.findParent('.x-form-element', 5, true);
21564                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21565                 }
21566                 this.alignErrorIcon();
21567                 this.errorIcon.dom.qtip = msg;
21568                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21569                 this.errorIcon.show();
21570                 this.on('resize', this.alignErrorIcon, this);
21571                 break;
21572             default:
21573                 var t = Roo.getDom(this.msgTarget);
21574                 t.innerHTML = msg;
21575                 t.style.display = this.msgDisplay;
21576                 break;
21577         }
21578         this.fireEvent('invalid', this, msg);
21579     },
21580
21581     // private
21582     alignErrorIcon : function(){
21583         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21584     },
21585
21586     /**
21587      * Clear any invalid styles/messages for this field
21588      */
21589     clearInvalid : function(){
21590         if(!this.rendered || this.preventMark){ // not rendered
21591             return;
21592         }
21593         this.el.removeClass(this.invalidClass);
21594         switch(this.msgTarget){
21595             case 'qtip':
21596                 this.el.dom.qtip = '';
21597                 break;
21598             case 'title':
21599                 this.el.dom.title = '';
21600                 break;
21601             case 'under':
21602                 if(this.errorEl){
21603                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21604                 }
21605                 break;
21606             case 'side':
21607                 if(this.errorIcon){
21608                     this.errorIcon.dom.qtip = '';
21609                     this.errorIcon.hide();
21610                     this.un('resize', this.alignErrorIcon, this);
21611                 }
21612                 break;
21613             default:
21614                 var t = Roo.getDom(this.msgTarget);
21615                 t.innerHTML = '';
21616                 t.style.display = 'none';
21617                 break;
21618         }
21619         this.fireEvent('valid', this);
21620     },
21621
21622     /**
21623      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21624      * @return {Mixed} value The field value
21625      */
21626     getRawValue : function(){
21627         var v = this.el.getValue();
21628         if(v === this.emptyText){
21629             v = '';
21630         }
21631         return v;
21632     },
21633
21634     /**
21635      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21636      * @return {Mixed} value The field value
21637      */
21638     getValue : function(){
21639         var v = this.el.getValue();
21640         if(v === this.emptyText || v === undefined){
21641             v = '';
21642         }
21643         return v;
21644     },
21645
21646     /**
21647      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21648      * @param {Mixed} value The value to set
21649      */
21650     setRawValue : function(v){
21651         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21652     },
21653
21654     /**
21655      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21656      * @param {Mixed} value The value to set
21657      */
21658     setValue : function(v){
21659         this.value = v;
21660         if(this.rendered){
21661             this.el.dom.value = (v === null || v === undefined ? '' : v);
21662              this.validate();
21663         }
21664     },
21665
21666     adjustSize : function(w, h){
21667         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21668         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21669         return s;
21670     },
21671
21672     adjustWidth : function(tag, w){
21673         tag = tag.toLowerCase();
21674         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21675             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21676                 if(tag == 'input'){
21677                     return w + 2;
21678                 }
21679                 if(tag = 'textarea'){
21680                     return w-2;
21681                 }
21682             }else if(Roo.isOpera){
21683                 if(tag == 'input'){
21684                     return w + 2;
21685                 }
21686                 if(tag = 'textarea'){
21687                     return w-2;
21688                 }
21689             }
21690         }
21691         return w;
21692     }
21693 });
21694
21695
21696 // anything other than normal should be considered experimental
21697 Roo.form.Field.msgFx = {
21698     normal : {
21699         show: function(msgEl, f){
21700             msgEl.setDisplayed('block');
21701         },
21702
21703         hide : function(msgEl, f){
21704             msgEl.setDisplayed(false).update('');
21705         }
21706     },
21707
21708     slide : {
21709         show: function(msgEl, f){
21710             msgEl.slideIn('t', {stopFx:true});
21711         },
21712
21713         hide : function(msgEl, f){
21714             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21715         }
21716     },
21717
21718     slideRight : {
21719         show: function(msgEl, f){
21720             msgEl.fixDisplay();
21721             msgEl.alignTo(f.el, 'tl-tr');
21722             msgEl.slideIn('l', {stopFx:true});
21723         },
21724
21725         hide : function(msgEl, f){
21726             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21727         }
21728     }
21729 };/*
21730  * Based on:
21731  * Ext JS Library 1.1.1
21732  * Copyright(c) 2006-2007, Ext JS, LLC.
21733  *
21734  * Originally Released Under LGPL - original licence link has changed is not relivant.
21735  *
21736  * Fork - LGPL
21737  * <script type="text/javascript">
21738  */
21739  
21740
21741 /**
21742  * @class Roo.form.TextField
21743  * @extends Roo.form.Field
21744  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21745  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21746  * @constructor
21747  * Creates a new TextField
21748  * @param {Object} config Configuration options
21749  */
21750 Roo.form.TextField = function(config){
21751     Roo.form.TextField.superclass.constructor.call(this, config);
21752     this.addEvents({
21753         /**
21754          * @event autosize
21755          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21756          * according to the default logic, but this event provides a hook for the developer to apply additional
21757          * logic at runtime to resize the field if needed.
21758              * @param {Roo.form.Field} this This text field
21759              * @param {Number} width The new field width
21760              */
21761         autosize : true
21762     });
21763 };
21764
21765 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21766     /**
21767      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21768      */
21769     grow : false,
21770     /**
21771      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21772      */
21773     growMin : 30,
21774     /**
21775      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21776      */
21777     growMax : 800,
21778     /**
21779      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21780      */
21781     vtype : null,
21782     /**
21783      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21784      */
21785     maskRe : null,
21786     /**
21787      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21788      */
21789     disableKeyFilter : false,
21790     /**
21791      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21792      */
21793     allowBlank : true,
21794     /**
21795      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21796      */
21797     minLength : 0,
21798     /**
21799      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21800      */
21801     maxLength : Number.MAX_VALUE,
21802     /**
21803      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21804      */
21805     minLengthText : "The minimum length for this field is {0}",
21806     /**
21807      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21808      */
21809     maxLengthText : "The maximum length for this field is {0}",
21810     /**
21811      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21812      */
21813     selectOnFocus : false,
21814     /**
21815      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21816      */
21817     blankText : "This field is required",
21818     /**
21819      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21820      * If available, this function will be called only after the basic validators all return true, and will be passed the
21821      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21822      */
21823     validator : null,
21824     /**
21825      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21826      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21827      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21828      */
21829     regex : null,
21830     /**
21831      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21832      */
21833     regexText : "",
21834     /**
21835      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21836      */
21837     emptyText : null,
21838     /**
21839      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21840      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21841      */
21842     emptyClass : 'x-form-empty-field',
21843
21844     // private
21845     initEvents : function(){
21846         Roo.form.TextField.superclass.initEvents.call(this);
21847         if(this.validationEvent == 'keyup'){
21848             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21849             this.el.on('keyup', this.filterValidation, this);
21850         }
21851         else if(this.validationEvent !== false){
21852             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21853         }
21854         if(this.selectOnFocus || this.emptyText){
21855             this.on("focus", this.preFocus, this);
21856             if(this.emptyText){
21857                 this.on('blur', this.postBlur, this);
21858                 this.applyEmptyText();
21859             }
21860         }
21861         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21862             this.el.on("keypress", this.filterKeys, this);
21863         }
21864         if(this.grow){
21865             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21866             this.el.on("click", this.autoSize,  this);
21867         }
21868     },
21869
21870     processValue : function(value){
21871         if(this.stripCharsRe){
21872             var newValue = value.replace(this.stripCharsRe, '');
21873             if(newValue !== value){
21874                 this.setRawValue(newValue);
21875                 return newValue;
21876             }
21877         }
21878         return value;
21879     },
21880
21881     filterValidation : function(e){
21882         if(!e.isNavKeyPress()){
21883             this.validationTask.delay(this.validationDelay);
21884         }
21885     },
21886
21887     // private
21888     onKeyUp : function(e){
21889         if(!e.isNavKeyPress()){
21890             this.autoSize();
21891         }
21892     },
21893
21894     /**
21895      * Resets the current field value to the originally-loaded value and clears any validation messages.
21896      * Also adds emptyText and emptyClass if the original value was blank.
21897      */
21898     reset : function(){
21899         Roo.form.TextField.superclass.reset.call(this);
21900         this.applyEmptyText();
21901     },
21902
21903     applyEmptyText : function(){
21904         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21905             this.setRawValue(this.emptyText);
21906             this.el.addClass(this.emptyClass);
21907         }
21908     },
21909
21910     // private
21911     preFocus : function(){
21912         if(this.emptyText){
21913             if(this.el.dom.value == this.emptyText){
21914                 this.setRawValue('');
21915             }
21916             this.el.removeClass(this.emptyClass);
21917         }
21918         if(this.selectOnFocus){
21919             this.el.dom.select();
21920         }
21921     },
21922
21923     // private
21924     postBlur : function(){
21925         this.applyEmptyText();
21926     },
21927
21928     // private
21929     filterKeys : function(e){
21930         var k = e.getKey();
21931         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21932             return;
21933         }
21934         var c = e.getCharCode(), cc = String.fromCharCode(c);
21935         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21936             return;
21937         }
21938         if(!this.maskRe.test(cc)){
21939             e.stopEvent();
21940         }
21941     },
21942
21943     setValue : function(v){
21944         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21945             this.el.removeClass(this.emptyClass);
21946         }
21947         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21948         this.applyEmptyText();
21949         this.autoSize();
21950     },
21951
21952     /**
21953      * Validates a value according to the field's validation rules and marks the field as invalid
21954      * if the validation fails
21955      * @param {Mixed} value The value to validate
21956      * @return {Boolean} True if the value is valid, else false
21957      */
21958     validateValue : function(value){
21959         if(value.length < 1 || value === this.emptyText){ // if it's blank
21960              if(this.allowBlank){
21961                 this.clearInvalid();
21962                 return true;
21963              }else{
21964                 this.markInvalid(this.blankText);
21965                 return false;
21966              }
21967         }
21968         if(value.length < this.minLength){
21969             this.markInvalid(String.format(this.minLengthText, this.minLength));
21970             return false;
21971         }
21972         if(value.length > this.maxLength){
21973             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21974             return false;
21975         }
21976         if(this.vtype){
21977             var vt = Roo.form.VTypes;
21978             if(!vt[this.vtype](value, this)){
21979                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21980                 return false;
21981             }
21982         }
21983         if(typeof this.validator == "function"){
21984             var msg = this.validator(value);
21985             if(msg !== true){
21986                 this.markInvalid(msg);
21987                 return false;
21988             }
21989         }
21990         if(this.regex && !this.regex.test(value)){
21991             this.markInvalid(this.regexText);
21992             return false;
21993         }
21994         return true;
21995     },
21996
21997     /**
21998      * Selects text in this field
21999      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22000      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22001      */
22002     selectText : function(start, end){
22003         var v = this.getRawValue();
22004         if(v.length > 0){
22005             start = start === undefined ? 0 : start;
22006             end = end === undefined ? v.length : end;
22007             var d = this.el.dom;
22008             if(d.setSelectionRange){
22009                 d.setSelectionRange(start, end);
22010             }else if(d.createTextRange){
22011                 var range = d.createTextRange();
22012                 range.moveStart("character", start);
22013                 range.moveEnd("character", v.length-end);
22014                 range.select();
22015             }
22016         }
22017     },
22018
22019     /**
22020      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22021      * This only takes effect if grow = true, and fires the autosize event.
22022      */
22023     autoSize : function(){
22024         if(!this.grow || !this.rendered){
22025             return;
22026         }
22027         if(!this.metrics){
22028             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22029         }
22030         var el = this.el;
22031         var v = el.dom.value;
22032         var d = document.createElement('div');
22033         d.appendChild(document.createTextNode(v));
22034         v = d.innerHTML;
22035         d = null;
22036         v += "&#160;";
22037         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22038         this.el.setWidth(w);
22039         this.fireEvent("autosize", this, w);
22040     }
22041 });/*
22042  * Based on:
22043  * Ext JS Library 1.1.1
22044  * Copyright(c) 2006-2007, Ext JS, LLC.
22045  *
22046  * Originally Released Under LGPL - original licence link has changed is not relivant.
22047  *
22048  * Fork - LGPL
22049  * <script type="text/javascript">
22050  */
22051  
22052 /**
22053  * @class Roo.form.Hidden
22054  * @extends Roo.form.TextField
22055  * Simple Hidden element used on forms 
22056  * 
22057  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22058  * 
22059  * @constructor
22060  * Creates a new Hidden form element.
22061  * @param {Object} config Configuration options
22062  */
22063
22064
22065
22066 // easy hidden field...
22067 Roo.form.Hidden = function(config){
22068     Roo.form.Hidden.superclass.constructor.call(this, config);
22069 };
22070   
22071 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22072     fieldLabel:      '',
22073     inputType:      'hidden',
22074     width:          50,
22075     allowBlank:     true,
22076     labelSeparator: '',
22077     hidden:         true,
22078     itemCls :       'x-form-item-display-none'
22079
22080
22081 });
22082
22083
22084 /*
22085  * Based on:
22086  * Ext JS Library 1.1.1
22087  * Copyright(c) 2006-2007, Ext JS, LLC.
22088  *
22089  * Originally Released Under LGPL - original licence link has changed is not relivant.
22090  *
22091  * Fork - LGPL
22092  * <script type="text/javascript">
22093  */
22094  
22095 /**
22096  * @class Roo.form.TriggerField
22097  * @extends Roo.form.TextField
22098  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22099  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22100  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22101  * for which you can provide a custom implementation.  For example:
22102  * <pre><code>
22103 var trigger = new Roo.form.TriggerField();
22104 trigger.onTriggerClick = myTriggerFn;
22105 trigger.applyTo('my-field');
22106 </code></pre>
22107  *
22108  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22109  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22110  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22111  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22112  * @constructor
22113  * Create a new TriggerField.
22114  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22115  * to the base TextField)
22116  */
22117 Roo.form.TriggerField = function(config){
22118     this.mimicing = false;
22119     Roo.form.TriggerField.superclass.constructor.call(this, config);
22120 };
22121
22122 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22123     /**
22124      * @cfg {String} triggerClass A CSS class to apply to the trigger
22125      */
22126     /**
22127      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22128      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22129      */
22130     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22131     /**
22132      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22133      */
22134     hideTrigger:false,
22135
22136     /** @cfg {Boolean} grow @hide */
22137     /** @cfg {Number} growMin @hide */
22138     /** @cfg {Number} growMax @hide */
22139
22140     /**
22141      * @hide 
22142      * @method
22143      */
22144     autoSize: Roo.emptyFn,
22145     // private
22146     monitorTab : true,
22147     // private
22148     deferHeight : true,
22149
22150     
22151     actionMode : 'wrap',
22152     // private
22153     onResize : function(w, h){
22154         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22155         if(typeof w == 'number'){
22156             var x = w - this.trigger.getWidth();
22157             this.el.setWidth(this.adjustWidth('input', x));
22158             this.trigger.setStyle('left', x+'px');
22159         }
22160     },
22161
22162     // private
22163     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22164
22165     // private
22166     getResizeEl : function(){
22167         return this.wrap;
22168     },
22169
22170     // private
22171     getPositionEl : function(){
22172         return this.wrap;
22173     },
22174
22175     // private
22176     alignErrorIcon : function(){
22177         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22178     },
22179
22180     // private
22181     onRender : function(ct, position){
22182         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22183         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22184         this.trigger = this.wrap.createChild(this.triggerConfig ||
22185                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22186         if(this.hideTrigger){
22187             this.trigger.setDisplayed(false);
22188         }
22189         this.initTrigger();
22190         if(!this.width){
22191             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22192         }
22193     },
22194
22195     // private
22196     initTrigger : function(){
22197         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22198         this.trigger.addClassOnOver('x-form-trigger-over');
22199         this.trigger.addClassOnClick('x-form-trigger-click');
22200     },
22201
22202     // private
22203     onDestroy : function(){
22204         if(this.trigger){
22205             this.trigger.removeAllListeners();
22206             this.trigger.remove();
22207         }
22208         if(this.wrap){
22209             this.wrap.remove();
22210         }
22211         Roo.form.TriggerField.superclass.onDestroy.call(this);
22212     },
22213
22214     // private
22215     onFocus : function(){
22216         Roo.form.TriggerField.superclass.onFocus.call(this);
22217         if(!this.mimicing){
22218             this.wrap.addClass('x-trigger-wrap-focus');
22219             this.mimicing = true;
22220             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22221             if(this.monitorTab){
22222                 this.el.on("keydown", this.checkTab, this);
22223             }
22224         }
22225     },
22226
22227     // private
22228     checkTab : function(e){
22229         if(e.getKey() == e.TAB){
22230             this.triggerBlur();
22231         }
22232     },
22233
22234     // private
22235     onBlur : function(){
22236         // do nothing
22237     },
22238
22239     // private
22240     mimicBlur : function(e, t){
22241         if(!this.wrap.contains(t) && this.validateBlur()){
22242             this.triggerBlur();
22243         }
22244     },
22245
22246     // private
22247     triggerBlur : function(){
22248         this.mimicing = false;
22249         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22250         if(this.monitorTab){
22251             this.el.un("keydown", this.checkTab, this);
22252         }
22253         this.wrap.removeClass('x-trigger-wrap-focus');
22254         Roo.form.TriggerField.superclass.onBlur.call(this);
22255     },
22256
22257     // private
22258     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22259     validateBlur : function(e, t){
22260         return true;
22261     },
22262
22263     // private
22264     onDisable : function(){
22265         Roo.form.TriggerField.superclass.onDisable.call(this);
22266         if(this.wrap){
22267             this.wrap.addClass('x-item-disabled');
22268         }
22269     },
22270
22271     // private
22272     onEnable : function(){
22273         Roo.form.TriggerField.superclass.onEnable.call(this);
22274         if(this.wrap){
22275             this.wrap.removeClass('x-item-disabled');
22276         }
22277     },
22278
22279     // private
22280     onShow : function(){
22281         var ae = this.getActionEl();
22282         
22283         if(ae){
22284             ae.dom.style.display = '';
22285             ae.dom.style.visibility = 'visible';
22286         }
22287     },
22288
22289     // private
22290     
22291     onHide : function(){
22292         var ae = this.getActionEl();
22293         ae.dom.style.display = 'none';
22294     },
22295
22296     /**
22297      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22298      * by an implementing function.
22299      * @method
22300      * @param {EventObject} e
22301      */
22302     onTriggerClick : Roo.emptyFn
22303 });
22304
22305 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22306 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22307 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22308 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22309     initComponent : function(){
22310         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22311
22312         this.triggerConfig = {
22313             tag:'span', cls:'x-form-twin-triggers', cn:[
22314             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22315             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22316         ]};
22317     },
22318
22319     getTrigger : function(index){
22320         return this.triggers[index];
22321     },
22322
22323     initTrigger : function(){
22324         var ts = this.trigger.select('.x-form-trigger', true);
22325         this.wrap.setStyle('overflow', 'hidden');
22326         var triggerField = this;
22327         ts.each(function(t, all, index){
22328             t.hide = function(){
22329                 var w = triggerField.wrap.getWidth();
22330                 this.dom.style.display = 'none';
22331                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22332             };
22333             t.show = function(){
22334                 var w = triggerField.wrap.getWidth();
22335                 this.dom.style.display = '';
22336                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22337             };
22338             var triggerIndex = 'Trigger'+(index+1);
22339
22340             if(this['hide'+triggerIndex]){
22341                 t.dom.style.display = 'none';
22342             }
22343             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22344             t.addClassOnOver('x-form-trigger-over');
22345             t.addClassOnClick('x-form-trigger-click');
22346         }, this);
22347         this.triggers = ts.elements;
22348     },
22349
22350     onTrigger1Click : Roo.emptyFn,
22351     onTrigger2Click : Roo.emptyFn
22352 });/*
22353  * Based on:
22354  * Ext JS Library 1.1.1
22355  * Copyright(c) 2006-2007, Ext JS, LLC.
22356  *
22357  * Originally Released Under LGPL - original licence link has changed is not relivant.
22358  *
22359  * Fork - LGPL
22360  * <script type="text/javascript">
22361  */
22362  
22363 /**
22364  * @class Roo.form.TextArea
22365  * @extends Roo.form.TextField
22366  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22367  * support for auto-sizing.
22368  * @constructor
22369  * Creates a new TextArea
22370  * @param {Object} config Configuration options
22371  */
22372 Roo.form.TextArea = function(config){
22373     Roo.form.TextArea.superclass.constructor.call(this, config);
22374     // these are provided exchanges for backwards compat
22375     // minHeight/maxHeight were replaced by growMin/growMax to be
22376     // compatible with TextField growing config values
22377     if(this.minHeight !== undefined){
22378         this.growMin = this.minHeight;
22379     }
22380     if(this.maxHeight !== undefined){
22381         this.growMax = this.maxHeight;
22382     }
22383 };
22384
22385 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22386     /**
22387      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22388      */
22389     growMin : 60,
22390     /**
22391      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22392      */
22393     growMax: 1000,
22394     /**
22395      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22396      * in the field (equivalent to setting overflow: hidden, defaults to false)
22397      */
22398     preventScrollbars: false,
22399     /**
22400      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22401      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22402      */
22403
22404     // private
22405     onRender : function(ct, position){
22406         if(!this.el){
22407             this.defaultAutoCreate = {
22408                 tag: "textarea",
22409                 style:"width:300px;height:60px;",
22410                 autocomplete: "off"
22411             };
22412         }
22413         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22414         if(this.grow){
22415             this.textSizeEl = Roo.DomHelper.append(document.body, {
22416                 tag: "pre", cls: "x-form-grow-sizer"
22417             });
22418             if(this.preventScrollbars){
22419                 this.el.setStyle("overflow", "hidden");
22420             }
22421             this.el.setHeight(this.growMin);
22422         }
22423     },
22424
22425     onDestroy : function(){
22426         if(this.textSizeEl){
22427             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22428         }
22429         Roo.form.TextArea.superclass.onDestroy.call(this);
22430     },
22431
22432     // private
22433     onKeyUp : function(e){
22434         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22435             this.autoSize();
22436         }
22437     },
22438
22439     /**
22440      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22441      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22442      */
22443     autoSize : function(){
22444         if(!this.grow || !this.textSizeEl){
22445             return;
22446         }
22447         var el = this.el;
22448         var v = el.dom.value;
22449         var ts = this.textSizeEl;
22450
22451         ts.innerHTML = '';
22452         ts.appendChild(document.createTextNode(v));
22453         v = ts.innerHTML;
22454
22455         Roo.fly(ts).setWidth(this.el.getWidth());
22456         if(v.length < 1){
22457             v = "&#160;&#160;";
22458         }else{
22459             if(Roo.isIE){
22460                 v = v.replace(/\n/g, '<p>&#160;</p>');
22461             }
22462             v += "&#160;\n&#160;";
22463         }
22464         ts.innerHTML = v;
22465         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22466         if(h != this.lastHeight){
22467             this.lastHeight = h;
22468             this.el.setHeight(h);
22469             this.fireEvent("autosize", this, h);
22470         }
22471     }
22472 });/*
22473  * Based on:
22474  * Ext JS Library 1.1.1
22475  * Copyright(c) 2006-2007, Ext JS, LLC.
22476  *
22477  * Originally Released Under LGPL - original licence link has changed is not relivant.
22478  *
22479  * Fork - LGPL
22480  * <script type="text/javascript">
22481  */
22482  
22483
22484 /**
22485  * @class Roo.form.NumberField
22486  * @extends Roo.form.TextField
22487  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22488  * @constructor
22489  * Creates a new NumberField
22490  * @param {Object} config Configuration options
22491  */
22492 Roo.form.NumberField = function(config){
22493     Roo.form.NumberField.superclass.constructor.call(this, config);
22494 };
22495
22496 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22497     /**
22498      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22499      */
22500     fieldClass: "x-form-field x-form-num-field",
22501     /**
22502      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22503      */
22504     allowDecimals : true,
22505     /**
22506      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22507      */
22508     decimalSeparator : ".",
22509     /**
22510      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22511      */
22512     decimalPrecision : 2,
22513     /**
22514      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22515      */
22516     allowNegative : true,
22517     /**
22518      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22519      */
22520     minValue : Number.NEGATIVE_INFINITY,
22521     /**
22522      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22523      */
22524     maxValue : Number.MAX_VALUE,
22525     /**
22526      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22527      */
22528     minText : "The minimum value for this field is {0}",
22529     /**
22530      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22531      */
22532     maxText : "The maximum value for this field is {0}",
22533     /**
22534      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22535      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22536      */
22537     nanText : "{0} is not a valid number",
22538
22539     // private
22540     initEvents : function(){
22541         Roo.form.NumberField.superclass.initEvents.call(this);
22542         var allowed = "0123456789";
22543         if(this.allowDecimals){
22544             allowed += this.decimalSeparator;
22545         }
22546         if(this.allowNegative){
22547             allowed += "-";
22548         }
22549         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22550         var keyPress = function(e){
22551             var k = e.getKey();
22552             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22553                 return;
22554             }
22555             var c = e.getCharCode();
22556             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22557                 e.stopEvent();
22558             }
22559         };
22560         this.el.on("keypress", keyPress, this);
22561     },
22562
22563     // private
22564     validateValue : function(value){
22565         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22566             return false;
22567         }
22568         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22569              return true;
22570         }
22571         var num = this.parseValue(value);
22572         if(isNaN(num)){
22573             this.markInvalid(String.format(this.nanText, value));
22574             return false;
22575         }
22576         if(num < this.minValue){
22577             this.markInvalid(String.format(this.minText, this.minValue));
22578             return false;
22579         }
22580         if(num > this.maxValue){
22581             this.markInvalid(String.format(this.maxText, this.maxValue));
22582             return false;
22583         }
22584         return true;
22585     },
22586
22587     getValue : function(){
22588         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22589     },
22590
22591     // private
22592     parseValue : function(value){
22593         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22594         return isNaN(value) ? '' : value;
22595     },
22596
22597     // private
22598     fixPrecision : function(value){
22599         var nan = isNaN(value);
22600         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22601             return nan ? '' : value;
22602         }
22603         return parseFloat(value).toFixed(this.decimalPrecision);
22604     },
22605
22606     setValue : function(v){
22607         v = this.fixPrecision(v);
22608         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22609     },
22610
22611     // private
22612     decimalPrecisionFcn : function(v){
22613         return Math.floor(v);
22614     },
22615
22616     beforeBlur : function(){
22617         var v = this.parseValue(this.getRawValue());
22618         if(v){
22619             this.setValue(v);
22620         }
22621     }
22622 });/*
22623  * Based on:
22624  * Ext JS Library 1.1.1
22625  * Copyright(c) 2006-2007, Ext JS, LLC.
22626  *
22627  * Originally Released Under LGPL - original licence link has changed is not relivant.
22628  *
22629  * Fork - LGPL
22630  * <script type="text/javascript">
22631  */
22632  
22633 /**
22634  * @class Roo.form.DateField
22635  * @extends Roo.form.TriggerField
22636  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22637 * @constructor
22638 * Create a new DateField
22639 * @param {Object} config
22640  */
22641 Roo.form.DateField = function(config){
22642     Roo.form.DateField.superclass.constructor.call(this, config);
22643     
22644       this.addEvents({
22645          
22646         /**
22647          * @event select
22648          * Fires when a date is selected
22649              * @param {Roo.form.DateField} combo This combo box
22650              * @param {Date} date The date selected
22651              */
22652         'select' : true
22653          
22654     });
22655     
22656     
22657     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22658     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22659     this.ddMatch = null;
22660     if(this.disabledDates){
22661         var dd = this.disabledDates;
22662         var re = "(?:";
22663         for(var i = 0; i < dd.length; i++){
22664             re += dd[i];
22665             if(i != dd.length-1) re += "|";
22666         }
22667         this.ddMatch = new RegExp(re + ")");
22668     }
22669 };
22670
22671 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22672     /**
22673      * @cfg {String} format
22674      * The default date format string which can be overriden for localization support.  The format must be
22675      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22676      */
22677     format : "m/d/y",
22678     /**
22679      * @cfg {String} altFormats
22680      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22681      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22682      */
22683     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22684     /**
22685      * @cfg {Array} disabledDays
22686      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22687      */
22688     disabledDays : null,
22689     /**
22690      * @cfg {String} disabledDaysText
22691      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22692      */
22693     disabledDaysText : "Disabled",
22694     /**
22695      * @cfg {Array} disabledDates
22696      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22697      * expression so they are very powerful. Some examples:
22698      * <ul>
22699      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22700      * <li>["03/08", "09/16"] would disable those days for every year</li>
22701      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22702      * <li>["03/../2006"] would disable every day in March 2006</li>
22703      * <li>["^03"] would disable every day in every March</li>
22704      * </ul>
22705      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22706      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22707      */
22708     disabledDates : null,
22709     /**
22710      * @cfg {String} disabledDatesText
22711      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22712      */
22713     disabledDatesText : "Disabled",
22714     /**
22715      * @cfg {Date/String} minValue
22716      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22717      * valid format (defaults to null).
22718      */
22719     minValue : null,
22720     /**
22721      * @cfg {Date/String} maxValue
22722      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22723      * valid format (defaults to null).
22724      */
22725     maxValue : null,
22726     /**
22727      * @cfg {String} minText
22728      * The error text to display when the date in the cell is before minValue (defaults to
22729      * 'The date in this field must be after {minValue}').
22730      */
22731     minText : "The date in this field must be equal to or after {0}",
22732     /**
22733      * @cfg {String} maxText
22734      * The error text to display when the date in the cell is after maxValue (defaults to
22735      * 'The date in this field must be before {maxValue}').
22736      */
22737     maxText : "The date in this field must be equal to or before {0}",
22738     /**
22739      * @cfg {String} invalidText
22740      * The error text to display when the date in the field is invalid (defaults to
22741      * '{value} is not a valid date - it must be in the format {format}').
22742      */
22743     invalidText : "{0} is not a valid date - it must be in the format {1}",
22744     /**
22745      * @cfg {String} triggerClass
22746      * An additional CSS class used to style the trigger button.  The trigger will always get the
22747      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22748      * which displays a calendar icon).
22749      */
22750     triggerClass : 'x-form-date-trigger',
22751     
22752
22753     /**
22754      * @cfg {bool} useIso
22755      * if enabled, then the date field will use a hidden field to store the 
22756      * real value as iso formated date. default (false)
22757      */ 
22758     useIso : false,
22759     /**
22760      * @cfg {String/Object} autoCreate
22761      * A DomHelper element spec, or true for a default element spec (defaults to
22762      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22763      */ 
22764     // private
22765     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22766     
22767     // private
22768     hiddenField: false,
22769     
22770     onRender : function(ct, position)
22771     {
22772         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22773         if (this.useIso) {
22774             this.el.dom.removeAttribute('name'); 
22775             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22776                     'before', true);
22777             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22778             // prevent input submission
22779             this.hiddenName = this.name;
22780         }
22781             
22782             
22783     },
22784     
22785     // private
22786     validateValue : function(value)
22787     {
22788         value = this.formatDate(value);
22789         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22790             return false;
22791         }
22792         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22793              return true;
22794         }
22795         var svalue = value;
22796         value = this.parseDate(value);
22797         if(!value){
22798             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22799             return false;
22800         }
22801         var time = value.getTime();
22802         if(this.minValue && time < this.minValue.getTime()){
22803             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22804             return false;
22805         }
22806         if(this.maxValue && time > this.maxValue.getTime()){
22807             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22808             return false;
22809         }
22810         if(this.disabledDays){
22811             var day = value.getDay();
22812             for(var i = 0; i < this.disabledDays.length; i++) {
22813                 if(day === this.disabledDays[i]){
22814                     this.markInvalid(this.disabledDaysText);
22815                     return false;
22816                 }
22817             }
22818         }
22819         var fvalue = this.formatDate(value);
22820         if(this.ddMatch && this.ddMatch.test(fvalue)){
22821             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22822             return false;
22823         }
22824         return true;
22825     },
22826
22827     // private
22828     // Provides logic to override the default TriggerField.validateBlur which just returns true
22829     validateBlur : function(){
22830         return !this.menu || !this.menu.isVisible();
22831     },
22832
22833     /**
22834      * Returns the current date value of the date field.
22835      * @return {Date} The date value
22836      */
22837     getValue : function(){
22838         
22839         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22840     },
22841
22842     /**
22843      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22844      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22845      * (the default format used is "m/d/y").
22846      * <br />Usage:
22847      * <pre><code>
22848 //All of these calls set the same date value (May 4, 2006)
22849
22850 //Pass a date object:
22851 var dt = new Date('5/4/06');
22852 dateField.setValue(dt);
22853
22854 //Pass a date string (default format):
22855 dateField.setValue('5/4/06');
22856
22857 //Pass a date string (custom format):
22858 dateField.format = 'Y-m-d';
22859 dateField.setValue('2006-5-4');
22860 </code></pre>
22861      * @param {String/Date} date The date or valid date string
22862      */
22863     setValue : function(date){
22864         if (this.hiddenField) {
22865             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22866         }
22867         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22868     },
22869
22870     // private
22871     parseDate : function(value){
22872         if(!value || value instanceof Date){
22873             return value;
22874         }
22875         var v = Date.parseDate(value, this.format);
22876         if(!v && this.altFormats){
22877             if(!this.altFormatsArray){
22878                 this.altFormatsArray = this.altFormats.split("|");
22879             }
22880             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22881                 v = Date.parseDate(value, this.altFormatsArray[i]);
22882             }
22883         }
22884         return v;
22885     },
22886
22887     // private
22888     formatDate : function(date, fmt){
22889         return (!date || !(date instanceof Date)) ?
22890                date : date.dateFormat(fmt || this.format);
22891     },
22892
22893     // private
22894     menuListeners : {
22895         select: function(m, d){
22896             this.setValue(d);
22897             this.fireEvent('select', this, d);
22898         },
22899         show : function(){ // retain focus styling
22900             this.onFocus();
22901         },
22902         hide : function(){
22903             this.focus.defer(10, this);
22904             var ml = this.menuListeners;
22905             this.menu.un("select", ml.select,  this);
22906             this.menu.un("show", ml.show,  this);
22907             this.menu.un("hide", ml.hide,  this);
22908         }
22909     },
22910
22911     // private
22912     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22913     onTriggerClick : function(){
22914         if(this.disabled){
22915             return;
22916         }
22917         if(this.menu == null){
22918             this.menu = new Roo.menu.DateMenu();
22919         }
22920         Roo.apply(this.menu.picker,  {
22921             showClear: this.allowBlank,
22922             minDate : this.minValue,
22923             maxDate : this.maxValue,
22924             disabledDatesRE : this.ddMatch,
22925             disabledDatesText : this.disabledDatesText,
22926             disabledDays : this.disabledDays,
22927             disabledDaysText : this.disabledDaysText,
22928             format : this.format,
22929             minText : String.format(this.minText, this.formatDate(this.minValue)),
22930             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22931         });
22932         this.menu.on(Roo.apply({}, this.menuListeners, {
22933             scope:this
22934         }));
22935         this.menu.picker.setValue(this.getValue() || new Date());
22936         this.menu.show(this.el, "tl-bl?");
22937     },
22938
22939     beforeBlur : function(){
22940         var v = this.parseDate(this.getRawValue());
22941         if(v){
22942             this.setValue(v);
22943         }
22944     }
22945
22946     /** @cfg {Boolean} grow @hide */
22947     /** @cfg {Number} growMin @hide */
22948     /** @cfg {Number} growMax @hide */
22949     /**
22950      * @hide
22951      * @method autoSize
22952      */
22953 });/*
22954  * Based on:
22955  * Ext JS Library 1.1.1
22956  * Copyright(c) 2006-2007, Ext JS, LLC.
22957  *
22958  * Originally Released Under LGPL - original licence link has changed is not relivant.
22959  *
22960  * Fork - LGPL
22961  * <script type="text/javascript">
22962  */
22963  
22964
22965 /**
22966  * @class Roo.form.ComboBox
22967  * @extends Roo.form.TriggerField
22968  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22969  * @constructor
22970  * Create a new ComboBox.
22971  * @param {Object} config Configuration options
22972  */
22973 Roo.form.ComboBox = function(config){
22974     Roo.form.ComboBox.superclass.constructor.call(this, config);
22975     this.addEvents({
22976         /**
22977          * @event expand
22978          * Fires when the dropdown list is expanded
22979              * @param {Roo.form.ComboBox} combo This combo box
22980              */
22981         'expand' : true,
22982         /**
22983          * @event collapse
22984          * Fires when the dropdown list is collapsed
22985              * @param {Roo.form.ComboBox} combo This combo box
22986              */
22987         'collapse' : true,
22988         /**
22989          * @event beforeselect
22990          * Fires before a list item is selected. Return false to cancel the selection.
22991              * @param {Roo.form.ComboBox} combo This combo box
22992              * @param {Roo.data.Record} record The data record returned from the underlying store
22993              * @param {Number} index The index of the selected item in the dropdown list
22994              */
22995         'beforeselect' : true,
22996         /**
22997          * @event select
22998          * Fires when a list item is selected
22999              * @param {Roo.form.ComboBox} combo This combo box
23000              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23001              * @param {Number} index The index of the selected item in the dropdown list
23002              */
23003         'select' : true,
23004         /**
23005          * @event beforequery
23006          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23007          * The event object passed has these properties:
23008              * @param {Roo.form.ComboBox} combo This combo box
23009              * @param {String} query The query
23010              * @param {Boolean} forceAll true to force "all" query
23011              * @param {Boolean} cancel true to cancel the query
23012              * @param {Object} e The query event object
23013              */
23014         'beforequery': true,
23015          /**
23016          * @event add
23017          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23018              * @param {Roo.form.ComboBox} combo This combo box
23019              */
23020         'add' : true,
23021         /**
23022          * @event edit
23023          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23024              * @param {Roo.form.ComboBox} combo This combo box
23025              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23026              */
23027         'edit' : true
23028         
23029         
23030     });
23031     if(this.transform){
23032         this.allowDomMove = false;
23033         var s = Roo.getDom(this.transform);
23034         if(!this.hiddenName){
23035             this.hiddenName = s.name;
23036         }
23037         if(!this.store){
23038             this.mode = 'local';
23039             var d = [], opts = s.options;
23040             for(var i = 0, len = opts.length;i < len; i++){
23041                 var o = opts[i];
23042                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23043                 if(o.selected) {
23044                     this.value = value;
23045                 }
23046                 d.push([value, o.text]);
23047             }
23048             this.store = new Roo.data.SimpleStore({
23049                 'id': 0,
23050                 fields: ['value', 'text'],
23051                 data : d
23052             });
23053             this.valueField = 'value';
23054             this.displayField = 'text';
23055         }
23056         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23057         if(!this.lazyRender){
23058             this.target = true;
23059             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23060             s.parentNode.removeChild(s); // remove it
23061             this.render(this.el.parentNode);
23062         }else{
23063             s.parentNode.removeChild(s); // remove it
23064         }
23065
23066     }
23067     if (this.store) {
23068         this.store = Roo.factory(this.store, Roo.data);
23069     }
23070     
23071     this.selectedIndex = -1;
23072     if(this.mode == 'local'){
23073         if(config.queryDelay === undefined){
23074             this.queryDelay = 10;
23075         }
23076         if(config.minChars === undefined){
23077             this.minChars = 0;
23078         }
23079     }
23080 };
23081
23082 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23083     /**
23084      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23085      */
23086     /**
23087      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23088      * rendering into an Roo.Editor, defaults to false)
23089      */
23090     /**
23091      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23092      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23093      */
23094     /**
23095      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23096      */
23097     /**
23098      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23099      * the dropdown list (defaults to undefined, with no header element)
23100      */
23101
23102      /**
23103      * @cfg {String/Roo.Template} tpl The template to use to render the output
23104      */
23105      
23106     // private
23107     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23108     /**
23109      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23110      */
23111     listWidth: undefined,
23112     /**
23113      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23114      * mode = 'remote' or 'text' if mode = 'local')
23115      */
23116     displayField: undefined,
23117     /**
23118      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23119      * mode = 'remote' or 'value' if mode = 'local'). 
23120      * Note: use of a valueField requires the user make a selection
23121      * in order for a value to be mapped.
23122      */
23123     valueField: undefined,
23124     
23125     
23126     /**
23127      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23128      * field's data value (defaults to the underlying DOM element's name)
23129      */
23130     hiddenName: undefined,
23131     /**
23132      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23133      */
23134     listClass: '',
23135     /**
23136      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23137      */
23138     selectedClass: 'x-combo-selected',
23139     /**
23140      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23141      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23142      * which displays a downward arrow icon).
23143      */
23144     triggerClass : 'x-form-arrow-trigger',
23145     /**
23146      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23147      */
23148     shadow:'sides',
23149     /**
23150      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23151      * anchor positions (defaults to 'tl-bl')
23152      */
23153     listAlign: 'tl-bl?',
23154     /**
23155      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23156      */
23157     maxHeight: 300,
23158     /**
23159      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23160      * query specified by the allQuery config option (defaults to 'query')
23161      */
23162     triggerAction: 'query',
23163     /**
23164      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23165      * (defaults to 4, does not apply if editable = false)
23166      */
23167     minChars : 4,
23168     /**
23169      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23170      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23171      */
23172     typeAhead: false,
23173     /**
23174      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23175      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23176      */
23177     queryDelay: 500,
23178     /**
23179      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23180      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23181      */
23182     pageSize: 0,
23183     /**
23184      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23185      * when editable = true (defaults to false)
23186      */
23187     selectOnFocus:false,
23188     /**
23189      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23190      */
23191     queryParam: 'query',
23192     /**
23193      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23194      * when mode = 'remote' (defaults to 'Loading...')
23195      */
23196     loadingText: 'Loading...',
23197     /**
23198      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23199      */
23200     resizable: false,
23201     /**
23202      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23203      */
23204     handleHeight : 8,
23205     /**
23206      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23207      * traditional select (defaults to true)
23208      */
23209     editable: true,
23210     /**
23211      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23212      */
23213     allQuery: '',
23214     /**
23215      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23216      */
23217     mode: 'remote',
23218     /**
23219      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23220      * listWidth has a higher value)
23221      */
23222     minListWidth : 70,
23223     /**
23224      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23225      * allow the user to set arbitrary text into the field (defaults to false)
23226      */
23227     forceSelection:false,
23228     /**
23229      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23230      * if typeAhead = true (defaults to 250)
23231      */
23232     typeAheadDelay : 250,
23233     /**
23234      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23235      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23236      */
23237     valueNotFoundText : undefined,
23238     /**
23239      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23240      */
23241     blockFocus : false,
23242     
23243     /**
23244      * @cfg {Boolean} disableClear Disable showing of clear button.
23245      */
23246     disableClear : false,
23247     /**
23248      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23249      */
23250     alwaysQuery : false,
23251     
23252     //private
23253     addicon : false,
23254     editicon: false,
23255     
23256     // element that contains real text value.. (when hidden is used..)
23257      
23258     // private
23259     onRender : function(ct, position){
23260         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23261         if(this.hiddenName){
23262             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23263                     'before', true);
23264             this.hiddenField.value =
23265                 this.hiddenValue !== undefined ? this.hiddenValue :
23266                 this.value !== undefined ? this.value : '';
23267
23268             // prevent input submission
23269             this.el.dom.removeAttribute('name');
23270              
23271              
23272         }
23273         if(Roo.isGecko){
23274             this.el.dom.setAttribute('autocomplete', 'off');
23275         }
23276
23277         var cls = 'x-combo-list';
23278
23279         this.list = new Roo.Layer({
23280             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23281         });
23282
23283         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23284         this.list.setWidth(lw);
23285         this.list.swallowEvent('mousewheel');
23286         this.assetHeight = 0;
23287
23288         if(this.title){
23289             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23290             this.assetHeight += this.header.getHeight();
23291         }
23292
23293         this.innerList = this.list.createChild({cls:cls+'-inner'});
23294         this.innerList.on('mouseover', this.onViewOver, this);
23295         this.innerList.on('mousemove', this.onViewMove, this);
23296         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23297         
23298         if(this.allowBlank && !this.pageSize && !this.disableClear){
23299             this.footer = this.list.createChild({cls:cls+'-ft'});
23300             this.pageTb = new Roo.Toolbar(this.footer);
23301            
23302         }
23303         if(this.pageSize){
23304             this.footer = this.list.createChild({cls:cls+'-ft'});
23305             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23306                     {pageSize: this.pageSize});
23307             
23308         }
23309         
23310         if (this.pageTb && this.allowBlank && !this.disableClear) {
23311             var _this = this;
23312             this.pageTb.add(new Roo.Toolbar.Fill(), {
23313                 cls: 'x-btn-icon x-btn-clear',
23314                 text: '&#160;',
23315                 handler: function()
23316                 {
23317                     _this.collapse();
23318                     _this.clearValue();
23319                     _this.onSelect(false, -1);
23320                 }
23321             });
23322         }
23323         if (this.footer) {
23324             this.assetHeight += this.footer.getHeight();
23325         }
23326         
23327
23328         if(!this.tpl){
23329             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23330         }
23331
23332         this.view = new Roo.View(this.innerList, this.tpl, {
23333             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23334         });
23335
23336         this.view.on('click', this.onViewClick, this);
23337
23338         this.store.on('beforeload', this.onBeforeLoad, this);
23339         this.store.on('load', this.onLoad, this);
23340         this.store.on('loadexception', this.onLoadException, this);
23341
23342         if(this.resizable){
23343             this.resizer = new Roo.Resizable(this.list,  {
23344                pinned:true, handles:'se'
23345             });
23346             this.resizer.on('resize', function(r, w, h){
23347                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23348                 this.listWidth = w;
23349                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23350                 this.restrictHeight();
23351             }, this);
23352             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23353         }
23354         if(!this.editable){
23355             this.editable = true;
23356             this.setEditable(false);
23357         }  
23358         
23359         
23360         if (typeof(this.events.add.listeners) != 'undefined') {
23361             
23362             this.addicon = this.wrap.createChild(
23363                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23364        
23365             this.addicon.on('click', function(e) {
23366                 this.fireEvent('add', this);
23367             }, this);
23368         }
23369         if (typeof(this.events.edit.listeners) != 'undefined') {
23370             
23371             this.editicon = this.wrap.createChild(
23372                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23373             if (this.addicon) {
23374                 this.editicon.setStyle('margin-left', '40px');
23375             }
23376             this.editicon.on('click', function(e) {
23377                 
23378                 // we fire even  if inothing is selected..
23379                 this.fireEvent('edit', this, this.lastData );
23380                 
23381             }, this);
23382         }
23383         
23384         
23385         
23386     },
23387
23388     // private
23389     initEvents : function(){
23390         Roo.form.ComboBox.superclass.initEvents.call(this);
23391
23392         this.keyNav = new Roo.KeyNav(this.el, {
23393             "up" : function(e){
23394                 this.inKeyMode = true;
23395                 this.selectPrev();
23396             },
23397
23398             "down" : function(e){
23399                 if(!this.isExpanded()){
23400                     this.onTriggerClick();
23401                 }else{
23402                     this.inKeyMode = true;
23403                     this.selectNext();
23404                 }
23405             },
23406
23407             "enter" : function(e){
23408                 this.onViewClick();
23409                 //return true;
23410             },
23411
23412             "esc" : function(e){
23413                 this.collapse();
23414             },
23415
23416             "tab" : function(e){
23417                 this.onViewClick(false);
23418                 this.fireEvent("specialkey", this, e);
23419                 return true;
23420             },
23421
23422             scope : this,
23423
23424             doRelay : function(foo, bar, hname){
23425                 if(hname == 'down' || this.scope.isExpanded()){
23426                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23427                 }
23428                 return true;
23429             },
23430
23431             forceKeyDown: true
23432         });
23433         this.queryDelay = Math.max(this.queryDelay || 10,
23434                 this.mode == 'local' ? 10 : 250);
23435         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23436         if(this.typeAhead){
23437             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23438         }
23439         if(this.editable !== false){
23440             this.el.on("keyup", this.onKeyUp, this);
23441         }
23442         if(this.forceSelection){
23443             this.on('blur', this.doForce, this);
23444         }
23445     },
23446
23447     onDestroy : function(){
23448         if(this.view){
23449             this.view.setStore(null);
23450             this.view.el.removeAllListeners();
23451             this.view.el.remove();
23452             this.view.purgeListeners();
23453         }
23454         if(this.list){
23455             this.list.destroy();
23456         }
23457         if(this.store){
23458             this.store.un('beforeload', this.onBeforeLoad, this);
23459             this.store.un('load', this.onLoad, this);
23460             this.store.un('loadexception', this.onLoadException, this);
23461         }
23462         Roo.form.ComboBox.superclass.onDestroy.call(this);
23463     },
23464
23465     // private
23466     fireKey : function(e){
23467         if(e.isNavKeyPress() && !this.list.isVisible()){
23468             this.fireEvent("specialkey", this, e);
23469         }
23470     },
23471
23472     // private
23473     onResize: function(w, h){
23474         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23475         
23476         if(typeof w != 'number'){
23477             // we do not handle it!?!?
23478             return;
23479         }
23480         var tw = this.trigger.getWidth();
23481         tw += this.addicon ? this.addicon.getWidth() : 0;
23482         tw += this.editicon ? this.editicon.getWidth() : 0;
23483         var x = w - tw;
23484         this.el.setWidth( this.adjustWidth('input', x));
23485             
23486         this.trigger.setStyle('left', x+'px');
23487         
23488         if(this.list && this.listWidth === undefined){
23489             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23490             this.list.setWidth(lw);
23491             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23492         }
23493         
23494     
23495         
23496     },
23497
23498     /**
23499      * Allow or prevent the user from directly editing the field text.  If false is passed,
23500      * the user will only be able to select from the items defined in the dropdown list.  This method
23501      * is the runtime equivalent of setting the 'editable' config option at config time.
23502      * @param {Boolean} value True to allow the user to directly edit the field text
23503      */
23504     setEditable : function(value){
23505         if(value == this.editable){
23506             return;
23507         }
23508         this.editable = value;
23509         if(!value){
23510             this.el.dom.setAttribute('readOnly', true);
23511             this.el.on('mousedown', this.onTriggerClick,  this);
23512             this.el.addClass('x-combo-noedit');
23513         }else{
23514             this.el.dom.setAttribute('readOnly', false);
23515             this.el.un('mousedown', this.onTriggerClick,  this);
23516             this.el.removeClass('x-combo-noedit');
23517         }
23518     },
23519
23520     // private
23521     onBeforeLoad : function(){
23522         if(!this.hasFocus){
23523             return;
23524         }
23525         this.innerList.update(this.loadingText ?
23526                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23527         this.restrictHeight();
23528         this.selectedIndex = -1;
23529     },
23530
23531     // private
23532     onLoad : function(){
23533         if(!this.hasFocus){
23534             return;
23535         }
23536         if(this.store.getCount() > 0){
23537             this.expand();
23538             this.restrictHeight();
23539             if(this.lastQuery == this.allQuery){
23540                 if(this.editable){
23541                     this.el.dom.select();
23542                 }
23543                 if(!this.selectByValue(this.value, true)){
23544                     this.select(0, true);
23545                 }
23546             }else{
23547                 this.selectNext();
23548                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23549                     this.taTask.delay(this.typeAheadDelay);
23550                 }
23551             }
23552         }else{
23553             this.onEmptyResults();
23554         }
23555         //this.el.focus();
23556     },
23557     // private
23558     onLoadException : function()
23559     {
23560         this.collapse();
23561         Roo.log(this.store.reader.jsonData);
23562         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23563             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23564         }
23565         
23566         
23567     },
23568     // private
23569     onTypeAhead : function(){
23570         if(this.store.getCount() > 0){
23571             var r = this.store.getAt(0);
23572             var newValue = r.data[this.displayField];
23573             var len = newValue.length;
23574             var selStart = this.getRawValue().length;
23575             if(selStart != len){
23576                 this.setRawValue(newValue);
23577                 this.selectText(selStart, newValue.length);
23578             }
23579         }
23580     },
23581
23582     // private
23583     onSelect : function(record, index){
23584         if(this.fireEvent('beforeselect', this, record, index) !== false){
23585             this.setFromData(index > -1 ? record.data : false);
23586             this.collapse();
23587             this.fireEvent('select', this, record, index);
23588         }
23589     },
23590
23591     /**
23592      * Returns the currently selected field value or empty string if no value is set.
23593      * @return {String} value The selected value
23594      */
23595     getValue : function(){
23596         if(this.valueField){
23597             return typeof this.value != 'undefined' ? this.value : '';
23598         }else{
23599             return Roo.form.ComboBox.superclass.getValue.call(this);
23600         }
23601     },
23602
23603     /**
23604      * Clears any text/value currently set in the field
23605      */
23606     clearValue : function(){
23607         if(this.hiddenField){
23608             this.hiddenField.value = '';
23609         }
23610         this.value = '';
23611         this.setRawValue('');
23612         this.lastSelectionText = '';
23613         this.applyEmptyText();
23614     },
23615
23616     /**
23617      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23618      * will be displayed in the field.  If the value does not match the data value of an existing item,
23619      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23620      * Otherwise the field will be blank (although the value will still be set).
23621      * @param {String} value The value to match
23622      */
23623     setValue : function(v){
23624         var text = v;
23625         if(this.valueField){
23626             var r = this.findRecord(this.valueField, v);
23627             if(r){
23628                 text = r.data[this.displayField];
23629             }else if(this.valueNotFoundText !== undefined){
23630                 text = this.valueNotFoundText;
23631             }
23632         }
23633         this.lastSelectionText = text;
23634         if(this.hiddenField){
23635             this.hiddenField.value = v;
23636         }
23637         Roo.form.ComboBox.superclass.setValue.call(this, text);
23638         this.value = v;
23639     },
23640     /**
23641      * @property {Object} the last set data for the element
23642      */
23643     
23644     lastData : false,
23645     /**
23646      * Sets the value of the field based on a object which is related to the record format for the store.
23647      * @param {Object} value the value to set as. or false on reset?
23648      */
23649     setFromData : function(o){
23650         var dv = ''; // display value
23651         var vv = ''; // value value..
23652         this.lastData = o;
23653         if (this.displayField) {
23654             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23655         } else {
23656             // this is an error condition!!!
23657             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23658         }
23659         
23660         if(this.valueField){
23661             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23662         }
23663         if(this.hiddenField){
23664             this.hiddenField.value = vv;
23665             
23666             this.lastSelectionText = dv;
23667             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23668             this.value = vv;
23669             return;
23670         }
23671         // no hidden field.. - we store the value in 'value', but still display
23672         // display field!!!!
23673         this.lastSelectionText = dv;
23674         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23675         this.value = vv;
23676         
23677         
23678     },
23679     // private
23680     reset : function(){
23681         // overridden so that last data is reset..
23682         this.setValue(this.originalValue);
23683         this.clearInvalid();
23684         this.lastData = false;
23685     },
23686     // private
23687     findRecord : function(prop, value){
23688         var record;
23689         if(this.store.getCount() > 0){
23690             this.store.each(function(r){
23691                 if(r.data[prop] == value){
23692                     record = r;
23693                     return false;
23694                 }
23695                 return true;
23696             });
23697         }
23698         return record;
23699     },
23700     
23701     getName: function()
23702     {
23703         // returns hidden if it's set..
23704         if (!this.rendered) {return ''};
23705         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23706         
23707     },
23708     // private
23709     onViewMove : function(e, t){
23710         this.inKeyMode = false;
23711     },
23712
23713     // private
23714     onViewOver : function(e, t){
23715         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23716             return;
23717         }
23718         var item = this.view.findItemFromChild(t);
23719         if(item){
23720             var index = this.view.indexOf(item);
23721             this.select(index, false);
23722         }
23723     },
23724
23725     // private
23726     onViewClick : function(doFocus)
23727     {
23728         var index = this.view.getSelectedIndexes()[0];
23729         var r = this.store.getAt(index);
23730         if(r){
23731             this.onSelect(r, index);
23732         }
23733         if(doFocus !== false && !this.blockFocus){
23734             this.el.focus();
23735         }
23736     },
23737
23738     // private
23739     restrictHeight : function(){
23740         this.innerList.dom.style.height = '';
23741         var inner = this.innerList.dom;
23742         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23743         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23744         this.list.beginUpdate();
23745         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23746         this.list.alignTo(this.el, this.listAlign);
23747         this.list.endUpdate();
23748     },
23749
23750     // private
23751     onEmptyResults : function(){
23752         this.collapse();
23753     },
23754
23755     /**
23756      * Returns true if the dropdown list is expanded, else false.
23757      */
23758     isExpanded : function(){
23759         return this.list.isVisible();
23760     },
23761
23762     /**
23763      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23764      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23765      * @param {String} value The data value of the item to select
23766      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23767      * selected item if it is not currently in view (defaults to true)
23768      * @return {Boolean} True if the value matched an item in the list, else false
23769      */
23770     selectByValue : function(v, scrollIntoView){
23771         if(v !== undefined && v !== null){
23772             var r = this.findRecord(this.valueField || this.displayField, v);
23773             if(r){
23774                 this.select(this.store.indexOf(r), scrollIntoView);
23775                 return true;
23776             }
23777         }
23778         return false;
23779     },
23780
23781     /**
23782      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23783      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23784      * @param {Number} index The zero-based index of the list item to select
23785      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23786      * selected item if it is not currently in view (defaults to true)
23787      */
23788     select : function(index, scrollIntoView){
23789         this.selectedIndex = index;
23790         this.view.select(index);
23791         if(scrollIntoView !== false){
23792             var el = this.view.getNode(index);
23793             if(el){
23794                 this.innerList.scrollChildIntoView(el, false);
23795             }
23796         }
23797     },
23798
23799     // private
23800     selectNext : function(){
23801         var ct = this.store.getCount();
23802         if(ct > 0){
23803             if(this.selectedIndex == -1){
23804                 this.select(0);
23805             }else if(this.selectedIndex < ct-1){
23806                 this.select(this.selectedIndex+1);
23807             }
23808         }
23809     },
23810
23811     // private
23812     selectPrev : function(){
23813         var ct = this.store.getCount();
23814         if(ct > 0){
23815             if(this.selectedIndex == -1){
23816                 this.select(0);
23817             }else if(this.selectedIndex != 0){
23818                 this.select(this.selectedIndex-1);
23819             }
23820         }
23821     },
23822
23823     // private
23824     onKeyUp : function(e){
23825         if(this.editable !== false && !e.isSpecialKey()){
23826             this.lastKey = e.getKey();
23827             this.dqTask.delay(this.queryDelay);
23828         }
23829     },
23830
23831     // private
23832     validateBlur : function(){
23833         return !this.list || !this.list.isVisible();   
23834     },
23835
23836     // private
23837     initQuery : function(){
23838         this.doQuery(this.getRawValue());
23839     },
23840
23841     // private
23842     doForce : function(){
23843         if(this.el.dom.value.length > 0){
23844             this.el.dom.value =
23845                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23846             this.applyEmptyText();
23847         }
23848     },
23849
23850     /**
23851      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23852      * query allowing the query action to be canceled if needed.
23853      * @param {String} query The SQL query to execute
23854      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23855      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23856      * saved in the current store (defaults to false)
23857      */
23858     doQuery : function(q, forceAll){
23859         if(q === undefined || q === null){
23860             q = '';
23861         }
23862         var qe = {
23863             query: q,
23864             forceAll: forceAll,
23865             combo: this,
23866             cancel:false
23867         };
23868         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23869             return false;
23870         }
23871         q = qe.query;
23872         forceAll = qe.forceAll;
23873         if(forceAll === true || (q.length >= this.minChars)){
23874             if(this.lastQuery != q || this.alwaysQuery){
23875                 this.lastQuery = q;
23876                 if(this.mode == 'local'){
23877                     this.selectedIndex = -1;
23878                     if(forceAll){
23879                         this.store.clearFilter();
23880                     }else{
23881                         this.store.filter(this.displayField, q);
23882                     }
23883                     this.onLoad();
23884                 }else{
23885                     this.store.baseParams[this.queryParam] = q;
23886                     this.store.load({
23887                         params: this.getParams(q)
23888                     });
23889                     this.expand();
23890                 }
23891             }else{
23892                 this.selectedIndex = -1;
23893                 this.onLoad();   
23894             }
23895         }
23896     },
23897
23898     // private
23899     getParams : function(q){
23900         var p = {};
23901         //p[this.queryParam] = q;
23902         if(this.pageSize){
23903             p.start = 0;
23904             p.limit = this.pageSize;
23905         }
23906         return p;
23907     },
23908
23909     /**
23910      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23911      */
23912     collapse : function(){
23913         if(!this.isExpanded()){
23914             return;
23915         }
23916         this.list.hide();
23917         Roo.get(document).un('mousedown', this.collapseIf, this);
23918         Roo.get(document).un('mousewheel', this.collapseIf, this);
23919         if (!this.editable) {
23920             Roo.get(document).un('keydown', this.listKeyPress, this);
23921         }
23922         this.fireEvent('collapse', this);
23923     },
23924
23925     // private
23926     collapseIf : function(e){
23927         if(!e.within(this.wrap) && !e.within(this.list)){
23928             this.collapse();
23929         }
23930     },
23931
23932     /**
23933      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23934      */
23935     expand : function(){
23936         if(this.isExpanded() || !this.hasFocus){
23937             return;
23938         }
23939         this.list.alignTo(this.el, this.listAlign);
23940         this.list.show();
23941         Roo.get(document).on('mousedown', this.collapseIf, this);
23942         Roo.get(document).on('mousewheel', this.collapseIf, this);
23943         if (!this.editable) {
23944             Roo.get(document).on('keydown', this.listKeyPress, this);
23945         }
23946         
23947         this.fireEvent('expand', this);
23948     },
23949
23950     // private
23951     // Implements the default empty TriggerField.onTriggerClick function
23952     onTriggerClick : function(){
23953         if(this.disabled){
23954             return;
23955         }
23956         if(this.isExpanded()){
23957             this.collapse();
23958             if (!this.blockFocus) {
23959                 this.el.focus();
23960             }
23961             
23962         }else {
23963             this.hasFocus = true;
23964             if(this.triggerAction == 'all') {
23965                 this.doQuery(this.allQuery, true);
23966             } else {
23967                 this.doQuery(this.getRawValue());
23968             }
23969             if (!this.blockFocus) {
23970                 this.el.focus();
23971             }
23972         }
23973     },
23974     listKeyPress : function(e)
23975     {
23976         //Roo.log('listkeypress');
23977         // scroll to first matching element based on key pres..
23978         if (e.isSpecialKey()) {
23979             return false;
23980         }
23981         var k = String.fromCharCode(e.getKey()).toUpperCase();
23982         //Roo.log(k);
23983         var match  = false;
23984         var csel = this.view.getSelectedNodes();
23985         var cselitem = false;
23986         if (csel.length) {
23987             var ix = this.view.indexOf(csel[0]);
23988             cselitem  = this.store.getAt(ix);
23989             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23990                 cselitem = false;
23991             }
23992             
23993         }
23994         
23995         this.store.each(function(v) { 
23996             if (cselitem) {
23997                 // start at existing selection.
23998                 if (cselitem.id == v.id) {
23999                     cselitem = false;
24000                 }
24001                 return;
24002             }
24003                 
24004             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24005                 match = this.store.indexOf(v);
24006                 return false;
24007             }
24008         }, this);
24009         
24010         if (match === false) {
24011             return true; // no more action?
24012         }
24013         // scroll to?
24014         this.view.select(match);
24015         var sn = Roo.get(this.view.getSelectedNodes()[0])
24016         sn.scrollIntoView(sn.dom.parentNode, false);
24017     }
24018
24019     /** 
24020     * @cfg {Boolean} grow 
24021     * @hide 
24022     */
24023     /** 
24024     * @cfg {Number} growMin 
24025     * @hide 
24026     */
24027     /** 
24028     * @cfg {Number} growMax 
24029     * @hide 
24030     */
24031     /**
24032      * @hide
24033      * @method autoSize
24034      */
24035 });/*
24036  * Copyright(c) 2010-2012, Roo J Solutions Limited
24037  *
24038  * Licence LGPL
24039  *
24040  */
24041
24042 /**
24043  * @class Roo.form.ComboBoxArray
24044  * @extends Roo.form.TextField
24045  * A facebook style adder... for lists of email / people / countries  etc...
24046  * pick multiple items from a combo box, and shows each one.
24047  *
24048  *  Fred [x]  Brian [x]  [Pick another |v]
24049  *
24050  *
24051  *  For this to work: it needs various extra information
24052  *    - normal combo problay has
24053  *      name, hiddenName
24054  *    + displayField, valueField
24055  *
24056  *    For our purpose...
24057  *
24058  *
24059  *   If we change from 'extends' to wrapping...
24060  *   
24061  *  
24062  *
24063  
24064  
24065  * @constructor
24066  * Create a new ComboBoxArray.
24067  * @param {Object} config Configuration options
24068  */
24069  
24070
24071 Roo.form.ComboBoxArray = function(config)
24072 {
24073     
24074     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24075     
24076     this.items = new Roo.util.MixedCollection(false);
24077     
24078     // construct the child combo...
24079     
24080     
24081     
24082     
24083    
24084     
24085 }
24086
24087  
24088 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24089
24090     /**
24091      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24092      */
24093     
24094     lastData : false,
24095     
24096     // behavies liek a hiddne field
24097     inputType:      'hidden',
24098     /**
24099      * @cfg {Number} width The width of the box that displays the selected element
24100      */ 
24101     width:          300,
24102
24103     
24104     
24105     /**
24106      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24107      */
24108     name : false,
24109     /**
24110      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24111      */
24112     hiddenName : false,
24113     
24114     
24115     // private the array of items that are displayed..
24116     items  : false,
24117     // private - the hidden field el.
24118     hiddenEl : false,
24119     // private - the filed el..
24120     el : false,
24121     
24122     //validateValue : function() { return true; }, // all values are ok!
24123     //onAddClick: function() { },
24124     
24125     onRender : function(ct, position) 
24126     {
24127         
24128         // create the standard hidden element
24129         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24130         
24131         
24132         // give fake names to child combo;
24133         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24134         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24135         
24136         this.combo = Roo.factory(this.combo, Roo.form);
24137         this.combo.onRender(ct, position);
24138         
24139         // assigned so form know we need to do this..
24140         this.store          = this.combo.store;
24141         this.valueField     = this.combo.valueField;
24142         this.displayField   = this.combo.displayField ;
24143         
24144         
24145         this.combo.wrap.addClass('x-cbarray-grp');
24146         
24147         var cbwrap = this.combo.wrap.createChild(
24148             {tag: 'div', cls: 'x-cbarray-cb'},
24149             this.combo.el.dom
24150         );
24151         
24152              
24153         this.hiddenEl = this.combo.wrap.createChild({
24154             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24155         });
24156         this.el = this.combo.wrap.createChild({
24157             tag: 'input',  type:'hidden' , name: this.name, value : ''
24158         });
24159          //   this.el.dom.removeAttribute("name");
24160         
24161         
24162         this.outerWrap = this.combo.wrap;
24163         this.wrap = cbwrap;
24164         
24165         this.outerWrap.setWidth(this.width);
24166         this.outerWrap.dom.removeChild(this.el.dom);
24167         
24168         this.wrap.dom.appendChild(this.el.dom);
24169         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24170         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24171         
24172         this.combo.trigger.setStyle('position','relative');
24173         this.combo.trigger.setStyle('left', '0px');
24174         this.combo.trigger.setStyle('top', '2px');
24175         
24176         this.combo.el.setStyle('vertical-align', 'text-bottom');
24177         
24178         //this.trigger.setStyle('vertical-align', 'top');
24179         
24180         // this should use the code from combo really... on('add' ....)
24181         if (this.adder) {
24182             
24183         
24184             this.adder = this.outerWrap.createChild(
24185                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24186             var _t = this;
24187             this.adder.on('click', function(e) {
24188                 _t.fireEvent('adderclick', this, e);
24189             }, _t);
24190         }
24191         //var _t = this;
24192         //this.adder.on('click', this.onAddClick, _t);
24193         
24194         
24195         this.combo.on('select', function(cb, rec, ix) {
24196             this.addItem(rec.data);
24197             
24198             cb.setValue('');
24199             cb.el.dom.value = '';
24200             //cb.lastData = rec.data;
24201             // add to list
24202             
24203         }, this);
24204         
24205         
24206     },
24207     
24208     
24209     getName: function()
24210     {
24211         // returns hidden if it's set..
24212         if (!this.rendered) {return ''};
24213         return  this.hiddenName ? this.hiddenName : this.name;
24214         
24215     },
24216     
24217     
24218     onResize: function(w, h){
24219         
24220         return;
24221         // not sure if this is needed..
24222         //this.combo.onResize(w,h);
24223         
24224         if(typeof w != 'number'){
24225             // we do not handle it!?!?
24226             return;
24227         }
24228         var tw = this.combo.trigger.getWidth();
24229         tw += this.addicon ? this.addicon.getWidth() : 0;
24230         tw += this.editicon ? this.editicon.getWidth() : 0;
24231         var x = w - tw;
24232         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24233             
24234         this.combo.trigger.setStyle('left', '0px');
24235         
24236         if(this.list && this.listWidth === undefined){
24237             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24238             this.list.setWidth(lw);
24239             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24240         }
24241         
24242     
24243         
24244     },
24245     
24246     addItem: function(rec)
24247     {
24248         var valueField = this.combo.valueField;
24249         var displayField = this.combo.displayField;
24250         if (this.items.indexOfKey(rec[valueField]) > -1) {
24251             //console.log("GOT " + rec.data.id);
24252             return;
24253         }
24254         
24255         var x = new Roo.form.ComboBoxArray.Item({
24256             //id : rec[this.idField],
24257             data : rec,
24258             displayField : displayField ,
24259             tipField : displayField ,
24260             cb : this
24261         });
24262         // use the 
24263         this.items.add(rec[valueField],x);
24264         // add it before the element..
24265         this.updateHiddenEl();
24266         x.render(this.outerWrap, this.wrap.dom);
24267         // add the image handler..
24268     },
24269     
24270     updateHiddenEl : function()
24271     {
24272         this.validate();
24273         if (!this.hiddenEl) {
24274             return;
24275         }
24276         var ar = [];
24277         var idField = this.combo.valueField;
24278         
24279         this.items.each(function(f) {
24280             ar.push(f.data[idField]);
24281            
24282         });
24283         this.hiddenEl.dom.value = ar.join(',');
24284         this.validate();
24285     },
24286     
24287     reset : function()
24288     {
24289         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24290         this.items.each(function(f) {
24291            f.remove(); 
24292         });
24293         this.el.dom.value = '';
24294         if (this.hiddenEl) {
24295             this.hiddenEl.dom.value = '';
24296         }
24297         
24298     },
24299     getValue: function()
24300     {
24301         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24302     },
24303     setValue: function(v) // not a valid action - must use addItems..
24304     {
24305          
24306         this.reset();
24307         
24308         
24309         
24310         if (this.store.isLocal && (typeof(v) == 'string')) {
24311             // then we can use the store to find the values..
24312             // comma seperated at present.. this needs to allow JSON based encoding..
24313             this.hiddenEl.value  = v;
24314             var v_ar = [];
24315             Roo.each(v.split(','), function(k) {
24316                 Roo.log("CHECK " + this.valueField + ',' + k);
24317                 var li = this.store.query(this.valueField, k);
24318                 if (!li.length) {
24319                     return;
24320                 }
24321                 add = {};
24322                 add[this.valueField] = k;
24323                 add[this.displayField] = li.item(0).data[this.displayField];
24324                 
24325                 this.addItem(add);
24326             }, this) 
24327              
24328         }
24329         if (typeof(v) == 'object') {
24330             // then let's assume it's an array of objects..
24331             Roo.each(v, function(l) {
24332                 this.addItem(l);
24333             }, this);
24334              
24335         }
24336         
24337         
24338     },
24339     setFromData: function(v)
24340     {
24341         // this recieves an object, if setValues is called.
24342         this.reset();
24343         this.el.dom.value = v[this.displayField];
24344         this.hiddenEl.dom.value = v[this.valueField];
24345         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24346             return;
24347         }
24348         var kv = v[this.valueField];
24349         var dv = v[this.displayField];
24350         kv = typeof(kv) != 'string' ? '' : kv;
24351         dv = typeof(dv) != 'string' ? '' : dv;
24352         
24353         
24354         var keys = kv.split(',');
24355         var display = dv.split(',');
24356         for (var i = 0 ; i < keys.length; i++) {
24357             
24358             add = {};
24359             add[this.valueField] = keys[i];
24360             add[this.displayField] = display[i];
24361             this.addItem(add);
24362         }
24363       
24364         
24365     },
24366     
24367     
24368     validateValue : function(value){
24369         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24370         
24371     }
24372     
24373 });
24374
24375
24376
24377 /**
24378  * @class Roo.form.ComboBoxArray.Item
24379  * @extends Roo.BoxComponent
24380  * A selected item in the list
24381  *  Fred [x]  Brian [x]  [Pick another |v]
24382  * 
24383  * @constructor
24384  * Create a new item.
24385  * @param {Object} config Configuration options
24386  */
24387  
24388 Roo.form.ComboBoxArray.Item = function(config) {
24389     config.id = Roo.id();
24390     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24391 }
24392
24393 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24394     data : {},
24395     cb: false,
24396     displayField : false,
24397     tipField : false,
24398     
24399     
24400     defaultAutoCreate : {
24401         tag: 'div',
24402         cls: 'x-cbarray-item',
24403         cn : [ 
24404             { tag: 'div' },
24405             {
24406                 tag: 'img',
24407                 width:16,
24408                 height : 16,
24409                 src : Roo.BLANK_IMAGE_URL ,
24410                 align: 'center'
24411             }
24412         ]
24413         
24414     },
24415     
24416  
24417     onRender : function(ct, position)
24418     {
24419         Roo.form.Field.superclass.onRender.call(this, ct, position);
24420         
24421         if(!this.el){
24422             var cfg = this.getAutoCreate();
24423             this.el = ct.createChild(cfg, position);
24424         }
24425         
24426         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24427         
24428         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24429             this.cb.renderer(this.data) :
24430             String.format('{0}',this.data[this.displayField]);
24431         
24432             
24433         this.el.child('div').dom.setAttribute('qtip',
24434                         String.format('{0}',this.data[this.tipField])
24435         );
24436         
24437         this.el.child('img').on('click', this.remove, this);
24438         
24439     },
24440    
24441     remove : function()
24442     {
24443         
24444         this.cb.items.remove(this);
24445         this.el.child('img').un('click', this.remove, this);
24446         this.el.remove();
24447         this.cb.updateHiddenEl();
24448     }
24449     
24450     
24451 });/*
24452  * Based on:
24453  * Ext JS Library 1.1.1
24454  * Copyright(c) 2006-2007, Ext JS, LLC.
24455  *
24456  * Originally Released Under LGPL - original licence link has changed is not relivant.
24457  *
24458  * Fork - LGPL
24459  * <script type="text/javascript">
24460  */
24461 /**
24462  * @class Roo.form.Checkbox
24463  * @extends Roo.form.Field
24464  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24465  * @constructor
24466  * Creates a new Checkbox
24467  * @param {Object} config Configuration options
24468  */
24469 Roo.form.Checkbox = function(config){
24470     Roo.form.Checkbox.superclass.constructor.call(this, config);
24471     this.addEvents({
24472         /**
24473          * @event check
24474          * Fires when the checkbox is checked or unchecked.
24475              * @param {Roo.form.Checkbox} this This checkbox
24476              * @param {Boolean} checked The new checked value
24477              */
24478         check : true
24479     });
24480 };
24481
24482 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24483     /**
24484      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24485      */
24486     focusClass : undefined,
24487     /**
24488      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24489      */
24490     fieldClass: "x-form-field",
24491     /**
24492      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24493      */
24494     checked: false,
24495     /**
24496      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24497      * {tag: "input", type: "checkbox", autocomplete: "off"})
24498      */
24499     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24500     /**
24501      * @cfg {String} boxLabel The text that appears beside the checkbox
24502      */
24503     boxLabel : "",
24504     /**
24505      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24506      */  
24507     inputValue : '1',
24508     /**
24509      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24510      */
24511      valueOff: '0', // value when not checked..
24512
24513     actionMode : 'viewEl', 
24514     //
24515     // private
24516     itemCls : 'x-menu-check-item x-form-item',
24517     groupClass : 'x-menu-group-item',
24518     inputType : 'hidden',
24519     
24520     
24521     inSetChecked: false, // check that we are not calling self...
24522     
24523     inputElement: false, // real input element?
24524     basedOn: false, // ????
24525     
24526     isFormField: true, // not sure where this is needed!!!!
24527
24528     onResize : function(){
24529         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24530         if(!this.boxLabel){
24531             this.el.alignTo(this.wrap, 'c-c');
24532         }
24533     },
24534
24535     initEvents : function(){
24536         Roo.form.Checkbox.superclass.initEvents.call(this);
24537         this.el.on("click", this.onClick,  this);
24538         this.el.on("change", this.onClick,  this);
24539     },
24540
24541
24542     getResizeEl : function(){
24543         return this.wrap;
24544     },
24545
24546     getPositionEl : function(){
24547         return this.wrap;
24548     },
24549
24550     // private
24551     onRender : function(ct, position){
24552         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24553         /*
24554         if(this.inputValue !== undefined){
24555             this.el.dom.value = this.inputValue;
24556         }
24557         */
24558         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24559         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24560         var viewEl = this.wrap.createChild({ 
24561             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24562         this.viewEl = viewEl;   
24563         this.wrap.on('click', this.onClick,  this); 
24564         
24565         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24566         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24567         
24568         
24569         
24570         if(this.boxLabel){
24571             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24572         //    viewEl.on('click', this.onClick,  this); 
24573         }
24574         //if(this.checked){
24575             this.setChecked(this.checked);
24576         //}else{
24577             //this.checked = this.el.dom;
24578         //}
24579
24580     },
24581
24582     // private
24583     initValue : Roo.emptyFn,
24584
24585     /**
24586      * Returns the checked state of the checkbox.
24587      * @return {Boolean} True if checked, else false
24588      */
24589     getValue : function(){
24590         if(this.el){
24591             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24592         }
24593         return this.valueOff;
24594         
24595     },
24596
24597         // private
24598     onClick : function(){ 
24599         this.setChecked(!this.checked);
24600
24601         //if(this.el.dom.checked != this.checked){
24602         //    this.setValue(this.el.dom.checked);
24603        // }
24604     },
24605
24606     /**
24607      * Sets the checked state of the checkbox.
24608      * On is always based on a string comparison between inputValue and the param.
24609      * @param {Boolean/String} value - the value to set 
24610      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24611      */
24612     setValue : function(v,suppressEvent){
24613         
24614         
24615         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24616         //if(this.el && this.el.dom){
24617         //    this.el.dom.checked = this.checked;
24618         //    this.el.dom.defaultChecked = this.checked;
24619         //}
24620         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24621         //this.fireEvent("check", this, this.checked);
24622     },
24623     // private..
24624     setChecked : function(state,suppressEvent)
24625     {
24626         if (this.inSetChecked) {
24627             this.checked = state;
24628             return;
24629         }
24630         
24631     
24632         if(this.wrap){
24633             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24634         }
24635         this.checked = state;
24636         if(suppressEvent !== true){
24637             this.fireEvent('check', this, state);
24638         }
24639         this.inSetChecked = true;
24640         this.el.dom.value = state ? this.inputValue : this.valueOff;
24641         this.inSetChecked = false;
24642         
24643     },
24644     // handle setting of hidden value by some other method!!?!?
24645     setFromHidden: function()
24646     {
24647         if(!this.el){
24648             return;
24649         }
24650         //console.log("SET FROM HIDDEN");
24651         //alert('setFrom hidden');
24652         this.setValue(this.el.dom.value);
24653     },
24654     
24655     onDestroy : function()
24656     {
24657         if(this.viewEl){
24658             Roo.get(this.viewEl).remove();
24659         }
24660          
24661         Roo.form.Checkbox.superclass.onDestroy.call(this);
24662     }
24663
24664 });/*
24665  * Based on:
24666  * Ext JS Library 1.1.1
24667  * Copyright(c) 2006-2007, Ext JS, LLC.
24668  *
24669  * Originally Released Under LGPL - original licence link has changed is not relivant.
24670  *
24671  * Fork - LGPL
24672  * <script type="text/javascript">
24673  */
24674  
24675 /**
24676  * @class Roo.form.Radio
24677  * @extends Roo.form.Checkbox
24678  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24679  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24680  * @constructor
24681  * Creates a new Radio
24682  * @param {Object} config Configuration options
24683  */
24684 Roo.form.Radio = function(){
24685     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24686 };
24687 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24688     inputType: 'radio',
24689
24690     /**
24691      * If this radio is part of a group, it will return the selected value
24692      * @return {String}
24693      */
24694     getGroupValue : function(){
24695         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24696     }
24697 });//<script type="text/javascript">
24698
24699 /*
24700  * Ext JS Library 1.1.1
24701  * Copyright(c) 2006-2007, Ext JS, LLC.
24702  * licensing@extjs.com
24703  * 
24704  * http://www.extjs.com/license
24705  */
24706  
24707  /*
24708   * 
24709   * Known bugs:
24710   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24711   * - IE ? - no idea how much works there.
24712   * 
24713   * 
24714   * 
24715   */
24716  
24717
24718 /**
24719  * @class Ext.form.HtmlEditor
24720  * @extends Ext.form.Field
24721  * Provides a lightweight HTML Editor component.
24722  *
24723  * This has been tested on Fireforx / Chrome.. IE may not be so great..
24724  * 
24725  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24726  * supported by this editor.</b><br/><br/>
24727  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24728  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24729  */
24730 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24731       /**
24732      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24733      */
24734     toolbars : false,
24735     /**
24736      * @cfg {String} createLinkText The default text for the create link prompt
24737      */
24738     createLinkText : 'Please enter the URL for the link:',
24739     /**
24740      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24741      */
24742     defaultLinkValue : 'http:/'+'/',
24743    
24744      /**
24745      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24746      *                        Roo.resizable.
24747      */
24748     resizable : false,
24749      /**
24750      * @cfg {Number} height (in pixels)
24751      */   
24752     height: 300,
24753    /**
24754      * @cfg {Number} width (in pixels)
24755      */   
24756     width: 500,
24757     
24758     /**
24759      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24760      * 
24761      */
24762     stylesheets: false,
24763     
24764     // id of frame..
24765     frameId: false,
24766     
24767     // private properties
24768     validationEvent : false,
24769     deferHeight: true,
24770     initialized : false,
24771     activated : false,
24772     sourceEditMode : false,
24773     onFocus : Roo.emptyFn,
24774     iframePad:3,
24775     hideMode:'offsets',
24776     
24777     defaultAutoCreate : { // modified by initCompnoent..
24778         tag: "textarea",
24779         style:"width:500px;height:300px;",
24780         autocomplete: "off"
24781     },
24782
24783     // private
24784     initComponent : function(){
24785         this.addEvents({
24786             /**
24787              * @event initialize
24788              * Fires when the editor is fully initialized (including the iframe)
24789              * @param {HtmlEditor} this
24790              */
24791             initialize: true,
24792             /**
24793              * @event activate
24794              * Fires when the editor is first receives the focus. Any insertion must wait
24795              * until after this event.
24796              * @param {HtmlEditor} this
24797              */
24798             activate: true,
24799              /**
24800              * @event beforesync
24801              * Fires before the textarea is updated with content from the editor iframe. Return false
24802              * to cancel the sync.
24803              * @param {HtmlEditor} this
24804              * @param {String} html
24805              */
24806             beforesync: true,
24807              /**
24808              * @event beforepush
24809              * Fires before the iframe editor is updated with content from the textarea. Return false
24810              * to cancel the push.
24811              * @param {HtmlEditor} this
24812              * @param {String} html
24813              */
24814             beforepush: true,
24815              /**
24816              * @event sync
24817              * Fires when the textarea is updated with content from the editor iframe.
24818              * @param {HtmlEditor} this
24819              * @param {String} html
24820              */
24821             sync: true,
24822              /**
24823              * @event push
24824              * Fires when the iframe editor is updated with content from the textarea.
24825              * @param {HtmlEditor} this
24826              * @param {String} html
24827              */
24828             push: true,
24829              /**
24830              * @event editmodechange
24831              * Fires when the editor switches edit modes
24832              * @param {HtmlEditor} this
24833              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24834              */
24835             editmodechange: true,
24836             /**
24837              * @event editorevent
24838              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24839              * @param {HtmlEditor} this
24840              */
24841             editorevent: true
24842         });
24843         this.defaultAutoCreate =  {
24844             tag: "textarea",
24845             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24846             autocomplete: "off"
24847         };
24848     },
24849
24850     /**
24851      * Protected method that will not generally be called directly. It
24852      * is called when the editor creates its toolbar. Override this method if you need to
24853      * add custom toolbar buttons.
24854      * @param {HtmlEditor} editor
24855      */
24856     createToolbar : function(editor){
24857         if (!editor.toolbars || !editor.toolbars.length) {
24858             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24859         }
24860         
24861         for (var i =0 ; i < editor.toolbars.length;i++) {
24862             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24863             editor.toolbars[i].init(editor);
24864         }
24865          
24866         
24867     },
24868
24869     /**
24870      * Protected method that will not generally be called directly. It
24871      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24872      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24873      */
24874     getDocMarkup : function(){
24875         // body styles..
24876         var st = '';
24877         if (this.stylesheets === false) {
24878             
24879             Roo.get(document.head).select('style').each(function(node) {
24880                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24881             });
24882             
24883             Roo.get(document.head).select('link').each(function(node) { 
24884                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24885             });
24886             
24887         } else if (!this.stylesheets.length) {
24888                 // simple..
24889                 st = '<style type="text/css">' +
24890                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24891                    '</style>';
24892         } else {
24893             Roo.each(this.stylesheets, function(s) {
24894                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24895             });
24896             
24897         }
24898         
24899         st +=  '<style type="text/css">' +
24900             'IMG { cursor: pointer } ' +
24901         '</style>';
24902
24903         
24904         return '<html><head>' + st  +
24905             //<style type="text/css">' +
24906             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24907             //'</style>' +
24908             ' </head><body class="roo-htmleditor-body"></body></html>';
24909     },
24910
24911     // private
24912     onRender : function(ct, position)
24913     {
24914         var _t = this;
24915         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24916         this.el.dom.style.border = '0 none';
24917         this.el.dom.setAttribute('tabIndex', -1);
24918         this.el.addClass('x-hidden');
24919         if(Roo.isIE){ // fix IE 1px bogus margin
24920             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24921         }
24922         this.wrap = this.el.wrap({
24923             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24924         });
24925         
24926         if (this.resizable) {
24927             this.resizeEl = new Roo.Resizable(this.wrap, {
24928                 pinned : true,
24929                 wrap: true,
24930                 dynamic : true,
24931                 minHeight : this.height,
24932                 height: this.height,
24933                 handles : this.resizable,
24934                 width: this.width,
24935                 listeners : {
24936                     resize : function(r, w, h) {
24937                         _t.onResize(w,h); // -something
24938                     }
24939                 }
24940             });
24941             
24942         }
24943
24944         this.frameId = Roo.id();
24945         
24946         this.createToolbar(this);
24947         
24948       
24949         
24950         var iframe = this.wrap.createChild({
24951             tag: 'iframe',
24952             id: this.frameId,
24953             name: this.frameId,
24954             frameBorder : 'no',
24955             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24956         }, this.el
24957         );
24958         
24959        // console.log(iframe);
24960         //this.wrap.dom.appendChild(iframe);
24961
24962         this.iframe = iframe.dom;
24963
24964          this.assignDocWin();
24965         
24966         this.doc.designMode = 'on';
24967        
24968         this.doc.open();
24969         this.doc.write(this.getDocMarkup());
24970         this.doc.close();
24971
24972         
24973         var task = { // must defer to wait for browser to be ready
24974             run : function(){
24975                 //console.log("run task?" + this.doc.readyState);
24976                 this.assignDocWin();
24977                 if(this.doc.body || this.doc.readyState == 'complete'){
24978                     try {
24979                         this.doc.designMode="on";
24980                     } catch (e) {
24981                         return;
24982                     }
24983                     Roo.TaskMgr.stop(task);
24984                     this.initEditor.defer(10, this);
24985                 }
24986             },
24987             interval : 10,
24988             duration:10000,
24989             scope: this
24990         };
24991         Roo.TaskMgr.start(task);
24992
24993         if(!this.width){
24994             this.setSize(this.wrap.getSize());
24995         }
24996         if (this.resizeEl) {
24997             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24998             // should trigger onReize..
24999         }
25000     },
25001
25002     // private
25003     onResize : function(w, h)
25004     {
25005         //Roo.log('resize: ' +w + ',' + h );
25006         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25007         if(this.el && this.iframe){
25008             if(typeof w == 'number'){
25009                 var aw = w - this.wrap.getFrameWidth('lr');
25010                 this.el.setWidth(this.adjustWidth('textarea', aw));
25011                 this.iframe.style.width = aw + 'px';
25012             }
25013             if(typeof h == 'number'){
25014                 var tbh = 0;
25015                 for (var i =0; i < this.toolbars.length;i++) {
25016                     // fixme - ask toolbars for heights?
25017                     tbh += this.toolbars[i].tb.el.getHeight();
25018                     if (this.toolbars[i].footer) {
25019                         tbh += this.toolbars[i].footer.el.getHeight();
25020                     }
25021                 }
25022                 
25023                 
25024                 
25025                 
25026                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25027                 ah -= 5; // knock a few pixes off for look..
25028                 this.el.setHeight(this.adjustWidth('textarea', ah));
25029                 this.iframe.style.height = ah + 'px';
25030                 if(this.doc){
25031                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25032                 }
25033             }
25034         }
25035     },
25036
25037     /**
25038      * Toggles the editor between standard and source edit mode.
25039      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25040      */
25041     toggleSourceEdit : function(sourceEditMode){
25042         
25043         this.sourceEditMode = sourceEditMode === true;
25044         
25045         if(this.sourceEditMode){
25046           
25047             this.syncValue();
25048             this.iframe.className = 'x-hidden';
25049             this.el.removeClass('x-hidden');
25050             this.el.dom.removeAttribute('tabIndex');
25051             this.el.focus();
25052         }else{
25053              
25054             this.pushValue();
25055             this.iframe.className = '';
25056             this.el.addClass('x-hidden');
25057             this.el.dom.setAttribute('tabIndex', -1);
25058             this.deferFocus();
25059         }
25060         this.setSize(this.wrap.getSize());
25061         this.fireEvent('editmodechange', this, this.sourceEditMode);
25062     },
25063
25064     // private used internally
25065     createLink : function(){
25066         var url = prompt(this.createLinkText, this.defaultLinkValue);
25067         if(url && url != 'http:/'+'/'){
25068             this.relayCmd('createlink', url);
25069         }
25070     },
25071
25072     // private (for BoxComponent)
25073     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25074
25075     // private (for BoxComponent)
25076     getResizeEl : function(){
25077         return this.wrap;
25078     },
25079
25080     // private (for BoxComponent)
25081     getPositionEl : function(){
25082         return this.wrap;
25083     },
25084
25085     // private
25086     initEvents : function(){
25087         this.originalValue = this.getValue();
25088     },
25089
25090     /**
25091      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25092      * @method
25093      */
25094     markInvalid : Roo.emptyFn,
25095     /**
25096      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25097      * @method
25098      */
25099     clearInvalid : Roo.emptyFn,
25100
25101     setValue : function(v){
25102         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25103         this.pushValue();
25104     },
25105
25106     /**
25107      * Protected method that will not generally be called directly. If you need/want
25108      * custom HTML cleanup, this is the method you should override.
25109      * @param {String} html The HTML to be cleaned
25110      * return {String} The cleaned HTML
25111      */
25112     cleanHtml : function(html){
25113         html = String(html);
25114         if(html.length > 5){
25115             if(Roo.isSafari){ // strip safari nonsense
25116                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25117             }
25118         }
25119         if(html == '&nbsp;'){
25120             html = '';
25121         }
25122         return html;
25123     },
25124
25125     /**
25126      * Protected method that will not generally be called directly. Syncs the contents
25127      * of the editor iframe with the textarea.
25128      */
25129     syncValue : function(){
25130         if(this.initialized){
25131             var bd = (this.doc.body || this.doc.documentElement);
25132             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25133             var html = bd.innerHTML;
25134             if(Roo.isSafari){
25135                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25136                 var m = bs.match(/text-align:(.*?);/i);
25137                 if(m && m[1]){
25138                     html = '<div style="'+m[0]+'">' + html + '</div>';
25139                 }
25140             }
25141             html = this.cleanHtml(html);
25142             // fix up the special chars..
25143             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25144                 return "&#"+b.charCodeAt()+";" 
25145             });
25146             if(this.fireEvent('beforesync', this, html) !== false){
25147                 this.el.dom.value = html;
25148                 this.fireEvent('sync', this, html);
25149             }
25150         }
25151     },
25152
25153     /**
25154      * Protected method that will not generally be called directly. Pushes the value of the textarea
25155      * into the iframe editor.
25156      */
25157     pushValue : function(){
25158         if(this.initialized){
25159             var v = this.el.dom.value;
25160             if(v.length < 1){
25161                 v = '&#160;';
25162             }
25163             
25164             if(this.fireEvent('beforepush', this, v) !== false){
25165                 var d = (this.doc.body || this.doc.documentElement);
25166                 d.innerHTML = v;
25167                 this.cleanUpPaste();
25168                 this.el.dom.value = d.innerHTML;
25169                 this.fireEvent('push', this, v);
25170             }
25171         }
25172     },
25173
25174     // private
25175     deferFocus : function(){
25176         this.focus.defer(10, this);
25177     },
25178
25179     // doc'ed in Field
25180     focus : function(){
25181         if(this.win && !this.sourceEditMode){
25182             this.win.focus();
25183         }else{
25184             this.el.focus();
25185         }
25186     },
25187     
25188     assignDocWin: function()
25189     {
25190         var iframe = this.iframe;
25191         
25192          if(Roo.isIE){
25193             this.doc = iframe.contentWindow.document;
25194             this.win = iframe.contentWindow;
25195         } else {
25196             if (!Roo.get(this.frameId)) {
25197                 return;
25198             }
25199             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25200             this.win = Roo.get(this.frameId).dom.contentWindow;
25201         }
25202     },
25203     
25204     // private
25205     initEditor : function(){
25206         //console.log("INIT EDITOR");
25207         this.assignDocWin();
25208         
25209         
25210         
25211         this.doc.designMode="on";
25212         this.doc.open();
25213         this.doc.write(this.getDocMarkup());
25214         this.doc.close();
25215         
25216         var dbody = (this.doc.body || this.doc.documentElement);
25217         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25218         // this copies styles from the containing element into thsi one..
25219         // not sure why we need all of this..
25220         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25221         ss['background-attachment'] = 'fixed'; // w3c
25222         dbody.bgProperties = 'fixed'; // ie
25223         Roo.DomHelper.applyStyles(dbody, ss);
25224         Roo.EventManager.on(this.doc, {
25225             //'mousedown': this.onEditorEvent,
25226             'mouseup': this.onEditorEvent,
25227             'dblclick': this.onEditorEvent,
25228             'click': this.onEditorEvent,
25229             'keyup': this.onEditorEvent,
25230             buffer:100,
25231             scope: this
25232         });
25233         if(Roo.isGecko){
25234             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25235         }
25236         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25237             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25238         }
25239         this.initialized = true;
25240
25241         this.fireEvent('initialize', this);
25242         this.pushValue();
25243     },
25244
25245     // private
25246     onDestroy : function(){
25247         
25248         
25249         
25250         if(this.rendered){
25251             
25252             for (var i =0; i < this.toolbars.length;i++) {
25253                 // fixme - ask toolbars for heights?
25254                 this.toolbars[i].onDestroy();
25255             }
25256             
25257             this.wrap.dom.innerHTML = '';
25258             this.wrap.remove();
25259         }
25260     },
25261
25262     // private
25263     onFirstFocus : function(){
25264         
25265         this.assignDocWin();
25266         
25267         
25268         this.activated = true;
25269         for (var i =0; i < this.toolbars.length;i++) {
25270             this.toolbars[i].onFirstFocus();
25271         }
25272        
25273         if(Roo.isGecko){ // prevent silly gecko errors
25274             this.win.focus();
25275             var s = this.win.getSelection();
25276             if(!s.focusNode || s.focusNode.nodeType != 3){
25277                 var r = s.getRangeAt(0);
25278                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25279                 r.collapse(true);
25280                 this.deferFocus();
25281             }
25282             try{
25283                 this.execCmd('useCSS', true);
25284                 this.execCmd('styleWithCSS', false);
25285             }catch(e){}
25286         }
25287         this.fireEvent('activate', this);
25288     },
25289
25290     // private
25291     adjustFont: function(btn){
25292         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25293         //if(Roo.isSafari){ // safari
25294         //    adjust *= 2;
25295        // }
25296         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25297         if(Roo.isSafari){ // safari
25298             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25299             v =  (v < 10) ? 10 : v;
25300             v =  (v > 48) ? 48 : v;
25301             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25302             
25303         }
25304         
25305         
25306         v = Math.max(1, v+adjust);
25307         
25308         this.execCmd('FontSize', v  );
25309     },
25310
25311     onEditorEvent : function(e){
25312         this.fireEvent('editorevent', this, e);
25313       //  this.updateToolbar();
25314         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25315     },
25316
25317     insertTag : function(tg)
25318     {
25319         // could be a bit smarter... -> wrap the current selected tRoo..
25320         
25321         this.execCmd("formatblock",   tg);
25322         
25323     },
25324     
25325     insertText : function(txt)
25326     {
25327         
25328         
25329         range = this.createRange();
25330         range.deleteContents();
25331                //alert(Sender.getAttribute('label'));
25332                
25333         range.insertNode(this.doc.createTextNode(txt));
25334     } ,
25335     
25336     // private
25337     relayBtnCmd : function(btn){
25338         this.relayCmd(btn.cmd);
25339     },
25340
25341     /**
25342      * Executes a Midas editor command on the editor document and performs necessary focus and
25343      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25344      * @param {String} cmd The Midas command
25345      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25346      */
25347     relayCmd : function(cmd, value){
25348         this.win.focus();
25349         this.execCmd(cmd, value);
25350         this.fireEvent('editorevent', this);
25351         //this.updateToolbar();
25352         this.deferFocus();
25353     },
25354
25355     /**
25356      * Executes a Midas editor command directly on the editor document.
25357      * For visual commands, you should use {@link #relayCmd} instead.
25358      * <b>This should only be called after the editor is initialized.</b>
25359      * @param {String} cmd The Midas command
25360      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25361      */
25362     execCmd : function(cmd, value){
25363         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25364         this.syncValue();
25365     },
25366  
25367  
25368    
25369     /**
25370      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25371      * to insert tRoo.
25372      * @param {String} text | dom node.. 
25373      */
25374     insertAtCursor : function(text)
25375     {
25376         
25377         
25378         
25379         if(!this.activated){
25380             return;
25381         }
25382         /*
25383         if(Roo.isIE){
25384             this.win.focus();
25385             var r = this.doc.selection.createRange();
25386             if(r){
25387                 r.collapse(true);
25388                 r.pasteHTML(text);
25389                 this.syncValue();
25390                 this.deferFocus();
25391             
25392             }
25393             return;
25394         }
25395         */
25396         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25397             this.win.focus();
25398             
25399             
25400             // from jquery ui (MIT licenced)
25401             var range, node;
25402             var win = this.win;
25403             
25404             if (win.getSelection && win.getSelection().getRangeAt) {
25405                 range = win.getSelection().getRangeAt(0);
25406                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25407                 range.insertNode(node);
25408             } else if (win.document.selection && win.document.selection.createRange) {
25409                 // no firefox support
25410                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25411                 win.document.selection.createRange().pasteHTML(txt);
25412             } else {
25413                 // no firefox support
25414                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25415                 this.execCmd('InsertHTML', txt);
25416             } 
25417             
25418             this.syncValue();
25419             
25420             this.deferFocus();
25421         }
25422     },
25423  // private
25424     mozKeyPress : function(e){
25425         if(e.ctrlKey){
25426             var c = e.getCharCode(), cmd;
25427           
25428             if(c > 0){
25429                 c = String.fromCharCode(c).toLowerCase();
25430                 switch(c){
25431                     case 'b':
25432                         cmd = 'bold';
25433                         break;
25434                     case 'i':
25435                         cmd = 'italic';
25436                         break;
25437                     
25438                     case 'u':
25439                         cmd = 'underline';
25440                         break;
25441                     
25442                     case 'v':
25443                         this.cleanUpPaste.defer(100, this);
25444                         return;
25445                         
25446                 }
25447                 if(cmd){
25448                     this.win.focus();
25449                     this.execCmd(cmd);
25450                     this.deferFocus();
25451                     e.preventDefault();
25452                 }
25453                 
25454             }
25455         }
25456     },
25457
25458     // private
25459     fixKeys : function(){ // load time branching for fastest keydown performance
25460         if(Roo.isIE){
25461             return function(e){
25462                 var k = e.getKey(), r;
25463                 if(k == e.TAB){
25464                     e.stopEvent();
25465                     r = this.doc.selection.createRange();
25466                     if(r){
25467                         r.collapse(true);
25468                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25469                         this.deferFocus();
25470                     }
25471                     return;
25472                 }
25473                 
25474                 if(k == e.ENTER){
25475                     r = this.doc.selection.createRange();
25476                     if(r){
25477                         var target = r.parentElement();
25478                         if(!target || target.tagName.toLowerCase() != 'li'){
25479                             e.stopEvent();
25480                             r.pasteHTML('<br />');
25481                             r.collapse(false);
25482                             r.select();
25483                         }
25484                     }
25485                 }
25486                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25487                     this.cleanUpPaste.defer(100, this);
25488                     return;
25489                 }
25490                 
25491                 
25492             };
25493         }else if(Roo.isOpera){
25494             return function(e){
25495                 var k = e.getKey();
25496                 if(k == e.TAB){
25497                     e.stopEvent();
25498                     this.win.focus();
25499                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25500                     this.deferFocus();
25501                 }
25502                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25503                     this.cleanUpPaste.defer(100, this);
25504                     return;
25505                 }
25506                 
25507             };
25508         }else if(Roo.isSafari){
25509             return function(e){
25510                 var k = e.getKey();
25511                 
25512                 if(k == e.TAB){
25513                     e.stopEvent();
25514                     this.execCmd('InsertText','\t');
25515                     this.deferFocus();
25516                     return;
25517                 }
25518                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25519                     this.cleanUpPaste.defer(100, this);
25520                     return;
25521                 }
25522                 
25523              };
25524         }
25525     }(),
25526     
25527     getAllAncestors: function()
25528     {
25529         var p = this.getSelectedNode();
25530         var a = [];
25531         if (!p) {
25532             a.push(p); // push blank onto stack..
25533             p = this.getParentElement();
25534         }
25535         
25536         
25537         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25538             a.push(p);
25539             p = p.parentNode;
25540         }
25541         a.push(this.doc.body);
25542         return a;
25543     },
25544     lastSel : false,
25545     lastSelNode : false,
25546     
25547     
25548     getSelection : function() 
25549     {
25550         this.assignDocWin();
25551         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25552     },
25553     
25554     getSelectedNode: function() 
25555     {
25556         // this may only work on Gecko!!!
25557         
25558         // should we cache this!!!!
25559         
25560         
25561         
25562          
25563         var range = this.createRange(this.getSelection()).cloneRange();
25564         
25565         if (Roo.isIE) {
25566             var parent = range.parentElement();
25567             while (true) {
25568                 var testRange = range.duplicate();
25569                 testRange.moveToElementText(parent);
25570                 if (testRange.inRange(range)) {
25571                     break;
25572                 }
25573                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25574                     break;
25575                 }
25576                 parent = parent.parentElement;
25577             }
25578             return parent;
25579         }
25580         
25581         // is ancestor a text element.
25582         var ac =  range.commonAncestorContainer;
25583         if (ac.nodeType == 3) {
25584             ac = ac.parentNode;
25585         }
25586         
25587         var ar = ac.childNodes;
25588          
25589         var nodes = [];
25590         var other_nodes = [];
25591         var has_other_nodes = false;
25592         for (var i=0;i<ar.length;i++) {
25593             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25594                 continue;
25595             }
25596             // fullly contained node.
25597             
25598             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25599                 nodes.push(ar[i]);
25600                 continue;
25601             }
25602             
25603             // probably selected..
25604             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25605                 other_nodes.push(ar[i]);
25606                 continue;
25607             }
25608             // outer..
25609             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25610                 continue;
25611             }
25612             
25613             
25614             has_other_nodes = true;
25615         }
25616         if (!nodes.length && other_nodes.length) {
25617             nodes= other_nodes;
25618         }
25619         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25620             return false;
25621         }
25622         
25623         return nodes[0];
25624     },
25625     createRange: function(sel)
25626     {
25627         // this has strange effects when using with 
25628         // top toolbar - not sure if it's a great idea.
25629         //this.editor.contentWindow.focus();
25630         if (typeof sel != "undefined") {
25631             try {
25632                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25633             } catch(e) {
25634                 return this.doc.createRange();
25635             }
25636         } else {
25637             return this.doc.createRange();
25638         }
25639     },
25640     getParentElement: function()
25641     {
25642         
25643         this.assignDocWin();
25644         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25645         
25646         var range = this.createRange(sel);
25647          
25648         try {
25649             var p = range.commonAncestorContainer;
25650             while (p.nodeType == 3) { // text node
25651                 p = p.parentNode;
25652             }
25653             return p;
25654         } catch (e) {
25655             return null;
25656         }
25657     
25658     },
25659     /***
25660      *
25661      * Range intersection.. the hard stuff...
25662      *  '-1' = before
25663      *  '0' = hits..
25664      *  '1' = after.
25665      *         [ -- selected range --- ]
25666      *   [fail]                        [fail]
25667      *
25668      *    basically..
25669      *      if end is before start or  hits it. fail.
25670      *      if start is after end or hits it fail.
25671      *
25672      *   if either hits (but other is outside. - then it's not 
25673      *   
25674      *    
25675      **/
25676     
25677     
25678     // @see http://www.thismuchiknow.co.uk/?p=64.
25679     rangeIntersectsNode : function(range, node)
25680     {
25681         var nodeRange = node.ownerDocument.createRange();
25682         try {
25683             nodeRange.selectNode(node);
25684         } catch (e) {
25685             nodeRange.selectNodeContents(node);
25686         }
25687     
25688         var rangeStartRange = range.cloneRange();
25689         rangeStartRange.collapse(true);
25690     
25691         var rangeEndRange = range.cloneRange();
25692         rangeEndRange.collapse(false);
25693     
25694         var nodeStartRange = nodeRange.cloneRange();
25695         nodeStartRange.collapse(true);
25696     
25697         var nodeEndRange = nodeRange.cloneRange();
25698         nodeEndRange.collapse(false);
25699     
25700         return rangeStartRange.compareBoundaryPoints(
25701                  Range.START_TO_START, nodeEndRange) == -1 &&
25702                rangeEndRange.compareBoundaryPoints(
25703                  Range.START_TO_START, nodeStartRange) == 1;
25704         
25705          
25706     },
25707     rangeCompareNode : function(range, node)
25708     {
25709         var nodeRange = node.ownerDocument.createRange();
25710         try {
25711             nodeRange.selectNode(node);
25712         } catch (e) {
25713             nodeRange.selectNodeContents(node);
25714         }
25715         
25716         
25717         range.collapse(true);
25718     
25719         nodeRange.collapse(true);
25720      
25721         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25722         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25723          
25724         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25725         
25726         var nodeIsBefore   =  ss == 1;
25727         var nodeIsAfter    = ee == -1;
25728         
25729         if (nodeIsBefore && nodeIsAfter)
25730             return 0; // outer
25731         if (!nodeIsBefore && nodeIsAfter)
25732             return 1; //right trailed.
25733         
25734         if (nodeIsBefore && !nodeIsAfter)
25735             return 2;  // left trailed.
25736         // fully contined.
25737         return 3;
25738     },
25739
25740     // private? - in a new class?
25741     cleanUpPaste :  function()
25742     {
25743         // cleans up the whole document..
25744          Roo.log('cleanuppaste');
25745         this.cleanUpChildren(this.doc.body);
25746         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25747         if (clean != this.doc.body.innerHTML) {
25748             this.doc.body.innerHTML = clean;
25749         }
25750         
25751     },
25752     
25753     cleanWordChars : function(input) {
25754         var he = Roo.form.HtmlEditor;
25755     
25756         var output = input;
25757         Roo.each(he.swapCodes, function(sw) { 
25758         
25759             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25760             output = output.replace(swapper, sw[1]);
25761         });
25762         return output;
25763     },
25764     
25765     
25766     cleanUpChildren : function (n)
25767     {
25768         if (!n.childNodes.length) {
25769             return;
25770         }
25771         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25772            this.cleanUpChild(n.childNodes[i]);
25773         }
25774     },
25775     
25776     
25777         
25778     
25779     cleanUpChild : function (node)
25780     {
25781         //console.log(node);
25782         if (node.nodeName == "#text") {
25783             // clean up silly Windows -- stuff?
25784             return; 
25785         }
25786         if (node.nodeName == "#comment") {
25787             node.parentNode.removeChild(node);
25788             // clean up silly Windows -- stuff?
25789             return; 
25790         }
25791         
25792         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25793             // remove node.
25794             node.parentNode.removeChild(node);
25795             return;
25796             
25797         }
25798         
25799         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25800         
25801         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25802         
25803         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25804             remove_keep_children = true;
25805         }
25806         
25807         if (remove_keep_children) {
25808             this.cleanUpChildren(node);
25809             // inserts everything just before this node...
25810             while (node.childNodes.length) {
25811                 var cn = node.childNodes[0];
25812                 node.removeChild(cn);
25813                 node.parentNode.insertBefore(cn, node);
25814             }
25815             node.parentNode.removeChild(node);
25816             return;
25817         }
25818         
25819         if (!node.attributes || !node.attributes.length) {
25820             this.cleanUpChildren(node);
25821             return;
25822         }
25823         
25824         function cleanAttr(n,v)
25825         {
25826             
25827             if (v.match(/^\./) || v.match(/^\//)) {
25828                 return;
25829             }
25830             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25831                 return;
25832             }
25833             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25834             node.removeAttribute(n);
25835             
25836         }
25837         
25838         function cleanStyle(n,v)
25839         {
25840             if (v.match(/expression/)) { //XSS?? should we even bother..
25841                 node.removeAttribute(n);
25842                 return;
25843             }
25844             
25845             
25846             var parts = v.split(/;/);
25847             Roo.each(parts, function(p) {
25848                 p = p.replace(/\s+/g,'');
25849                 if (!p.length) {
25850                     return true;
25851                 }
25852                 var l = p.split(':').shift().replace(/\s+/g,'');
25853                 
25854                 // only allow 'c whitelisted system attributes'
25855                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25856                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25857                     node.removeAttribute(n);
25858                     return false;
25859                 }
25860                 return true;
25861             });
25862             
25863             
25864         }
25865         
25866         
25867         for (var i = node.attributes.length-1; i > -1 ; i--) {
25868             var a = node.attributes[i];
25869             //console.log(a);
25870             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25871                 node.removeAttribute(a.name);
25872                 return;
25873             }
25874             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25875                 cleanAttr(a.name,a.value); // fixme..
25876                 return;
25877             }
25878             if (a.name == 'style') {
25879                 cleanStyle(a.name,a.value);
25880             }
25881             /// clean up MS crap..
25882             // tecnically this should be a list of valid class'es..
25883             
25884             
25885             if (a.name == 'class') {
25886                 if (a.value.match(/^Mso/)) {
25887                     node.className = '';
25888                 }
25889                 
25890                 if (a.value.match(/body/)) {
25891                     node.className = '';
25892                 }
25893             }
25894             
25895             // style cleanup!?
25896             // class cleanup?
25897             
25898         }
25899         
25900         
25901         this.cleanUpChildren(node);
25902         
25903         
25904     }
25905     
25906     
25907     // hide stuff that is not compatible
25908     /**
25909      * @event blur
25910      * @hide
25911      */
25912     /**
25913      * @event change
25914      * @hide
25915      */
25916     /**
25917      * @event focus
25918      * @hide
25919      */
25920     /**
25921      * @event specialkey
25922      * @hide
25923      */
25924     /**
25925      * @cfg {String} fieldClass @hide
25926      */
25927     /**
25928      * @cfg {String} focusClass @hide
25929      */
25930     /**
25931      * @cfg {String} autoCreate @hide
25932      */
25933     /**
25934      * @cfg {String} inputType @hide
25935      */
25936     /**
25937      * @cfg {String} invalidClass @hide
25938      */
25939     /**
25940      * @cfg {String} invalidText @hide
25941      */
25942     /**
25943      * @cfg {String} msgFx @hide
25944      */
25945     /**
25946      * @cfg {String} validateOnBlur @hide
25947      */
25948 });
25949
25950 Roo.form.HtmlEditor.white = [
25951         'area', 'br', 'img', 'input', 'hr', 'wbr',
25952         
25953        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25954        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25955        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25956        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25957        'table',   'ul',         'xmp', 
25958        
25959        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25960       'thead',   'tr', 
25961      
25962       'dir', 'menu', 'ol', 'ul', 'dl',
25963        
25964       'embed',  'object'
25965 ];
25966
25967
25968 Roo.form.HtmlEditor.black = [
25969     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25970         'applet', // 
25971         'base',   'basefont', 'bgsound', 'blink',  'body', 
25972         'frame',  'frameset', 'head',    'html',   'ilayer', 
25973         'iframe', 'layer',  'link',     'meta',    'object',   
25974         'script', 'style' ,'title',  'xml' // clean later..
25975 ];
25976 Roo.form.HtmlEditor.clean = [
25977     'script', 'style', 'title', 'xml'
25978 ];
25979 Roo.form.HtmlEditor.remove = [
25980     'font'
25981 ];
25982 // attributes..
25983
25984 Roo.form.HtmlEditor.ablack = [
25985     'on'
25986 ];
25987     
25988 Roo.form.HtmlEditor.aclean = [ 
25989     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25990 ];
25991
25992 // protocols..
25993 Roo.form.HtmlEditor.pwhite= [
25994         'http',  'https',  'mailto'
25995 ];
25996
25997 // white listed style attributes.
25998 Roo.form.HtmlEditor.cwhite= [
25999         'text-align',
26000         'font-size'
26001 ];
26002
26003
26004 Roo.form.HtmlEditor.swapCodes   =[ 
26005     [    8211, "--" ], 
26006     [    8212, "--" ], 
26007     [    8216,  "'" ],  
26008     [    8217, "'" ],  
26009     [    8220, '"' ],  
26010     [    8221, '"' ],  
26011     [    8226, "*" ],  
26012     [    8230, "..." ]
26013 ]; 
26014
26015     // <script type="text/javascript">
26016 /*
26017  * Based on
26018  * Ext JS Library 1.1.1
26019  * Copyright(c) 2006-2007, Ext JS, LLC.
26020  *  
26021  
26022  */
26023
26024 /**
26025  * @class Roo.form.HtmlEditorToolbar1
26026  * Basic Toolbar
26027  * 
26028  * Usage:
26029  *
26030  new Roo.form.HtmlEditor({
26031     ....
26032     toolbars : [
26033         new Roo.form.HtmlEditorToolbar1({
26034             disable : { fonts: 1 , format: 1, ..., ... , ...],
26035             btns : [ .... ]
26036         })
26037     }
26038      
26039  * 
26040  * @cfg {Object} disable List of elements to disable..
26041  * @cfg {Array} btns List of additional buttons.
26042  * 
26043  * 
26044  * NEEDS Extra CSS? 
26045  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26046  */
26047  
26048 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26049 {
26050     
26051     Roo.apply(this, config);
26052     
26053     // default disabled, based on 'good practice'..
26054     this.disable = this.disable || {};
26055     Roo.applyIf(this.disable, {
26056         fontSize : true,
26057         colors : true,
26058         specialElements : true
26059     });
26060     
26061     
26062     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26063     // dont call parent... till later.
26064 }
26065
26066 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26067     
26068     tb: false,
26069     
26070     rendered: false,
26071     
26072     editor : false,
26073     /**
26074      * @cfg {Object} disable  List of toolbar elements to disable
26075          
26076      */
26077     disable : false,
26078       /**
26079      * @cfg {Array} fontFamilies An array of available font families
26080      */
26081     fontFamilies : [
26082         'Arial',
26083         'Courier New',
26084         'Tahoma',
26085         'Times New Roman',
26086         'Verdana'
26087     ],
26088     
26089     specialChars : [
26090            "&#169;",
26091           "&#174;",     
26092           "&#8482;",    
26093           "&#163;" ,    
26094          // "&#8212;",    
26095           "&#8230;",    
26096           "&#247;" ,    
26097         //  "&#225;" ,     ?? a acute?
26098            "&#8364;"    , //Euro
26099        //   "&#8220;"    ,
26100         //  "&#8221;"    ,
26101         //  "&#8226;"    ,
26102           "&#176;"  //   , // degrees
26103
26104          // "&#233;"     , // e ecute
26105          // "&#250;"     , // u ecute?
26106     ],
26107     
26108     specialElements : [
26109         {
26110             text: "Insert Table",
26111             xtype: 'MenuItem',
26112             xns : Roo.Menu,
26113             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26114                 
26115         },
26116         {    
26117             text: "Insert Image",
26118             xtype: 'MenuItem',
26119             xns : Roo.Menu,
26120             ihtml : '<img src="about:blank"/>'
26121             
26122         }
26123         
26124          
26125     ],
26126     
26127     
26128     inputElements : [ 
26129             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26130             "input:submit", "input:button", "select", "textarea", "label" ],
26131     formats : [
26132         ["p"] ,  
26133         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26134         ["pre"],[ "code"], 
26135         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26136     ],
26137      /**
26138      * @cfg {String} defaultFont default font to use.
26139      */
26140     defaultFont: 'tahoma',
26141    
26142     fontSelect : false,
26143     
26144     
26145     formatCombo : false,
26146     
26147     init : function(editor)
26148     {
26149         this.editor = editor;
26150         
26151         
26152         var fid = editor.frameId;
26153         var etb = this;
26154         function btn(id, toggle, handler){
26155             var xid = fid + '-'+ id ;
26156             return {
26157                 id : xid,
26158                 cmd : id,
26159                 cls : 'x-btn-icon x-edit-'+id,
26160                 enableToggle:toggle !== false,
26161                 scope: editor, // was editor...
26162                 handler:handler||editor.relayBtnCmd,
26163                 clickEvent:'mousedown',
26164                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26165                 tabIndex:-1
26166             };
26167         }
26168         
26169         
26170         
26171         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26172         this.tb = tb;
26173          // stop form submits
26174         tb.el.on('click', function(e){
26175             e.preventDefault(); // what does this do?
26176         });
26177
26178         if(!this.disable.font && !Roo.isSafari){
26179             /* why no safari for fonts
26180             editor.fontSelect = tb.el.createChild({
26181                 tag:'select',
26182                 tabIndex: -1,
26183                 cls:'x-font-select',
26184                 html: editor.createFontOptions()
26185             });
26186             editor.fontSelect.on('change', function(){
26187                 var font = editor.fontSelect.dom.value;
26188                 editor.relayCmd('fontname', font);
26189                 editor.deferFocus();
26190             }, editor);
26191             tb.add(
26192                 editor.fontSelect.dom,
26193                 '-'
26194             );
26195             */
26196         };
26197         if(!this.disable.formats){
26198             this.formatCombo = new Roo.form.ComboBox({
26199                 store: new Roo.data.SimpleStore({
26200                     id : 'tag',
26201                     fields: ['tag'],
26202                     data : this.formats // from states.js
26203                 }),
26204                 blockFocus : true,
26205                 //autoCreate : {tag: "div",  size: "20"},
26206                 displayField:'tag',
26207                 typeAhead: false,
26208                 mode: 'local',
26209                 editable : false,
26210                 triggerAction: 'all',
26211                 emptyText:'Add tag',
26212                 selectOnFocus:true,
26213                 width:135,
26214                 listeners : {
26215                     'select': function(c, r, i) {
26216                         editor.insertTag(r.get('tag'));
26217                         editor.focus();
26218                     }
26219                 }
26220
26221             });
26222             tb.addField(this.formatCombo);
26223             
26224         }
26225         
26226         if(!this.disable.format){
26227             tb.add(
26228                 btn('bold'),
26229                 btn('italic'),
26230                 btn('underline')
26231             );
26232         };
26233         if(!this.disable.fontSize){
26234             tb.add(
26235                 '-',
26236                 
26237                 
26238                 btn('increasefontsize', false, editor.adjustFont),
26239                 btn('decreasefontsize', false, editor.adjustFont)
26240             );
26241         };
26242         
26243         
26244         if(!this.disable.colors){
26245             tb.add(
26246                 '-', {
26247                     id:editor.frameId +'-forecolor',
26248                     cls:'x-btn-icon x-edit-forecolor',
26249                     clickEvent:'mousedown',
26250                     tooltip: this.buttonTips['forecolor'] || undefined,
26251                     tabIndex:-1,
26252                     menu : new Roo.menu.ColorMenu({
26253                         allowReselect: true,
26254                         focus: Roo.emptyFn,
26255                         value:'000000',
26256                         plain:true,
26257                         selectHandler: function(cp, color){
26258                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26259                             editor.deferFocus();
26260                         },
26261                         scope: editor,
26262                         clickEvent:'mousedown'
26263                     })
26264                 }, {
26265                     id:editor.frameId +'backcolor',
26266                     cls:'x-btn-icon x-edit-backcolor',
26267                     clickEvent:'mousedown',
26268                     tooltip: this.buttonTips['backcolor'] || undefined,
26269                     tabIndex:-1,
26270                     menu : new Roo.menu.ColorMenu({
26271                         focus: Roo.emptyFn,
26272                         value:'FFFFFF',
26273                         plain:true,
26274                         allowReselect: true,
26275                         selectHandler: function(cp, color){
26276                             if(Roo.isGecko){
26277                                 editor.execCmd('useCSS', false);
26278                                 editor.execCmd('hilitecolor', color);
26279                                 editor.execCmd('useCSS', true);
26280                                 editor.deferFocus();
26281                             }else{
26282                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26283                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26284                                 editor.deferFocus();
26285                             }
26286                         },
26287                         scope:editor,
26288                         clickEvent:'mousedown'
26289                     })
26290                 }
26291             );
26292         };
26293         // now add all the items...
26294         
26295
26296         if(!this.disable.alignments){
26297             tb.add(
26298                 '-',
26299                 btn('justifyleft'),
26300                 btn('justifycenter'),
26301                 btn('justifyright')
26302             );
26303         };
26304
26305         //if(!Roo.isSafari){
26306             if(!this.disable.links){
26307                 tb.add(
26308                     '-',
26309                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26310                 );
26311             };
26312
26313             if(!this.disable.lists){
26314                 tb.add(
26315                     '-',
26316                     btn('insertorderedlist'),
26317                     btn('insertunorderedlist')
26318                 );
26319             }
26320             if(!this.disable.sourceEdit){
26321                 tb.add(
26322                     '-',
26323                     btn('sourceedit', true, function(btn){
26324                         this.toggleSourceEdit(btn.pressed);
26325                     })
26326                 );
26327             }
26328         //}
26329         
26330         var smenu = { };
26331         // special menu.. - needs to be tidied up..
26332         if (!this.disable.special) {
26333             smenu = {
26334                 text: "&#169;",
26335                 cls: 'x-edit-none',
26336                 
26337                 menu : {
26338                     items : []
26339                 }
26340             };
26341             for (var i =0; i < this.specialChars.length; i++) {
26342                 smenu.menu.items.push({
26343                     
26344                     html: this.specialChars[i],
26345                     handler: function(a,b) {
26346                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26347                         //editor.insertAtCursor(a.html);
26348                         
26349                     },
26350                     tabIndex:-1
26351                 });
26352             }
26353             
26354             
26355             tb.add(smenu);
26356             
26357             
26358         }
26359          
26360         if (!this.disable.specialElements) {
26361             var semenu = {
26362                 text: "Other;",
26363                 cls: 'x-edit-none',
26364                 menu : {
26365                     items : []
26366                 }
26367             };
26368             for (var i =0; i < this.specialElements.length; i++) {
26369                 semenu.menu.items.push(
26370                     Roo.apply({ 
26371                         handler: function(a,b) {
26372                             editor.insertAtCursor(this.ihtml);
26373                         }
26374                     }, this.specialElements[i])
26375                 );
26376                     
26377             }
26378             
26379             tb.add(semenu);
26380             
26381             
26382         }
26383          
26384         
26385         if (this.btns) {
26386             for(var i =0; i< this.btns.length;i++) {
26387                 var b = Roo.factory(this.btns[i],Roo.form);
26388                 b.cls =  'x-edit-none';
26389                 b.scope = editor;
26390                 tb.add(b);
26391             }
26392         
26393         }
26394         
26395         
26396         
26397         // disable everything...
26398         
26399         this.tb.items.each(function(item){
26400            if(item.id != editor.frameId+ '-sourceedit'){
26401                 item.disable();
26402             }
26403         });
26404         this.rendered = true;
26405         
26406         // the all the btns;
26407         editor.on('editorevent', this.updateToolbar, this);
26408         // other toolbars need to implement this..
26409         //editor.on('editmodechange', this.updateToolbar, this);
26410     },
26411     
26412     
26413     
26414     /**
26415      * Protected method that will not generally be called directly. It triggers
26416      * a toolbar update by reading the markup state of the current selection in the editor.
26417      */
26418     updateToolbar: function(){
26419
26420         if(!this.editor.activated){
26421             this.editor.onFirstFocus();
26422             return;
26423         }
26424
26425         var btns = this.tb.items.map, 
26426             doc = this.editor.doc,
26427             frameId = this.editor.frameId;
26428
26429         if(!this.disable.font && !Roo.isSafari){
26430             /*
26431             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26432             if(name != this.fontSelect.dom.value){
26433                 this.fontSelect.dom.value = name;
26434             }
26435             */
26436         }
26437         if(!this.disable.format){
26438             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26439             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26440             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26441         }
26442         if(!this.disable.alignments){
26443             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26444             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26445             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26446         }
26447         if(!Roo.isSafari && !this.disable.lists){
26448             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26449             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26450         }
26451         
26452         var ans = this.editor.getAllAncestors();
26453         if (this.formatCombo) {
26454             
26455             
26456             var store = this.formatCombo.store;
26457             this.formatCombo.setValue("");
26458             for (var i =0; i < ans.length;i++) {
26459                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26460                     // select it..
26461                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26462                     break;
26463                 }
26464             }
26465         }
26466         
26467         
26468         
26469         // hides menus... - so this cant be on a menu...
26470         Roo.menu.MenuMgr.hideAll();
26471
26472         //this.editorsyncValue();
26473     },
26474    
26475     
26476     createFontOptions : function(){
26477         var buf = [], fs = this.fontFamilies, ff, lc;
26478         for(var i = 0, len = fs.length; i< len; i++){
26479             ff = fs[i];
26480             lc = ff.toLowerCase();
26481             buf.push(
26482                 '<option value="',lc,'" style="font-family:',ff,';"',
26483                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26484                     ff,
26485                 '</option>'
26486             );
26487         }
26488         return buf.join('');
26489     },
26490     
26491     toggleSourceEdit : function(sourceEditMode){
26492         if(sourceEditMode === undefined){
26493             sourceEditMode = !this.sourceEditMode;
26494         }
26495         this.sourceEditMode = sourceEditMode === true;
26496         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26497         // just toggle the button?
26498         if(btn.pressed !== this.editor.sourceEditMode){
26499             btn.toggle(this.editor.sourceEditMode);
26500             return;
26501         }
26502         
26503         if(this.sourceEditMode){
26504             this.tb.items.each(function(item){
26505                 if(item.cmd != 'sourceedit'){
26506                     item.disable();
26507                 }
26508             });
26509           
26510         }else{
26511             if(this.initialized){
26512                 this.tb.items.each(function(item){
26513                     item.enable();
26514                 });
26515             }
26516             
26517         }
26518         // tell the editor that it's been pressed..
26519         this.editor.toggleSourceEdit(sourceEditMode);
26520        
26521     },
26522      /**
26523      * Object collection of toolbar tooltips for the buttons in the editor. The key
26524      * is the command id associated with that button and the value is a valid QuickTips object.
26525      * For example:
26526 <pre><code>
26527 {
26528     bold : {
26529         title: 'Bold (Ctrl+B)',
26530         text: 'Make the selected text bold.',
26531         cls: 'x-html-editor-tip'
26532     },
26533     italic : {
26534         title: 'Italic (Ctrl+I)',
26535         text: 'Make the selected text italic.',
26536         cls: 'x-html-editor-tip'
26537     },
26538     ...
26539 </code></pre>
26540     * @type Object
26541      */
26542     buttonTips : {
26543         bold : {
26544             title: 'Bold (Ctrl+B)',
26545             text: 'Make the selected text bold.',
26546             cls: 'x-html-editor-tip'
26547         },
26548         italic : {
26549             title: 'Italic (Ctrl+I)',
26550             text: 'Make the selected text italic.',
26551             cls: 'x-html-editor-tip'
26552         },
26553         underline : {
26554             title: 'Underline (Ctrl+U)',
26555             text: 'Underline the selected text.',
26556             cls: 'x-html-editor-tip'
26557         },
26558         increasefontsize : {
26559             title: 'Grow Text',
26560             text: 'Increase the font size.',
26561             cls: 'x-html-editor-tip'
26562         },
26563         decreasefontsize : {
26564             title: 'Shrink Text',
26565             text: 'Decrease the font size.',
26566             cls: 'x-html-editor-tip'
26567         },
26568         backcolor : {
26569             title: 'Text Highlight Color',
26570             text: 'Change the background color of the selected text.',
26571             cls: 'x-html-editor-tip'
26572         },
26573         forecolor : {
26574             title: 'Font Color',
26575             text: 'Change the color of the selected text.',
26576             cls: 'x-html-editor-tip'
26577         },
26578         justifyleft : {
26579             title: 'Align Text Left',
26580             text: 'Align text to the left.',
26581             cls: 'x-html-editor-tip'
26582         },
26583         justifycenter : {
26584             title: 'Center Text',
26585             text: 'Center text in the editor.',
26586             cls: 'x-html-editor-tip'
26587         },
26588         justifyright : {
26589             title: 'Align Text Right',
26590             text: 'Align text to the right.',
26591             cls: 'x-html-editor-tip'
26592         },
26593         insertunorderedlist : {
26594             title: 'Bullet List',
26595             text: 'Start a bulleted list.',
26596             cls: 'x-html-editor-tip'
26597         },
26598         insertorderedlist : {
26599             title: 'Numbered List',
26600             text: 'Start a numbered list.',
26601             cls: 'x-html-editor-tip'
26602         },
26603         createlink : {
26604             title: 'Hyperlink',
26605             text: 'Make the selected text a hyperlink.',
26606             cls: 'x-html-editor-tip'
26607         },
26608         sourceedit : {
26609             title: 'Source Edit',
26610             text: 'Switch to source editing mode.',
26611             cls: 'x-html-editor-tip'
26612         }
26613     },
26614     // private
26615     onDestroy : function(){
26616         if(this.rendered){
26617             
26618             this.tb.items.each(function(item){
26619                 if(item.menu){
26620                     item.menu.removeAll();
26621                     if(item.menu.el){
26622                         item.menu.el.destroy();
26623                     }
26624                 }
26625                 item.destroy();
26626             });
26627              
26628         }
26629     },
26630     onFirstFocus: function() {
26631         this.tb.items.each(function(item){
26632            item.enable();
26633         });
26634     }
26635 });
26636
26637
26638
26639
26640 // <script type="text/javascript">
26641 /*
26642  * Based on
26643  * Ext JS Library 1.1.1
26644  * Copyright(c) 2006-2007, Ext JS, LLC.
26645  *  
26646  
26647  */
26648
26649  
26650 /**
26651  * @class Roo.form.HtmlEditor.ToolbarContext
26652  * Context Toolbar
26653  * 
26654  * Usage:
26655  *
26656  new Roo.form.HtmlEditor({
26657     ....
26658     toolbars : [
26659         { xtype: 'ToolbarStandard', styles : {} }
26660         { xtype: 'ToolbarContext', disable : {} }
26661     ]
26662 })
26663
26664      
26665  * 
26666  * @config : {Object} disable List of elements to disable.. (not done yet.)
26667  * @config : {Object} styles  Map of styles available.
26668  * 
26669  */
26670
26671 Roo.form.HtmlEditor.ToolbarContext = function(config)
26672 {
26673     
26674     Roo.apply(this, config);
26675     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26676     // dont call parent... till later.
26677     this.styles = this.styles || {};
26678 }
26679 Roo.form.HtmlEditor.ToolbarContext.types = {
26680     'IMG' : {
26681         width : {
26682             title: "Width",
26683             width: 40
26684         },
26685         height:  {
26686             title: "Height",
26687             width: 40
26688         },
26689         align: {
26690             title: "Align",
26691             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26692             width : 80
26693             
26694         },
26695         border: {
26696             title: "Border",
26697             width: 40
26698         },
26699         alt: {
26700             title: "Alt",
26701             width: 120
26702         },
26703         src : {
26704             title: "Src",
26705             width: 220
26706         }
26707         
26708     },
26709     'A' : {
26710         name : {
26711             title: "Name",
26712             width: 50
26713         },
26714         href:  {
26715             title: "Href",
26716             width: 220
26717         } // border?
26718         
26719     },
26720     'TABLE' : {
26721         rows : {
26722             title: "Rows",
26723             width: 20
26724         },
26725         cols : {
26726             title: "Cols",
26727             width: 20
26728         },
26729         width : {
26730             title: "Width",
26731             width: 40
26732         },
26733         height : {
26734             title: "Height",
26735             width: 40
26736         },
26737         border : {
26738             title: "Border",
26739             width: 20
26740         }
26741     },
26742     'TD' : {
26743         width : {
26744             title: "Width",
26745             width: 40
26746         },
26747         height : {
26748             title: "Height",
26749             width: 40
26750         },   
26751         align: {
26752             title: "Align",
26753             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26754             width: 80
26755         },
26756         valign: {
26757             title: "Valign",
26758             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26759             width: 80
26760         },
26761         colspan: {
26762             title: "Colspan",
26763             width: 20
26764             
26765         }
26766     },
26767     'INPUT' : {
26768         name : {
26769             title: "name",
26770             width: 120
26771         },
26772         value : {
26773             title: "Value",
26774             width: 120
26775         },
26776         width : {
26777             title: "Width",
26778             width: 40
26779         }
26780     },
26781     'LABEL' : {
26782         'for' : {
26783             title: "For",
26784             width: 120
26785         }
26786     },
26787     'TEXTAREA' : {
26788           name : {
26789             title: "name",
26790             width: 120
26791         },
26792         rows : {
26793             title: "Rows",
26794             width: 20
26795         },
26796         cols : {
26797             title: "Cols",
26798             width: 20
26799         }
26800     },
26801     'SELECT' : {
26802         name : {
26803             title: "name",
26804             width: 120
26805         },
26806         selectoptions : {
26807             title: "Options",
26808             width: 200
26809         }
26810     },
26811     
26812     // should we really allow this??
26813     // should this just be 
26814     'BODY' : {
26815         title : {
26816             title: "title",
26817             width: 200,
26818             disabled : true
26819         }
26820     },
26821     '*' : {
26822         // empty..
26823     }
26824 };
26825
26826
26827
26828 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26829     
26830     tb: false,
26831     
26832     rendered: false,
26833     
26834     editor : false,
26835     /**
26836      * @cfg {Object} disable  List of toolbar elements to disable
26837          
26838      */
26839     disable : false,
26840     /**
26841      * @cfg {Object} styles List of styles 
26842      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26843      *
26844      * These must be defined in the page, so they get rendered correctly..
26845      * .headline { }
26846      * TD.underline { }
26847      * 
26848      */
26849     styles : false,
26850     
26851     
26852     
26853     toolbars : false,
26854     
26855     init : function(editor)
26856     {
26857         this.editor = editor;
26858         
26859         
26860         var fid = editor.frameId;
26861         var etb = this;
26862         function btn(id, toggle, handler){
26863             var xid = fid + '-'+ id ;
26864             return {
26865                 id : xid,
26866                 cmd : id,
26867                 cls : 'x-btn-icon x-edit-'+id,
26868                 enableToggle:toggle !== false,
26869                 scope: editor, // was editor...
26870                 handler:handler||editor.relayBtnCmd,
26871                 clickEvent:'mousedown',
26872                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26873                 tabIndex:-1
26874             };
26875         }
26876         // create a new element.
26877         var wdiv = editor.wrap.createChild({
26878                 tag: 'div'
26879             }, editor.wrap.dom.firstChild.nextSibling, true);
26880         
26881         // can we do this more than once??
26882         
26883          // stop form submits
26884       
26885  
26886         // disable everything...
26887         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26888         this.toolbars = {};
26889            
26890         for (var i in  ty) {
26891           
26892             this.toolbars[i] = this.buildToolbar(ty[i],i);
26893         }
26894         this.tb = this.toolbars.BODY;
26895         this.tb.el.show();
26896         this.buildFooter();
26897         this.footer.show();
26898         editor.on('hide', function( ) { this.footer.hide() }, this);
26899         editor.on('show', function( ) { this.footer.show() }, this);
26900         
26901          
26902         this.rendered = true;
26903         
26904         // the all the btns;
26905         editor.on('editorevent', this.updateToolbar, this);
26906         // other toolbars need to implement this..
26907         //editor.on('editmodechange', this.updateToolbar, this);
26908     },
26909     
26910     
26911     
26912     /**
26913      * Protected method that will not generally be called directly. It triggers
26914      * a toolbar update by reading the markup state of the current selection in the editor.
26915      */
26916     updateToolbar: function(editor,ev,sel){
26917
26918         //Roo.log(ev);
26919         // capture mouse up - this is handy for selecting images..
26920         // perhaps should go somewhere else...
26921         if(!this.editor.activated){
26922              this.editor.onFirstFocus();
26923             return;
26924         }
26925         
26926         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
26927         // selectNode - might want to handle IE?
26928         if (ev &&
26929             (ev.type == 'mouseup' || ev.type == 'click' ) &&
26930             ev.target && ev.target.tagName == 'IMG') {
26931             // they have click on an image...
26932             // let's see if we can change the selection...
26933             sel = ev.target;
26934          
26935               var nodeRange = sel.ownerDocument.createRange();
26936             try {
26937                 nodeRange.selectNode(sel);
26938             } catch (e) {
26939                 nodeRange.selectNodeContents(sel);
26940             }
26941             //nodeRange.collapse(true);
26942             var s = editor.win.getSelection();
26943             s.removeAllRanges();
26944             s.addRange(nodeRange);
26945         }  
26946         
26947       
26948         var updateFooter = sel ? false : true;
26949         
26950         
26951         var ans = this.editor.getAllAncestors();
26952         
26953         // pick
26954         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26955         
26956         if (!sel) { 
26957             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26958             sel = sel ? sel : this.editor.doc.body;
26959             sel = sel.tagName.length ? sel : this.editor.doc.body;
26960             
26961         }
26962         // pick a menu that exists..
26963         var tn = sel.tagName.toUpperCase();
26964         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26965         
26966         tn = sel.tagName.toUpperCase();
26967         
26968         var lastSel = this.tb.selectedNode
26969         
26970         this.tb.selectedNode = sel;
26971         
26972         // if current menu does not match..
26973         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26974                 
26975             this.tb.el.hide();
26976             ///console.log("show: " + tn);
26977             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26978             this.tb.el.show();
26979             // update name
26980             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26981             
26982             
26983             // update attributes
26984             if (this.tb.fields) {
26985                 this.tb.fields.each(function(e) {
26986                    e.setValue(sel.getAttribute(e.attrname));
26987                 });
26988             }
26989             
26990             var hasStyles = false;
26991             for(var i in this.styles) {
26992                 hasStyles = true;
26993                 break;
26994             }
26995             
26996             // update styles
26997             if (hasStyles) { 
26998                 var st = this.tb.fields.item(0);
26999                 
27000                 st.store.removeAll();
27001                
27002                 
27003                 var cn = sel.className.split(/\s+/);
27004                 
27005                 var avs = [];
27006                 if (this.styles['*']) {
27007                     
27008                     Roo.each(this.styles['*'], function(v) {
27009                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27010                     });
27011                 }
27012                 if (this.styles[tn]) { 
27013                     Roo.each(this.styles[tn], function(v) {
27014                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27015                     });
27016                 }
27017                 
27018                 st.store.loadData(avs);
27019                 st.collapse();
27020                 st.setValue(cn);
27021             }
27022             // flag our selected Node.
27023             this.tb.selectedNode = sel;
27024            
27025            
27026             Roo.menu.MenuMgr.hideAll();
27027
27028         }
27029         
27030         if (!updateFooter) {
27031             return;
27032         }
27033         // update the footer
27034         //
27035         var html = '';
27036         
27037         this.footerEls = ans.reverse();
27038         Roo.each(this.footerEls, function(a,i) {
27039             if (!a) { return; }
27040             html += html.length ? ' &gt; '  :  '';
27041             
27042             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27043             
27044         });
27045        
27046         // 
27047         var sz = this.footDisp.up('td').getSize();
27048         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27049         this.footDisp.dom.style.marginLeft = '5px';
27050         
27051         this.footDisp.dom.style.overflow = 'hidden';
27052         
27053         this.footDisp.dom.innerHTML = html;
27054             
27055         //this.editorsyncValue();
27056     },
27057    
27058        
27059     // private
27060     onDestroy : function(){
27061         if(this.rendered){
27062             
27063             this.tb.items.each(function(item){
27064                 if(item.menu){
27065                     item.menu.removeAll();
27066                     if(item.menu.el){
27067                         item.menu.el.destroy();
27068                     }
27069                 }
27070                 item.destroy();
27071             });
27072              
27073         }
27074     },
27075     onFirstFocus: function() {
27076         // need to do this for all the toolbars..
27077         this.tb.items.each(function(item){
27078            item.enable();
27079         });
27080     },
27081     buildToolbar: function(tlist, nm)
27082     {
27083         var editor = this.editor;
27084          // create a new element.
27085         var wdiv = editor.wrap.createChild({
27086                 tag: 'div'
27087             }, editor.wrap.dom.firstChild.nextSibling, true);
27088         
27089        
27090         var tb = new Roo.Toolbar(wdiv);
27091         // add the name..
27092         
27093         tb.add(nm+ ":&nbsp;");
27094         
27095         var styles = [];
27096         for(var i in this.styles) {
27097             styles.push(i);
27098         }
27099         
27100         // styles...
27101         if (styles && styles.length) {
27102             
27103             // this needs a multi-select checkbox...
27104             tb.addField( new Roo.form.ComboBox({
27105                 store: new Roo.data.SimpleStore({
27106                     id : 'val',
27107                     fields: ['val', 'selected'],
27108                     data : [] 
27109                 }),
27110                 name : '-roo-edit-className',
27111                 attrname : 'className',
27112                 displayField:'val',
27113                 typeAhead: false,
27114                 mode: 'local',
27115                 editable : false,
27116                 triggerAction: 'all',
27117                 emptyText:'Select Style',
27118                 selectOnFocus:true,
27119                 width: 130,
27120                 listeners : {
27121                     'select': function(c, r, i) {
27122                         // initial support only for on class per el..
27123                         tb.selectedNode.className =  r ? r.get('val') : '';
27124                         editor.syncValue();
27125                     }
27126                 }
27127     
27128             }));
27129         }
27130             
27131         
27132         
27133         for (var i in tlist) {
27134             
27135             var item = tlist[i];
27136             tb.add(item.title + ":&nbsp;");
27137             
27138             
27139             
27140             
27141             if (item.opts) {
27142                 // opts == pulldown..
27143                 tb.addField( new Roo.form.ComboBox({
27144                     store: new Roo.data.SimpleStore({
27145                         id : 'val',
27146                         fields: ['val'],
27147                         data : item.opts  
27148                     }),
27149                     name : '-roo-edit-' + i,
27150                     attrname : i,
27151                     displayField:'val',
27152                     typeAhead: false,
27153                     mode: 'local',
27154                     editable : false,
27155                     triggerAction: 'all',
27156                     emptyText:'Select',
27157                     selectOnFocus:true,
27158                     width: item.width ? item.width  : 130,
27159                     listeners : {
27160                         'select': function(c, r, i) {
27161                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27162                         }
27163                     }
27164
27165                 }));
27166                 continue;
27167                     
27168                  
27169                 
27170                 tb.addField( new Roo.form.TextField({
27171                     name: i,
27172                     width: 100,
27173                     //allowBlank:false,
27174                     value: ''
27175                 }));
27176                 continue;
27177             }
27178             tb.addField( new Roo.form.TextField({
27179                 name: '-roo-edit-' + i,
27180                 attrname : i,
27181                 
27182                 width: item.width,
27183                 //allowBlank:true,
27184                 value: '',
27185                 listeners: {
27186                     'change' : function(f, nv, ov) {
27187                         tb.selectedNode.setAttribute(f.attrname, nv);
27188                     }
27189                 }
27190             }));
27191              
27192         }
27193         tb.el.on('click', function(e){
27194             e.preventDefault(); // what does this do?
27195         });
27196         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27197         tb.el.hide();
27198         tb.name = nm;
27199         // dont need to disable them... as they will get hidden
27200         return tb;
27201          
27202         
27203     },
27204     buildFooter : function()
27205     {
27206         
27207         var fel = this.editor.wrap.createChild();
27208         this.footer = new Roo.Toolbar(fel);
27209         // toolbar has scrolly on left / right?
27210         var footDisp= new Roo.Toolbar.Fill();
27211         var _t = this;
27212         this.footer.add(
27213             {
27214                 text : '&lt;',
27215                 xtype: 'Button',
27216                 handler : function() {
27217                     _t.footDisp.scrollTo('left',0,true)
27218                 }
27219             }
27220         );
27221         this.footer.add( footDisp );
27222         this.footer.add( 
27223             {
27224                 text : '&gt;',
27225                 xtype: 'Button',
27226                 handler : function() {
27227                     // no animation..
27228                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27229                 }
27230             }
27231         );
27232         var fel = Roo.get(footDisp.el);
27233         fel.addClass('x-editor-context');
27234         this.footDispWrap = fel; 
27235         this.footDispWrap.overflow  = 'hidden';
27236         
27237         this.footDisp = fel.createChild();
27238         this.footDispWrap.on('click', this.onContextClick, this)
27239         
27240         
27241     },
27242     onContextClick : function (ev,dom)
27243     {
27244         ev.preventDefault();
27245         var  cn = dom.className;
27246         Roo.log(cn);
27247         if (!cn.match(/x-ed-loc-/)) {
27248             return;
27249         }
27250         var n = cn.split('-').pop();
27251         var ans = this.footerEls;
27252         var sel = ans[n];
27253         
27254          // pick
27255         var range = this.editor.createRange();
27256         
27257         range.selectNodeContents(sel);
27258         //range.selectNode(sel);
27259         
27260         
27261         var selection = this.editor.getSelection();
27262         selection.removeAllRanges();
27263         selection.addRange(range);
27264         
27265         
27266         
27267         this.updateToolbar(null, null, sel);
27268         
27269         
27270     }
27271     
27272     
27273     
27274     
27275     
27276 });
27277
27278
27279
27280
27281
27282 /*
27283  * Based on:
27284  * Ext JS Library 1.1.1
27285  * Copyright(c) 2006-2007, Ext JS, LLC.
27286  *
27287  * Originally Released Under LGPL - original licence link has changed is not relivant.
27288  *
27289  * Fork - LGPL
27290  * <script type="text/javascript">
27291  */
27292  
27293 /**
27294  * @class Roo.form.BasicForm
27295  * @extends Roo.util.Observable
27296  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27297  * @constructor
27298  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27299  * @param {Object} config Configuration options
27300  */
27301 Roo.form.BasicForm = function(el, config){
27302     this.allItems = [];
27303     this.childForms = [];
27304     Roo.apply(this, config);
27305     /*
27306      * The Roo.form.Field items in this form.
27307      * @type MixedCollection
27308      */
27309      
27310      
27311     this.items = new Roo.util.MixedCollection(false, function(o){
27312         return o.id || (o.id = Roo.id());
27313     });
27314     this.addEvents({
27315         /**
27316          * @event beforeaction
27317          * Fires before any action is performed. Return false to cancel the action.
27318          * @param {Form} this
27319          * @param {Action} action The action to be performed
27320          */
27321         beforeaction: true,
27322         /**
27323          * @event actionfailed
27324          * Fires when an action fails.
27325          * @param {Form} this
27326          * @param {Action} action The action that failed
27327          */
27328         actionfailed : true,
27329         /**
27330          * @event actioncomplete
27331          * Fires when an action is completed.
27332          * @param {Form} this
27333          * @param {Action} action The action that completed
27334          */
27335         actioncomplete : true
27336     });
27337     if(el){
27338         this.initEl(el);
27339     }
27340     Roo.form.BasicForm.superclass.constructor.call(this);
27341 };
27342
27343 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27344     /**
27345      * @cfg {String} method
27346      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27347      */
27348     /**
27349      * @cfg {DataReader} reader
27350      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27351      * This is optional as there is built-in support for processing JSON.
27352      */
27353     /**
27354      * @cfg {DataReader} errorReader
27355      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27356      * This is completely optional as there is built-in support for processing JSON.
27357      */
27358     /**
27359      * @cfg {String} url
27360      * The URL to use for form actions if one isn't supplied in the action options.
27361      */
27362     /**
27363      * @cfg {Boolean} fileUpload
27364      * Set to true if this form is a file upload.
27365      */
27366      
27367     /**
27368      * @cfg {Object} baseParams
27369      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27370      */
27371      /**
27372      
27373     /**
27374      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27375      */
27376     timeout: 30,
27377
27378     // private
27379     activeAction : null,
27380
27381     /**
27382      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27383      * or setValues() data instead of when the form was first created.
27384      */
27385     trackResetOnLoad : false,
27386     
27387     
27388     /**
27389      * childForms - used for multi-tab forms
27390      * @type {Array}
27391      */
27392     childForms : false,
27393     
27394     /**
27395      * allItems - full list of fields.
27396      * @type {Array}
27397      */
27398     allItems : false,
27399     
27400     /**
27401      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27402      * element by passing it or its id or mask the form itself by passing in true.
27403      * @type Mixed
27404      */
27405     waitMsgTarget : false,
27406
27407     // private
27408     initEl : function(el){
27409         this.el = Roo.get(el);
27410         this.id = this.el.id || Roo.id();
27411         this.el.on('submit', this.onSubmit, this);
27412         this.el.addClass('x-form');
27413     },
27414
27415     // private
27416     onSubmit : function(e){
27417         e.stopEvent();
27418     },
27419
27420     /**
27421      * Returns true if client-side validation on the form is successful.
27422      * @return Boolean
27423      */
27424     isValid : function(){
27425         var valid = true;
27426         this.items.each(function(f){
27427            if(!f.validate()){
27428                valid = false;
27429            }
27430         });
27431         return valid;
27432     },
27433
27434     /**
27435      * Returns true if any fields in this form have changed since their original load.
27436      * @return Boolean
27437      */
27438     isDirty : function(){
27439         var dirty = false;
27440         this.items.each(function(f){
27441            if(f.isDirty()){
27442                dirty = true;
27443                return false;
27444            }
27445         });
27446         return dirty;
27447     },
27448
27449     /**
27450      * Performs a predefined action (submit or load) or custom actions you define on this form.
27451      * @param {String} actionName The name of the action type
27452      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27453      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27454      * accept other config options):
27455      * <pre>
27456 Property          Type             Description
27457 ----------------  ---------------  ----------------------------------------------------------------------------------
27458 url               String           The url for the action (defaults to the form's url)
27459 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27460 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27461 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27462                                    validate the form on the client (defaults to false)
27463      * </pre>
27464      * @return {BasicForm} this
27465      */
27466     doAction : function(action, options){
27467         if(typeof action == 'string'){
27468             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27469         }
27470         if(this.fireEvent('beforeaction', this, action) !== false){
27471             this.beforeAction(action);
27472             action.run.defer(100, action);
27473         }
27474         return this;
27475     },
27476
27477     /**
27478      * Shortcut to do a submit action.
27479      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27480      * @return {BasicForm} this
27481      */
27482     submit : function(options){
27483         this.doAction('submit', options);
27484         return this;
27485     },
27486
27487     /**
27488      * Shortcut to do a load action.
27489      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27490      * @return {BasicForm} this
27491      */
27492     load : function(options){
27493         this.doAction('load', options);
27494         return this;
27495     },
27496
27497     /**
27498      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27499      * @param {Record} record The record to edit
27500      * @return {BasicForm} this
27501      */
27502     updateRecord : function(record){
27503         record.beginEdit();
27504         var fs = record.fields;
27505         fs.each(function(f){
27506             var field = this.findField(f.name);
27507             if(field){
27508                 record.set(f.name, field.getValue());
27509             }
27510         }, this);
27511         record.endEdit();
27512         return this;
27513     },
27514
27515     /**
27516      * Loads an Roo.data.Record into this form.
27517      * @param {Record} record The record to load
27518      * @return {BasicForm} this
27519      */
27520     loadRecord : function(record){
27521         this.setValues(record.data);
27522         return this;
27523     },
27524
27525     // private
27526     beforeAction : function(action){
27527         var o = action.options;
27528         
27529        
27530         if(this.waitMsgTarget === true){
27531             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27532         }else if(this.waitMsgTarget){
27533             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27534             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27535         }else {
27536             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27537         }
27538          
27539     },
27540
27541     // private
27542     afterAction : function(action, success){
27543         this.activeAction = null;
27544         var o = action.options;
27545         
27546         if(this.waitMsgTarget === true){
27547             this.el.unmask();
27548         }else if(this.waitMsgTarget){
27549             this.waitMsgTarget.unmask();
27550         }else{
27551             Roo.MessageBox.updateProgress(1);
27552             Roo.MessageBox.hide();
27553         }
27554          
27555         if(success){
27556             if(o.reset){
27557                 this.reset();
27558             }
27559             Roo.callback(o.success, o.scope, [this, action]);
27560             this.fireEvent('actioncomplete', this, action);
27561             
27562         }else{
27563             
27564             // failure condition..
27565             // we have a scenario where updates need confirming.
27566             // eg. if a locking scenario exists..
27567             // we look for { errors : { needs_confirm : true }} in the response.
27568             if (
27569                 (typeof(action.result) != 'undefined')  &&
27570                 (typeof(action.result.errors) != 'undefined')  &&
27571                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27572           ){
27573                 var _t = this;
27574                 Roo.MessageBox.confirm(
27575                     "Change requires confirmation",
27576                     action.result.errorMsg,
27577                     function(r) {
27578                         if (r != 'yes') {
27579                             return;
27580                         }
27581                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27582                     }
27583                     
27584                 );
27585                 
27586                 
27587                 
27588                 return;
27589             }
27590             
27591             Roo.callback(o.failure, o.scope, [this, action]);
27592             // show an error message if no failed handler is set..
27593             if (!this.hasListener('actionfailed')) {
27594                 Roo.MessageBox.alert("Error",
27595                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27596                         action.result.errorMsg :
27597                         "Saving Failed, please check your entries or try again"
27598                 );
27599             }
27600             
27601             this.fireEvent('actionfailed', this, action);
27602         }
27603         
27604     },
27605
27606     /**
27607      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27608      * @param {String} id The value to search for
27609      * @return Field
27610      */
27611     findField : function(id){
27612         var field = this.items.get(id);
27613         if(!field){
27614             this.items.each(function(f){
27615                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27616                     field = f;
27617                     return false;
27618                 }
27619             });
27620         }
27621         return field || null;
27622     },
27623
27624     /**
27625      * Add a secondary form to this one, 
27626      * Used to provide tabbed forms. One form is primary, with hidden values 
27627      * which mirror the elements from the other forms.
27628      * 
27629      * @param {Roo.form.Form} form to add.
27630      * 
27631      */
27632     addForm : function(form)
27633     {
27634        
27635         if (this.childForms.indexOf(form) > -1) {
27636             // already added..
27637             return;
27638         }
27639         this.childForms.push(form);
27640         var n = '';
27641         Roo.each(form.allItems, function (fe) {
27642             
27643             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27644             if (this.findField(n)) { // already added..
27645                 return;
27646             }
27647             var add = new Roo.form.Hidden({
27648                 name : n
27649             });
27650             add.render(this.el);
27651             
27652             this.add( add );
27653         }, this);
27654         
27655     },
27656     /**
27657      * Mark fields in this form invalid in bulk.
27658      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27659      * @return {BasicForm} this
27660      */
27661     markInvalid : function(errors){
27662         if(errors instanceof Array){
27663             for(var i = 0, len = errors.length; i < len; i++){
27664                 var fieldError = errors[i];
27665                 var f = this.findField(fieldError.id);
27666                 if(f){
27667                     f.markInvalid(fieldError.msg);
27668                 }
27669             }
27670         }else{
27671             var field, id;
27672             for(id in errors){
27673                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27674                     field.markInvalid(errors[id]);
27675                 }
27676             }
27677         }
27678         Roo.each(this.childForms || [], function (f) {
27679             f.markInvalid(errors);
27680         });
27681         
27682         return this;
27683     },
27684
27685     /**
27686      * Set values for fields in this form in bulk.
27687      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27688      * @return {BasicForm} this
27689      */
27690     setValues : function(values){
27691         if(values instanceof Array){ // array of objects
27692             for(var i = 0, len = values.length; i < len; i++){
27693                 var v = values[i];
27694                 var f = this.findField(v.id);
27695                 if(f){
27696                     f.setValue(v.value);
27697                     if(this.trackResetOnLoad){
27698                         f.originalValue = f.getValue();
27699                     }
27700                 }
27701             }
27702         }else{ // object hash
27703             var field, id;
27704             for(id in values){
27705                 if(typeof values[id] != 'function' && (field = this.findField(id))){
27706                     
27707                     if (field.setFromData && 
27708                         field.valueField && 
27709                         field.displayField &&
27710                         // combos' with local stores can 
27711                         // be queried via setValue()
27712                         // to set their value..
27713                         (field.store && !field.store.isLocal)
27714                         ) {
27715                         // it's a combo
27716                         var sd = { };
27717                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27718                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27719                         field.setFromData(sd);
27720                         
27721                     } else {
27722                         field.setValue(values[id]);
27723                     }
27724                     
27725                     
27726                     if(this.trackResetOnLoad){
27727                         field.originalValue = field.getValue();
27728                     }
27729                 }
27730             }
27731         }
27732          
27733         Roo.each(this.childForms || [], function (f) {
27734             f.setValues(values);
27735         });
27736                 
27737         return this;
27738     },
27739
27740     /**
27741      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27742      * they are returned as an array.
27743      * @param {Boolean} asString
27744      * @return {Object}
27745      */
27746     getValues : function(asString){
27747         if (this.childForms) {
27748             // copy values from the child forms
27749             Roo.each(this.childForms, function (f) {
27750                 this.setValues(f.getValues());
27751             }, this);
27752         }
27753         
27754         
27755         
27756         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27757         if(asString === true){
27758             return fs;
27759         }
27760         return Roo.urlDecode(fs);
27761     },
27762     
27763     /**
27764      * Returns the fields in this form as an object with key/value pairs. 
27765      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27766      * @return {Object}
27767      */
27768     getFieldValues : function(with_hidden)
27769     {
27770         if (this.childForms) {
27771             // copy values from the child forms
27772             // should this call getFieldValues - probably not as we do not currently copy
27773             // hidden fields when we generate..
27774             Roo.each(this.childForms, function (f) {
27775                 this.setValues(f.getValues());
27776             }, this);
27777         }
27778         
27779         var ret = {};
27780         this.items.each(function(f){
27781             if (!f.getName()) {
27782                 return;
27783             }
27784             var v = f.getValue();
27785             // not sure if this supported any more..
27786             if ((typeof(v) == 'object') && f.getRawValue) {
27787                 v = f.getRawValue() ; // dates..
27788             }
27789             // combo boxes where name != hiddenName...
27790             if (f.name != f.getName()) {
27791                 ret[f.name] = f.getRawValue();
27792             }
27793             ret[f.getName()] = v;
27794         });
27795         
27796         return ret;
27797     },
27798
27799     /**
27800      * Clears all invalid messages in this form.
27801      * @return {BasicForm} this
27802      */
27803     clearInvalid : function(){
27804         this.items.each(function(f){
27805            f.clearInvalid();
27806         });
27807         
27808         Roo.each(this.childForms || [], function (f) {
27809             f.clearInvalid();
27810         });
27811         
27812         
27813         return this;
27814     },
27815
27816     /**
27817      * Resets this form.
27818      * @return {BasicForm} this
27819      */
27820     reset : function(){
27821         this.items.each(function(f){
27822             f.reset();
27823         });
27824         
27825         Roo.each(this.childForms || [], function (f) {
27826             f.reset();
27827         });
27828        
27829         
27830         return this;
27831     },
27832
27833     /**
27834      * Add Roo.form components to this form.
27835      * @param {Field} field1
27836      * @param {Field} field2 (optional)
27837      * @param {Field} etc (optional)
27838      * @return {BasicForm} this
27839      */
27840     add : function(){
27841         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27842         return this;
27843     },
27844
27845
27846     /**
27847      * Removes a field from the items collection (does NOT remove its markup).
27848      * @param {Field} field
27849      * @return {BasicForm} this
27850      */
27851     remove : function(field){
27852         this.items.remove(field);
27853         return this;
27854     },
27855
27856     /**
27857      * Looks at the fields in this form, checks them for an id attribute,
27858      * and calls applyTo on the existing dom element with that id.
27859      * @return {BasicForm} this
27860      */
27861     render : function(){
27862         this.items.each(function(f){
27863             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27864                 f.applyTo(f.id);
27865             }
27866         });
27867         return this;
27868     },
27869
27870     /**
27871      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27872      * @param {Object} values
27873      * @return {BasicForm} this
27874      */
27875     applyToFields : function(o){
27876         this.items.each(function(f){
27877            Roo.apply(f, o);
27878         });
27879         return this;
27880     },
27881
27882     /**
27883      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27884      * @param {Object} values
27885      * @return {BasicForm} this
27886      */
27887     applyIfToFields : function(o){
27888         this.items.each(function(f){
27889            Roo.applyIf(f, o);
27890         });
27891         return this;
27892     }
27893 });
27894
27895 // back compat
27896 Roo.BasicForm = Roo.form.BasicForm;/*
27897  * Based on:
27898  * Ext JS Library 1.1.1
27899  * Copyright(c) 2006-2007, Ext JS, LLC.
27900  *
27901  * Originally Released Under LGPL - original licence link has changed is not relivant.
27902  *
27903  * Fork - LGPL
27904  * <script type="text/javascript">
27905  */
27906
27907 /**
27908  * @class Roo.form.Form
27909  * @extends Roo.form.BasicForm
27910  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27911  * @constructor
27912  * @param {Object} config Configuration options
27913  */
27914 Roo.form.Form = function(config){
27915     var xitems =  [];
27916     if (config.items) {
27917         xitems = config.items;
27918         delete config.items;
27919     }
27920    
27921     
27922     Roo.form.Form.superclass.constructor.call(this, null, config);
27923     this.url = this.url || this.action;
27924     if(!this.root){
27925         this.root = new Roo.form.Layout(Roo.applyIf({
27926             id: Roo.id()
27927         }, config));
27928     }
27929     this.active = this.root;
27930     /**
27931      * Array of all the buttons that have been added to this form via {@link addButton}
27932      * @type Array
27933      */
27934     this.buttons = [];
27935     this.allItems = [];
27936     this.addEvents({
27937         /**
27938          * @event clientvalidation
27939          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27940          * @param {Form} this
27941          * @param {Boolean} valid true if the form has passed client-side validation
27942          */
27943         clientvalidation: true,
27944         /**
27945          * @event rendered
27946          * Fires when the form is rendered
27947          * @param {Roo.form.Form} form
27948          */
27949         rendered : true
27950     });
27951     
27952     if (this.progressUrl) {
27953             // push a hidden field onto the list of fields..
27954             this.addxtype( {
27955                     xns: Roo.form, 
27956                     xtype : 'Hidden', 
27957                     name : 'UPLOAD_IDENTIFIER' 
27958             });
27959         }
27960         
27961     
27962     Roo.each(xitems, this.addxtype, this);
27963     
27964     
27965     
27966 };
27967
27968 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27969     /**
27970      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27971      */
27972     /**
27973      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27974      */
27975     /**
27976      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27977      */
27978     buttonAlign:'center',
27979
27980     /**
27981      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27982      */
27983     minButtonWidth:75,
27984
27985     /**
27986      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27987      * This property cascades to child containers if not set.
27988      */
27989     labelAlign:'left',
27990
27991     /**
27992      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27993      * fires a looping event with that state. This is required to bind buttons to the valid
27994      * state using the config value formBind:true on the button.
27995      */
27996     monitorValid : false,
27997
27998     /**
27999      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28000      */
28001     monitorPoll : 200,
28002     
28003     /**
28004      * @cfg {String} progressUrl - Url to return progress data 
28005      */
28006     
28007     progressUrl : false,
28008   
28009     /**
28010      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28011      * fields are added and the column is closed. If no fields are passed the column remains open
28012      * until end() is called.
28013      * @param {Object} config The config to pass to the column
28014      * @param {Field} field1 (optional)
28015      * @param {Field} field2 (optional)
28016      * @param {Field} etc (optional)
28017      * @return Column The column container object
28018      */
28019     column : function(c){
28020         var col = new Roo.form.Column(c);
28021         this.start(col);
28022         if(arguments.length > 1){ // duplicate code required because of Opera
28023             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28024             this.end();
28025         }
28026         return col;
28027     },
28028
28029     /**
28030      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28031      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28032      * until end() is called.
28033      * @param {Object} config The config to pass to the fieldset
28034      * @param {Field} field1 (optional)
28035      * @param {Field} field2 (optional)
28036      * @param {Field} etc (optional)
28037      * @return FieldSet The fieldset container object
28038      */
28039     fieldset : function(c){
28040         var fs = new Roo.form.FieldSet(c);
28041         this.start(fs);
28042         if(arguments.length > 1){ // duplicate code required because of Opera
28043             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28044             this.end();
28045         }
28046         return fs;
28047     },
28048
28049     /**
28050      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28051      * fields are added and the container is closed. If no fields are passed the container remains open
28052      * until end() is called.
28053      * @param {Object} config The config to pass to the Layout
28054      * @param {Field} field1 (optional)
28055      * @param {Field} field2 (optional)
28056      * @param {Field} etc (optional)
28057      * @return Layout The container object
28058      */
28059     container : function(c){
28060         var l = new Roo.form.Layout(c);
28061         this.start(l);
28062         if(arguments.length > 1){ // duplicate code required because of Opera
28063             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28064             this.end();
28065         }
28066         return l;
28067     },
28068
28069     /**
28070      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28071      * @param {Object} container A Roo.form.Layout or subclass of Layout
28072      * @return {Form} this
28073      */
28074     start : function(c){
28075         // cascade label info
28076         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28077         this.active.stack.push(c);
28078         c.ownerCt = this.active;
28079         this.active = c;
28080         return this;
28081     },
28082
28083     /**
28084      * Closes the current open container
28085      * @return {Form} this
28086      */
28087     end : function(){
28088         if(this.active == this.root){
28089             return this;
28090         }
28091         this.active = this.active.ownerCt;
28092         return this;
28093     },
28094
28095     /**
28096      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28097      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28098      * as the label of the field.
28099      * @param {Field} field1
28100      * @param {Field} field2 (optional)
28101      * @param {Field} etc. (optional)
28102      * @return {Form} this
28103      */
28104     add : function(){
28105         this.active.stack.push.apply(this.active.stack, arguments);
28106         this.allItems.push.apply(this.allItems,arguments);
28107         var r = [];
28108         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28109             if(a[i].isFormField){
28110                 r.push(a[i]);
28111             }
28112         }
28113         if(r.length > 0){
28114             Roo.form.Form.superclass.add.apply(this, r);
28115         }
28116         return this;
28117     },
28118     
28119
28120     
28121     
28122     
28123      /**
28124      * Find any element that has been added to a form, using it's ID or name
28125      * This can include framesets, columns etc. along with regular fields..
28126      * @param {String} id - id or name to find.
28127      
28128      * @return {Element} e - or false if nothing found.
28129      */
28130     findbyId : function(id)
28131     {
28132         var ret = false;
28133         if (!id) {
28134             return ret;
28135         }
28136         Roo.each(this.allItems, function(f){
28137             if (f.id == id || f.name == id ){
28138                 ret = f;
28139                 return false;
28140             }
28141         });
28142         return ret;
28143     },
28144
28145     
28146     
28147     /**
28148      * Render this form into the passed container. This should only be called once!
28149      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28150      * @return {Form} this
28151      */
28152     render : function(ct)
28153     {
28154         
28155         
28156         
28157         ct = Roo.get(ct);
28158         var o = this.autoCreate || {
28159             tag: 'form',
28160             method : this.method || 'POST',
28161             id : this.id || Roo.id()
28162         };
28163         this.initEl(ct.createChild(o));
28164
28165         this.root.render(this.el);
28166         
28167        
28168              
28169         this.items.each(function(f){
28170             f.render('x-form-el-'+f.id);
28171         });
28172
28173         if(this.buttons.length > 0){
28174             // tables are required to maintain order and for correct IE layout
28175             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28176                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28177                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28178             }}, null, true);
28179             var tr = tb.getElementsByTagName('tr')[0];
28180             for(var i = 0, len = this.buttons.length; i < len; i++) {
28181                 var b = this.buttons[i];
28182                 var td = document.createElement('td');
28183                 td.className = 'x-form-btn-td';
28184                 b.render(tr.appendChild(td));
28185             }
28186         }
28187         if(this.monitorValid){ // initialize after render
28188             this.startMonitoring();
28189         }
28190         this.fireEvent('rendered', this);
28191         return this;
28192     },
28193
28194     /**
28195      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28196      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28197      * object or a valid Roo.DomHelper element config
28198      * @param {Function} handler The function called when the button is clicked
28199      * @param {Object} scope (optional) The scope of the handler function
28200      * @return {Roo.Button}
28201      */
28202     addButton : function(config, handler, scope){
28203         var bc = {
28204             handler: handler,
28205             scope: scope,
28206             minWidth: this.minButtonWidth,
28207             hideParent:true
28208         };
28209         if(typeof config == "string"){
28210             bc.text = config;
28211         }else{
28212             Roo.apply(bc, config);
28213         }
28214         var btn = new Roo.Button(null, bc);
28215         this.buttons.push(btn);
28216         return btn;
28217     },
28218
28219      /**
28220      * Adds a series of form elements (using the xtype property as the factory method.
28221      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28222      * @param {Object} config 
28223      */
28224     
28225     addxtype : function()
28226     {
28227         var ar = Array.prototype.slice.call(arguments, 0);
28228         var ret = false;
28229         for(var i = 0; i < ar.length; i++) {
28230             if (!ar[i]) {
28231                 continue; // skip -- if this happends something invalid got sent, we 
28232                 // should ignore it, as basically that interface element will not show up
28233                 // and that should be pretty obvious!!
28234             }
28235             
28236             if (Roo.form[ar[i].xtype]) {
28237                 ar[i].form = this;
28238                 var fe = Roo.factory(ar[i], Roo.form);
28239                 if (!ret) {
28240                     ret = fe;
28241                 }
28242                 fe.form = this;
28243                 if (fe.store) {
28244                     fe.store.form = this;
28245                 }
28246                 if (fe.isLayout) {  
28247                          
28248                     this.start(fe);
28249                     this.allItems.push(fe);
28250                     if (fe.items && fe.addxtype) {
28251                         fe.addxtype.apply(fe, fe.items);
28252                         delete fe.items;
28253                     }
28254                      this.end();
28255                     continue;
28256                 }
28257                 
28258                 
28259                  
28260                 this.add(fe);
28261               //  console.log('adding ' + ar[i].xtype);
28262             }
28263             if (ar[i].xtype == 'Button') {  
28264                 //console.log('adding button');
28265                 //console.log(ar[i]);
28266                 this.addButton(ar[i]);
28267                 this.allItems.push(fe);
28268                 continue;
28269             }
28270             
28271             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28272                 alert('end is not supported on xtype any more, use items');
28273             //    this.end();
28274             //    //console.log('adding end');
28275             }
28276             
28277         }
28278         return ret;
28279     },
28280     
28281     /**
28282      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28283      * option "monitorValid"
28284      */
28285     startMonitoring : function(){
28286         if(!this.bound){
28287             this.bound = true;
28288             Roo.TaskMgr.start({
28289                 run : this.bindHandler,
28290                 interval : this.monitorPoll || 200,
28291                 scope: this
28292             });
28293         }
28294     },
28295
28296     /**
28297      * Stops monitoring of the valid state of this form
28298      */
28299     stopMonitoring : function(){
28300         this.bound = false;
28301     },
28302
28303     // private
28304     bindHandler : function(){
28305         if(!this.bound){
28306             return false; // stops binding
28307         }
28308         var valid = true;
28309         this.items.each(function(f){
28310             if(!f.isValid(true)){
28311                 valid = false;
28312                 return false;
28313             }
28314         });
28315         for(var i = 0, len = this.buttons.length; i < len; i++){
28316             var btn = this.buttons[i];
28317             if(btn.formBind === true && btn.disabled === valid){
28318                 btn.setDisabled(!valid);
28319             }
28320         }
28321         this.fireEvent('clientvalidation', this, valid);
28322     }
28323     
28324     
28325     
28326     
28327     
28328     
28329     
28330     
28331 });
28332
28333
28334 // back compat
28335 Roo.Form = Roo.form.Form;
28336 /*
28337  * Based on:
28338  * Ext JS Library 1.1.1
28339  * Copyright(c) 2006-2007, Ext JS, LLC.
28340  *
28341  * Originally Released Under LGPL - original licence link has changed is not relivant.
28342  *
28343  * Fork - LGPL
28344  * <script type="text/javascript">
28345  */
28346  
28347  /**
28348  * @class Roo.form.Action
28349  * Internal Class used to handle form actions
28350  * @constructor
28351  * @param {Roo.form.BasicForm} el The form element or its id
28352  * @param {Object} config Configuration options
28353  */
28354  
28355  
28356 // define the action interface
28357 Roo.form.Action = function(form, options){
28358     this.form = form;
28359     this.options = options || {};
28360 };
28361 /**
28362  * Client Validation Failed
28363  * @const 
28364  */
28365 Roo.form.Action.CLIENT_INVALID = 'client';
28366 /**
28367  * Server Validation Failed
28368  * @const 
28369  */
28370  Roo.form.Action.SERVER_INVALID = 'server';
28371  /**
28372  * Connect to Server Failed
28373  * @const 
28374  */
28375 Roo.form.Action.CONNECT_FAILURE = 'connect';
28376 /**
28377  * Reading Data from Server Failed
28378  * @const 
28379  */
28380 Roo.form.Action.LOAD_FAILURE = 'load';
28381
28382 Roo.form.Action.prototype = {
28383     type : 'default',
28384     failureType : undefined,
28385     response : undefined,
28386     result : undefined,
28387
28388     // interface method
28389     run : function(options){
28390
28391     },
28392
28393     // interface method
28394     success : function(response){
28395
28396     },
28397
28398     // interface method
28399     handleResponse : function(response){
28400
28401     },
28402
28403     // default connection failure
28404     failure : function(response){
28405         
28406         this.response = response;
28407         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28408         this.form.afterAction(this, false);
28409     },
28410
28411     processResponse : function(response){
28412         this.response = response;
28413         if(!response.responseText){
28414             return true;
28415         }
28416         this.result = this.handleResponse(response);
28417         return this.result;
28418     },
28419
28420     // utility functions used internally
28421     getUrl : function(appendParams){
28422         var url = this.options.url || this.form.url || this.form.el.dom.action;
28423         if(appendParams){
28424             var p = this.getParams();
28425             if(p){
28426                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28427             }
28428         }
28429         return url;
28430     },
28431
28432     getMethod : function(){
28433         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28434     },
28435
28436     getParams : function(){
28437         var bp = this.form.baseParams;
28438         var p = this.options.params;
28439         if(p){
28440             if(typeof p == "object"){
28441                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28442             }else if(typeof p == 'string' && bp){
28443                 p += '&' + Roo.urlEncode(bp);
28444             }
28445         }else if(bp){
28446             p = Roo.urlEncode(bp);
28447         }
28448         return p;
28449     },
28450
28451     createCallback : function(){
28452         return {
28453             success: this.success,
28454             failure: this.failure,
28455             scope: this,
28456             timeout: (this.form.timeout*1000),
28457             upload: this.form.fileUpload ? this.success : undefined
28458         };
28459     }
28460 };
28461
28462 Roo.form.Action.Submit = function(form, options){
28463     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28464 };
28465
28466 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28467     type : 'submit',
28468
28469     haveProgress : false,
28470     uploadComplete : false,
28471     
28472     // uploadProgress indicator.
28473     uploadProgress : function()
28474     {
28475         if (!this.form.progressUrl) {
28476             return;
28477         }
28478         
28479         if (!this.haveProgress) {
28480             Roo.MessageBox.progress("Uploading", "Uploading");
28481         }
28482         if (this.uploadComplete) {
28483            Roo.MessageBox.hide();
28484            return;
28485         }
28486         
28487         this.haveProgress = true;
28488    
28489         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28490         
28491         var c = new Roo.data.Connection();
28492         c.request({
28493             url : this.form.progressUrl,
28494             params: {
28495                 id : uid
28496             },
28497             method: 'GET',
28498             success : function(req){
28499                //console.log(data);
28500                 var rdata = false;
28501                 var edata;
28502                 try  {
28503                    rdata = Roo.decode(req.responseText)
28504                 } catch (e) {
28505                     Roo.log("Invalid data from server..");
28506                     Roo.log(edata);
28507                     return;
28508                 }
28509                 if (!rdata || !rdata.success) {
28510                     Roo.log(rdata);
28511                     return;
28512                 }
28513                 var data = rdata.data;
28514                 
28515                 if (this.uploadComplete) {
28516                    Roo.MessageBox.hide();
28517                    return;
28518                 }
28519                    
28520                 if (data){
28521                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28522                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28523                     );
28524                 }
28525                 this.uploadProgress.defer(2000,this);
28526             },
28527        
28528             failure: function(data) {
28529                 Roo.log('progress url failed ');
28530                 Roo.log(data);
28531             },
28532             scope : this
28533         });
28534            
28535     },
28536     
28537     
28538     run : function()
28539     {
28540         // run get Values on the form, so it syncs any secondary forms.
28541         this.form.getValues();
28542         
28543         var o = this.options;
28544         var method = this.getMethod();
28545         var isPost = method == 'POST';
28546         if(o.clientValidation === false || this.form.isValid()){
28547             
28548             if (this.form.progressUrl) {
28549                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28550                     (new Date() * 1) + '' + Math.random());
28551                     
28552             } 
28553             
28554             
28555             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28556                 form:this.form.el.dom,
28557                 url:this.getUrl(!isPost),
28558                 method: method,
28559                 params:isPost ? this.getParams() : null,
28560                 isUpload: this.form.fileUpload
28561             }));
28562             
28563             this.uploadProgress();
28564
28565         }else if (o.clientValidation !== false){ // client validation failed
28566             this.failureType = Roo.form.Action.CLIENT_INVALID;
28567             this.form.afterAction(this, false);
28568         }
28569     },
28570
28571     success : function(response)
28572     {
28573         this.uploadComplete= true;
28574         if (this.haveProgress) {
28575             Roo.MessageBox.hide();
28576         }
28577         
28578         
28579         var result = this.processResponse(response);
28580         if(result === true || result.success){
28581             this.form.afterAction(this, true);
28582             return;
28583         }
28584         if(result.errors){
28585             this.form.markInvalid(result.errors);
28586             this.failureType = Roo.form.Action.SERVER_INVALID;
28587         }
28588         this.form.afterAction(this, false);
28589     },
28590     failure : function(response)
28591     {
28592         this.uploadComplete= true;
28593         if (this.haveProgress) {
28594             Roo.MessageBox.hide();
28595         }
28596         
28597         this.response = response;
28598         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28599         this.form.afterAction(this, false);
28600     },
28601     
28602     handleResponse : function(response){
28603         if(this.form.errorReader){
28604             var rs = this.form.errorReader.read(response);
28605             var errors = [];
28606             if(rs.records){
28607                 for(var i = 0, len = rs.records.length; i < len; i++) {
28608                     var r = rs.records[i];
28609                     errors[i] = r.data;
28610                 }
28611             }
28612             if(errors.length < 1){
28613                 errors = null;
28614             }
28615             return {
28616                 success : rs.success,
28617                 errors : errors
28618             };
28619         }
28620         var ret = false;
28621         try {
28622             ret = Roo.decode(response.responseText);
28623         } catch (e) {
28624             ret = {
28625                 success: false,
28626                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28627                 errors : []
28628             };
28629         }
28630         return ret;
28631         
28632     }
28633 });
28634
28635
28636 Roo.form.Action.Load = function(form, options){
28637     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28638     this.reader = this.form.reader;
28639 };
28640
28641 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28642     type : 'load',
28643
28644     run : function(){
28645         
28646         Roo.Ajax.request(Roo.apply(
28647                 this.createCallback(), {
28648                     method:this.getMethod(),
28649                     url:this.getUrl(false),
28650                     params:this.getParams()
28651         }));
28652     },
28653
28654     success : function(response){
28655         
28656         var result = this.processResponse(response);
28657         if(result === true || !result.success || !result.data){
28658             this.failureType = Roo.form.Action.LOAD_FAILURE;
28659             this.form.afterAction(this, false);
28660             return;
28661         }
28662         this.form.clearInvalid();
28663         this.form.setValues(result.data);
28664         this.form.afterAction(this, true);
28665     },
28666
28667     handleResponse : function(response){
28668         if(this.form.reader){
28669             var rs = this.form.reader.read(response);
28670             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28671             return {
28672                 success : rs.success,
28673                 data : data
28674             };
28675         }
28676         return Roo.decode(response.responseText);
28677     }
28678 });
28679
28680 Roo.form.Action.ACTION_TYPES = {
28681     'load' : Roo.form.Action.Load,
28682     'submit' : Roo.form.Action.Submit
28683 };/*
28684  * Based on:
28685  * Ext JS Library 1.1.1
28686  * Copyright(c) 2006-2007, Ext JS, LLC.
28687  *
28688  * Originally Released Under LGPL - original licence link has changed is not relivant.
28689  *
28690  * Fork - LGPL
28691  * <script type="text/javascript">
28692  */
28693  
28694 /**
28695  * @class Roo.form.Layout
28696  * @extends Roo.Component
28697  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
28698  * @constructor
28699  * @param {Object} config Configuration options
28700  */
28701 Roo.form.Layout = function(config){
28702     var xitems = [];
28703     if (config.items) {
28704         xitems = config.items;
28705         delete config.items;
28706     }
28707     Roo.form.Layout.superclass.constructor.call(this, config);
28708     this.stack = [];
28709     Roo.each(xitems, this.addxtype, this);
28710      
28711 };
28712
28713 Roo.extend(Roo.form.Layout, Roo.Component, {
28714     /**
28715      * @cfg {String/Object} autoCreate
28716      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28717      */
28718     /**
28719      * @cfg {String/Object/Function} style
28720      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28721      * a function which returns such a specification.
28722      */
28723     /**
28724      * @cfg {String} labelAlign
28725      * Valid values are "left," "top" and "right" (defaults to "left")
28726      */
28727     /**
28728      * @cfg {Number} labelWidth
28729      * Fixed width in pixels of all field labels (defaults to undefined)
28730      */
28731     /**
28732      * @cfg {Boolean} clear
28733      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28734      */
28735     clear : true,
28736     /**
28737      * @cfg {String} labelSeparator
28738      * The separator to use after field labels (defaults to ':')
28739      */
28740     labelSeparator : ':',
28741     /**
28742      * @cfg {Boolean} hideLabels
28743      * True to suppress the display of field labels in this layout (defaults to false)
28744      */
28745     hideLabels : false,
28746
28747     // private
28748     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28749     
28750     isLayout : true,
28751     
28752     // private
28753     onRender : function(ct, position){
28754         if(this.el){ // from markup
28755             this.el = Roo.get(this.el);
28756         }else {  // generate
28757             var cfg = this.getAutoCreate();
28758             this.el = ct.createChild(cfg, position);
28759         }
28760         if(this.style){
28761             this.el.applyStyles(this.style);
28762         }
28763         if(this.labelAlign){
28764             this.el.addClass('x-form-label-'+this.labelAlign);
28765         }
28766         if(this.hideLabels){
28767             this.labelStyle = "display:none";
28768             this.elementStyle = "padding-left:0;";
28769         }else{
28770             if(typeof this.labelWidth == 'number'){
28771                 this.labelStyle = "width:"+this.labelWidth+"px;";
28772                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28773             }
28774             if(this.labelAlign == 'top'){
28775                 this.labelStyle = "width:auto;";
28776                 this.elementStyle = "padding-left:0;";
28777             }
28778         }
28779         var stack = this.stack;
28780         var slen = stack.length;
28781         if(slen > 0){
28782             if(!this.fieldTpl){
28783                 var t = new Roo.Template(
28784                     '<div class="x-form-item {5}">',
28785                         '<label for="{0}" style="{2}">{1}{4}</label>',
28786                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28787                         '</div>',
28788                     '</div><div class="x-form-clear-left"></div>'
28789                 );
28790                 t.disableFormats = true;
28791                 t.compile();
28792                 Roo.form.Layout.prototype.fieldTpl = t;
28793             }
28794             for(var i = 0; i < slen; i++) {
28795                 if(stack[i].isFormField){
28796                     this.renderField(stack[i]);
28797                 }else{
28798                     this.renderComponent(stack[i]);
28799                 }
28800             }
28801         }
28802         if(this.clear){
28803             this.el.createChild({cls:'x-form-clear'});
28804         }
28805     },
28806
28807     // private
28808     renderField : function(f){
28809         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28810                f.id, //0
28811                f.fieldLabel, //1
28812                f.labelStyle||this.labelStyle||'', //2
28813                this.elementStyle||'', //3
28814                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28815                f.itemCls||this.itemCls||''  //5
28816        ], true).getPrevSibling());
28817     },
28818
28819     // private
28820     renderComponent : function(c){
28821         c.render(c.isLayout ? this.el : this.el.createChild());    
28822     },
28823     /**
28824      * Adds a object form elements (using the xtype property as the factory method.)
28825      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28826      * @param {Object} config 
28827      */
28828     addxtype : function(o)
28829     {
28830         // create the lement.
28831         o.form = this.form;
28832         var fe = Roo.factory(o, Roo.form);
28833         this.form.allItems.push(fe);
28834         this.stack.push(fe);
28835         
28836         if (fe.isFormField) {
28837             this.form.items.add(fe);
28838         }
28839          
28840         return fe;
28841     }
28842 });
28843
28844 /**
28845  * @class Roo.form.Column
28846  * @extends Roo.form.Layout
28847  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28848  * @constructor
28849  * @param {Object} config Configuration options
28850  */
28851 Roo.form.Column = function(config){
28852     Roo.form.Column.superclass.constructor.call(this, config);
28853 };
28854
28855 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28856     /**
28857      * @cfg {Number/String} width
28858      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28859      */
28860     /**
28861      * @cfg {String/Object} autoCreate
28862      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28863      */
28864
28865     // private
28866     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28867
28868     // private
28869     onRender : function(ct, position){
28870         Roo.form.Column.superclass.onRender.call(this, ct, position);
28871         if(this.width){
28872             this.el.setWidth(this.width);
28873         }
28874     }
28875 });
28876
28877
28878 /**
28879  * @class Roo.form.Row
28880  * @extends Roo.form.Layout
28881  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28882  * @constructor
28883  * @param {Object} config Configuration options
28884  */
28885
28886  
28887 Roo.form.Row = function(config){
28888     Roo.form.Row.superclass.constructor.call(this, config);
28889 };
28890  
28891 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28892       /**
28893      * @cfg {Number/String} width
28894      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28895      */
28896     /**
28897      * @cfg {Number/String} height
28898      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28899      */
28900     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28901     
28902     padWidth : 20,
28903     // private
28904     onRender : function(ct, position){
28905         //console.log('row render');
28906         if(!this.rowTpl){
28907             var t = new Roo.Template(
28908                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28909                     '<label for="{0}" style="{2}">{1}{4}</label>',
28910                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28911                     '</div>',
28912                 '</div>'
28913             );
28914             t.disableFormats = true;
28915             t.compile();
28916             Roo.form.Layout.prototype.rowTpl = t;
28917         }
28918         this.fieldTpl = this.rowTpl;
28919         
28920         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28921         var labelWidth = 100;
28922         
28923         if ((this.labelAlign != 'top')) {
28924             if (typeof this.labelWidth == 'number') {
28925                 labelWidth = this.labelWidth
28926             }
28927             this.padWidth =  20 + labelWidth;
28928             
28929         }
28930         
28931         Roo.form.Column.superclass.onRender.call(this, ct, position);
28932         if(this.width){
28933             this.el.setWidth(this.width);
28934         }
28935         if(this.height){
28936             this.el.setHeight(this.height);
28937         }
28938     },
28939     
28940     // private
28941     renderField : function(f){
28942         f.fieldEl = this.fieldTpl.append(this.el, [
28943                f.id, f.fieldLabel,
28944                f.labelStyle||this.labelStyle||'',
28945                this.elementStyle||'',
28946                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28947                f.itemCls||this.itemCls||'',
28948                f.width ? f.width + this.padWidth : 160 + this.padWidth
28949        ],true);
28950     }
28951 });
28952  
28953
28954 /**
28955  * @class Roo.form.FieldSet
28956  * @extends Roo.form.Layout
28957  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28958  * @constructor
28959  * @param {Object} config Configuration options
28960  */
28961 Roo.form.FieldSet = function(config){
28962     Roo.form.FieldSet.superclass.constructor.call(this, config);
28963 };
28964
28965 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28966     /**
28967      * @cfg {String} legend
28968      * The text to display as the legend for the FieldSet (defaults to '')
28969      */
28970     /**
28971      * @cfg {String/Object} autoCreate
28972      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28973      */
28974
28975     // private
28976     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28977
28978     // private
28979     onRender : function(ct, position){
28980         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28981         if(this.legend){
28982             this.setLegend(this.legend);
28983         }
28984     },
28985
28986     // private
28987     setLegend : function(text){
28988         if(this.rendered){
28989             this.el.child('legend').update(text);
28990         }
28991     }
28992 });/*
28993  * Based on:
28994  * Ext JS Library 1.1.1
28995  * Copyright(c) 2006-2007, Ext JS, LLC.
28996  *
28997  * Originally Released Under LGPL - original licence link has changed is not relivant.
28998  *
28999  * Fork - LGPL
29000  * <script type="text/javascript">
29001  */
29002 /**
29003  * @class Roo.form.VTypes
29004  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29005  * @singleton
29006  */
29007 Roo.form.VTypes = function(){
29008     // closure these in so they are only created once.
29009     var alpha = /^[a-zA-Z_]+$/;
29010     var alphanum = /^[a-zA-Z0-9_]+$/;
29011     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29012     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29013
29014     // All these messages and functions are configurable
29015     return {
29016         /**
29017          * The function used to validate email addresses
29018          * @param {String} value The email address
29019          */
29020         'email' : function(v){
29021             return email.test(v);
29022         },
29023         /**
29024          * The error text to display when the email validation function returns false
29025          * @type String
29026          */
29027         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29028         /**
29029          * The keystroke filter mask to be applied on email input
29030          * @type RegExp
29031          */
29032         'emailMask' : /[a-z0-9_\.\-@]/i,
29033
29034         /**
29035          * The function used to validate URLs
29036          * @param {String} value The URL
29037          */
29038         'url' : function(v){
29039             return url.test(v);
29040         },
29041         /**
29042          * The error text to display when the url validation function returns false
29043          * @type String
29044          */
29045         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29046         
29047         /**
29048          * The function used to validate alpha values
29049          * @param {String} value The value
29050          */
29051         'alpha' : function(v){
29052             return alpha.test(v);
29053         },
29054         /**
29055          * The error text to display when the alpha validation function returns false
29056          * @type String
29057          */
29058         'alphaText' : 'This field should only contain letters and _',
29059         /**
29060          * The keystroke filter mask to be applied on alpha input
29061          * @type RegExp
29062          */
29063         'alphaMask' : /[a-z_]/i,
29064
29065         /**
29066          * The function used to validate alphanumeric values
29067          * @param {String} value The value
29068          */
29069         'alphanum' : function(v){
29070             return alphanum.test(v);
29071         },
29072         /**
29073          * The error text to display when the alphanumeric validation function returns false
29074          * @type String
29075          */
29076         'alphanumText' : 'This field should only contain letters, numbers and _',
29077         /**
29078          * The keystroke filter mask to be applied on alphanumeric input
29079          * @type RegExp
29080          */
29081         'alphanumMask' : /[a-z0-9_]/i
29082     };
29083 }();//<script type="text/javascript">
29084
29085 /**
29086  * @class Roo.form.FCKeditor
29087  * @extends Roo.form.TextArea
29088  * Wrapper around the FCKEditor http://www.fckeditor.net
29089  * @constructor
29090  * Creates a new FCKeditor
29091  * @param {Object} config Configuration options
29092  */
29093 Roo.form.FCKeditor = function(config){
29094     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29095     this.addEvents({
29096          /**
29097          * @event editorinit
29098          * Fired when the editor is initialized - you can add extra handlers here..
29099          * @param {FCKeditor} this
29100          * @param {Object} the FCK object.
29101          */
29102         editorinit : true
29103     });
29104     
29105     
29106 };
29107 Roo.form.FCKeditor.editors = { };
29108 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29109 {
29110     //defaultAutoCreate : {
29111     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29112     //},
29113     // private
29114     /**
29115      * @cfg {Object} fck options - see fck manual for details.
29116      */
29117     fckconfig : false,
29118     
29119     /**
29120      * @cfg {Object} fck toolbar set (Basic or Default)
29121      */
29122     toolbarSet : 'Basic',
29123     /**
29124      * @cfg {Object} fck BasePath
29125      */ 
29126     basePath : '/fckeditor/',
29127     
29128     
29129     frame : false,
29130     
29131     value : '',
29132     
29133    
29134     onRender : function(ct, position)
29135     {
29136         if(!this.el){
29137             this.defaultAutoCreate = {
29138                 tag: "textarea",
29139                 style:"width:300px;height:60px;",
29140                 autocomplete: "off"
29141             };
29142         }
29143         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29144         /*
29145         if(this.grow){
29146             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29147             if(this.preventScrollbars){
29148                 this.el.setStyle("overflow", "hidden");
29149             }
29150             this.el.setHeight(this.growMin);
29151         }
29152         */
29153         //console.log('onrender' + this.getId() );
29154         Roo.form.FCKeditor.editors[this.getId()] = this;
29155          
29156
29157         this.replaceTextarea() ;
29158         
29159     },
29160     
29161     getEditor : function() {
29162         return this.fckEditor;
29163     },
29164     /**
29165      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29166      * @param {Mixed} value The value to set
29167      */
29168     
29169     
29170     setValue : function(value)
29171     {
29172         //console.log('setValue: ' + value);
29173         
29174         if(typeof(value) == 'undefined') { // not sure why this is happending...
29175             return;
29176         }
29177         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29178         
29179         //if(!this.el || !this.getEditor()) {
29180         //    this.value = value;
29181             //this.setValue.defer(100,this,[value]);    
29182         //    return;
29183         //} 
29184         
29185         if(!this.getEditor()) {
29186             return;
29187         }
29188         
29189         this.getEditor().SetData(value);
29190         
29191         //
29192
29193     },
29194
29195     /**
29196      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29197      * @return {Mixed} value The field value
29198      */
29199     getValue : function()
29200     {
29201         
29202         if (this.frame && this.frame.dom.style.display == 'none') {
29203             return Roo.form.FCKeditor.superclass.getValue.call(this);
29204         }
29205         
29206         if(!this.el || !this.getEditor()) {
29207            
29208            // this.getValue.defer(100,this); 
29209             return this.value;
29210         }
29211        
29212         
29213         var value=this.getEditor().GetData();
29214         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29215         return Roo.form.FCKeditor.superclass.getValue.call(this);
29216         
29217
29218     },
29219
29220     /**
29221      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29222      * @return {Mixed} value The field value
29223      */
29224     getRawValue : function()
29225     {
29226         if (this.frame && this.frame.dom.style.display == 'none') {
29227             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29228         }
29229         
29230         if(!this.el || !this.getEditor()) {
29231             //this.getRawValue.defer(100,this); 
29232             return this.value;
29233             return;
29234         }
29235         
29236         
29237         
29238         var value=this.getEditor().GetData();
29239         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29240         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29241          
29242     },
29243     
29244     setSize : function(w,h) {
29245         
29246         
29247         
29248         //if (this.frame && this.frame.dom.style.display == 'none') {
29249         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29250         //    return;
29251         //}
29252         //if(!this.el || !this.getEditor()) {
29253         //    this.setSize.defer(100,this, [w,h]); 
29254         //    return;
29255         //}
29256         
29257         
29258         
29259         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29260         
29261         this.frame.dom.setAttribute('width', w);
29262         this.frame.dom.setAttribute('height', h);
29263         this.frame.setSize(w,h);
29264         
29265     },
29266     
29267     toggleSourceEdit : function(value) {
29268         
29269       
29270          
29271         this.el.dom.style.display = value ? '' : 'none';
29272         this.frame.dom.style.display = value ?  'none' : '';
29273         
29274     },
29275     
29276     
29277     focus: function(tag)
29278     {
29279         if (this.frame.dom.style.display == 'none') {
29280             return Roo.form.FCKeditor.superclass.focus.call(this);
29281         }
29282         if(!this.el || !this.getEditor()) {
29283             this.focus.defer(100,this, [tag]); 
29284             return;
29285         }
29286         
29287         
29288         
29289         
29290         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29291         this.getEditor().Focus();
29292         if (tgs.length) {
29293             if (!this.getEditor().Selection.GetSelection()) {
29294                 this.focus.defer(100,this, [tag]); 
29295                 return;
29296             }
29297             
29298             
29299             var r = this.getEditor().EditorDocument.createRange();
29300             r.setStart(tgs[0],0);
29301             r.setEnd(tgs[0],0);
29302             this.getEditor().Selection.GetSelection().removeAllRanges();
29303             this.getEditor().Selection.GetSelection().addRange(r);
29304             this.getEditor().Focus();
29305         }
29306         
29307     },
29308     
29309     
29310     
29311     replaceTextarea : function()
29312     {
29313         if ( document.getElementById( this.getId() + '___Frame' ) )
29314             return ;
29315         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29316         //{
29317             // We must check the elements firstly using the Id and then the name.
29318         var oTextarea = document.getElementById( this.getId() );
29319         
29320         var colElementsByName = document.getElementsByName( this.getId() ) ;
29321          
29322         oTextarea.style.display = 'none' ;
29323
29324         if ( oTextarea.tabIndex ) {            
29325             this.TabIndex = oTextarea.tabIndex ;
29326         }
29327         
29328         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29329         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29330         this.frame = Roo.get(this.getId() + '___Frame')
29331     },
29332     
29333     _getConfigHtml : function()
29334     {
29335         var sConfig = '' ;
29336
29337         for ( var o in this.fckconfig ) {
29338             sConfig += sConfig.length > 0  ? '&amp;' : '';
29339             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29340         }
29341
29342         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29343     },
29344     
29345     
29346     _getIFrameHtml : function()
29347     {
29348         var sFile = 'fckeditor.html' ;
29349         /* no idea what this is about..
29350         try
29351         {
29352             if ( (/fcksource=true/i).test( window.top.location.search ) )
29353                 sFile = 'fckeditor.original.html' ;
29354         }
29355         catch (e) { 
29356         */
29357
29358         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29359         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29360         
29361         
29362         var html = '<iframe id="' + this.getId() +
29363             '___Frame" src="' + sLink +
29364             '" width="' + this.width +
29365             '" height="' + this.height + '"' +
29366             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29367             ' frameborder="0" scrolling="no"></iframe>' ;
29368
29369         return html ;
29370     },
29371     
29372     _insertHtmlBefore : function( html, element )
29373     {
29374         if ( element.insertAdjacentHTML )       {
29375             // IE
29376             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29377         } else { // Gecko
29378             var oRange = document.createRange() ;
29379             oRange.setStartBefore( element ) ;
29380             var oFragment = oRange.createContextualFragment( html );
29381             element.parentNode.insertBefore( oFragment, element ) ;
29382         }
29383     }
29384     
29385     
29386   
29387     
29388     
29389     
29390     
29391
29392 });
29393
29394 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29395
29396 function FCKeditor_OnComplete(editorInstance){
29397     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29398     f.fckEditor = editorInstance;
29399     //console.log("loaded");
29400     f.fireEvent('editorinit', f, editorInstance);
29401
29402   
29403
29404  
29405
29406
29407
29408
29409
29410
29411
29412
29413
29414
29415
29416
29417
29418
29419
29420 //<script type="text/javascript">
29421 /**
29422  * @class Roo.form.GridField
29423  * @extends Roo.form.Field
29424  * Embed a grid (or editable grid into a form)
29425  * STATUS ALPHA
29426  * 
29427  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29428  * it needs 
29429  * xgrid.store = Roo.data.Store
29430  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29431  * xgrid.store.reader = Roo.data.JsonReader 
29432  * 
29433  * 
29434  * @constructor
29435  * Creates a new GridField
29436  * @param {Object} config Configuration options
29437  */
29438 Roo.form.GridField = function(config){
29439     Roo.form.GridField.superclass.constructor.call(this, config);
29440      
29441 };
29442
29443 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29444     /**
29445      * @cfg {Number} width  - used to restrict width of grid..
29446      */
29447     width : 100,
29448     /**
29449      * @cfg {Number} height - used to restrict height of grid..
29450      */
29451     height : 50,
29452      /**
29453      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29454          * 
29455          *}
29456      */
29457     xgrid : false, 
29458     /**
29459      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29460      * {tag: "input", type: "checkbox", autocomplete: "off"})
29461      */
29462    // defaultAutoCreate : { tag: 'div' },
29463     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29464     /**
29465      * @cfg {String} addTitle Text to include for adding a title.
29466      */
29467     addTitle : false,
29468     //
29469     onResize : function(){
29470         Roo.form.Field.superclass.onResize.apply(this, arguments);
29471     },
29472
29473     initEvents : function(){
29474         // Roo.form.Checkbox.superclass.initEvents.call(this);
29475         // has no events...
29476        
29477     },
29478
29479
29480     getResizeEl : function(){
29481         return this.wrap;
29482     },
29483
29484     getPositionEl : function(){
29485         return this.wrap;
29486     },
29487
29488     // private
29489     onRender : function(ct, position){
29490         
29491         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29492         var style = this.style;
29493         delete this.style;
29494         
29495         Roo.form.GridField.superclass.onRender.call(this, ct, position);
29496         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29497         this.viewEl = this.wrap.createChild({ tag: 'div' });
29498         if (style) {
29499             this.viewEl.applyStyles(style);
29500         }
29501         if (this.width) {
29502             this.viewEl.setWidth(this.width);
29503         }
29504         if (this.height) {
29505             this.viewEl.setHeight(this.height);
29506         }
29507         //if(this.inputValue !== undefined){
29508         //this.setValue(this.value);
29509         
29510         
29511         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29512         
29513         
29514         this.grid.render();
29515         this.grid.getDataSource().on('remove', this.refreshValue, this);
29516         this.grid.getDataSource().on('update', this.refreshValue, this);
29517         this.grid.on('afteredit', this.refreshValue, this);
29518  
29519     },
29520      
29521     
29522     /**
29523      * Sets the value of the item. 
29524      * @param {String} either an object  or a string..
29525      */
29526     setValue : function(v){
29527         //this.value = v;
29528         v = v || []; // empty set..
29529         // this does not seem smart - it really only affects memoryproxy grids..
29530         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29531             var ds = this.grid.getDataSource();
29532             // assumes a json reader..
29533             var data = {}
29534             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29535             ds.loadData( data);
29536         }
29537         // clear selection so it does not get stale.
29538         if (this.grid.sm) { 
29539             this.grid.sm.clearSelections();
29540         }
29541         
29542         Roo.form.GridField.superclass.setValue.call(this, v);
29543         this.refreshValue();
29544         // should load data in the grid really....
29545     },
29546     
29547     // private
29548     refreshValue: function() {
29549          var val = [];
29550         this.grid.getDataSource().each(function(r) {
29551             val.push(r.data);
29552         });
29553         this.el.dom.value = Roo.encode(val);
29554     }
29555     
29556      
29557     
29558     
29559 });/*
29560  * Based on:
29561  * Ext JS Library 1.1.1
29562  * Copyright(c) 2006-2007, Ext JS, LLC.
29563  *
29564  * Originally Released Under LGPL - original licence link has changed is not relivant.
29565  *
29566  * Fork - LGPL
29567  * <script type="text/javascript">
29568  */
29569 /**
29570  * @class Roo.form.DisplayField
29571  * @extends Roo.form.Field
29572  * A generic Field to display non-editable data.
29573  * @constructor
29574  * Creates a new Display Field item.
29575  * @param {Object} config Configuration options
29576  */
29577 Roo.form.DisplayField = function(config){
29578     Roo.form.DisplayField.superclass.constructor.call(this, config);
29579     
29580 };
29581
29582 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29583     inputType:      'hidden',
29584     allowBlank:     true,
29585     readOnly:         true,
29586     
29587  
29588     /**
29589      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29590      */
29591     focusClass : undefined,
29592     /**
29593      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29594      */
29595     fieldClass: 'x-form-field',
29596     
29597      /**
29598      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29599      */
29600     valueRenderer: undefined,
29601     
29602     width: 100,
29603     /**
29604      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29605      * {tag: "input", type: "checkbox", autocomplete: "off"})
29606      */
29607      
29608  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29609
29610     onResize : function(){
29611         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29612         
29613     },
29614
29615     initEvents : function(){
29616         // Roo.form.Checkbox.superclass.initEvents.call(this);
29617         // has no events...
29618        
29619     },
29620
29621
29622     getResizeEl : function(){
29623         return this.wrap;
29624     },
29625
29626     getPositionEl : function(){
29627         return this.wrap;
29628     },
29629
29630     // private
29631     onRender : function(ct, position){
29632         
29633         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29634         //if(this.inputValue !== undefined){
29635         this.wrap = this.el.wrap();
29636         
29637         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29638         
29639         if (this.bodyStyle) {
29640             this.viewEl.applyStyles(this.bodyStyle);
29641         }
29642         //this.viewEl.setStyle('padding', '2px');
29643         
29644         this.setValue(this.value);
29645         
29646     },
29647 /*
29648     // private
29649     initValue : Roo.emptyFn,
29650
29651   */
29652
29653         // private
29654     onClick : function(){
29655         
29656     },
29657
29658     /**
29659      * Sets the checked state of the checkbox.
29660      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29661      */
29662     setValue : function(v){
29663         this.value = v;
29664         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29665         // this might be called before we have a dom element..
29666         if (!this.viewEl) {
29667             return;
29668         }
29669         this.viewEl.dom.innerHTML = html;
29670         Roo.form.DisplayField.superclass.setValue.call(this, v);
29671
29672     }
29673 });/*
29674  * 
29675  * Licence- LGPL
29676  * 
29677  */
29678
29679 /**
29680  * @class Roo.form.DayPicker
29681  * @extends Roo.form.Field
29682  * A Day picker show [M] [T] [W] ....
29683  * @constructor
29684  * Creates a new Day Picker
29685  * @param {Object} config Configuration options
29686  */
29687 Roo.form.DayPicker= function(config){
29688     Roo.form.DayPicker.superclass.constructor.call(this, config);
29689      
29690 };
29691
29692 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
29693     /**
29694      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29695      */
29696     focusClass : undefined,
29697     /**
29698      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29699      */
29700     fieldClass: "x-form-field",
29701    
29702     /**
29703      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29704      * {tag: "input", type: "checkbox", autocomplete: "off"})
29705      */
29706     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
29707     
29708    
29709     actionMode : 'viewEl', 
29710     //
29711     // private
29712  
29713     inputType : 'hidden',
29714     
29715      
29716     inputElement: false, // real input element?
29717     basedOn: false, // ????
29718     
29719     isFormField: true, // not sure where this is needed!!!!
29720
29721     onResize : function(){
29722         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29723         if(!this.boxLabel){
29724             this.el.alignTo(this.wrap, 'c-c');
29725         }
29726     },
29727
29728     initEvents : function(){
29729         Roo.form.Checkbox.superclass.initEvents.call(this);
29730         this.el.on("click", this.onClick,  this);
29731         this.el.on("change", this.onClick,  this);
29732     },
29733
29734
29735     getResizeEl : function(){
29736         return this.wrap;
29737     },
29738
29739     getPositionEl : function(){
29740         return this.wrap;
29741     },
29742
29743     
29744     // private
29745     onRender : function(ct, position){
29746         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29747        
29748         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29749         
29750         var r1 = '<table><tr>';
29751         var r2 = '<tr class="x-form-daypick-icons">';
29752         for (var i=0; i < 7; i++) {
29753             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29754             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29755         }
29756         
29757         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29758         viewEl.select('img').on('click', this.onClick, this);
29759         this.viewEl = viewEl;   
29760         
29761         
29762         // this will not work on Chrome!!!
29763         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29764         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29765         
29766         
29767           
29768
29769     },
29770
29771     // private
29772     initValue : Roo.emptyFn,
29773
29774     /**
29775      * Returns the checked state of the checkbox.
29776      * @return {Boolean} True if checked, else false
29777      */
29778     getValue : function(){
29779         return this.el.dom.value;
29780         
29781     },
29782
29783         // private
29784     onClick : function(e){ 
29785         //this.setChecked(!this.checked);
29786         Roo.get(e.target).toggleClass('x-menu-item-checked');
29787         this.refreshValue();
29788         //if(this.el.dom.checked != this.checked){
29789         //    this.setValue(this.el.dom.checked);
29790        // }
29791     },
29792     
29793     // private
29794     refreshValue : function()
29795     {
29796         var val = '';
29797         this.viewEl.select('img',true).each(function(e,i,n)  {
29798             val += e.is(".x-menu-item-checked") ? String(n) : '';
29799         });
29800         this.setValue(val, true);
29801     },
29802
29803     /**
29804      * Sets the checked state of the checkbox.
29805      * On is always based on a string comparison between inputValue and the param.
29806      * @param {Boolean/String} value - the value to set 
29807      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29808      */
29809     setValue : function(v,suppressEvent){
29810         if (!this.el.dom) {
29811             return;
29812         }
29813         var old = this.el.dom.value ;
29814         this.el.dom.value = v;
29815         if (suppressEvent) {
29816             return ;
29817         }
29818          
29819         // update display..
29820         this.viewEl.select('img',true).each(function(e,i,n)  {
29821             
29822             var on = e.is(".x-menu-item-checked");
29823             var newv = v.indexOf(String(n)) > -1;
29824             if (on != newv) {
29825                 e.toggleClass('x-menu-item-checked');
29826             }
29827             
29828         });
29829         
29830         
29831         this.fireEvent('change', this, v, old);
29832         
29833         
29834     },
29835    
29836     // handle setting of hidden value by some other method!!?!?
29837     setFromHidden: function()
29838     {
29839         if(!this.el){
29840             return;
29841         }
29842         //console.log("SET FROM HIDDEN");
29843         //alert('setFrom hidden');
29844         this.setValue(this.el.dom.value);
29845     },
29846     
29847     onDestroy : function()
29848     {
29849         if(this.viewEl){
29850             Roo.get(this.viewEl).remove();
29851         }
29852          
29853         Roo.form.DayPicker.superclass.onDestroy.call(this);
29854     }
29855
29856 });/*
29857  * RooJS Library 1.1.1
29858  * Copyright(c) 2008-2011  Alan Knowles
29859  *
29860  * License - LGPL
29861  */
29862  
29863
29864 /**
29865  * @class Roo.form.ComboCheck
29866  * @extends Roo.form.ComboBox
29867  * A combobox for multiple select items.
29868  *
29869  * FIXME - could do with a reset button..
29870  * 
29871  * @constructor
29872  * Create a new ComboCheck
29873  * @param {Object} config Configuration options
29874  */
29875 Roo.form.ComboCheck = function(config){
29876     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29877     // should verify some data...
29878     // like
29879     // hiddenName = required..
29880     // displayField = required
29881     // valudField == required
29882     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29883     var _t = this;
29884     Roo.each(req, function(e) {
29885         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29886             throw "Roo.form.ComboCheck : missing value for: " + e;
29887         }
29888     });
29889     
29890     
29891 };
29892
29893 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29894      
29895      
29896     editable : false,
29897      
29898     selectedClass: 'x-menu-item-checked', 
29899     
29900     // private
29901     onRender : function(ct, position){
29902         var _t = this;
29903         
29904         
29905         
29906         if(!this.tpl){
29907             var cls = 'x-combo-list';
29908
29909             
29910             this.tpl =  new Roo.Template({
29911                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29912                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29913                    '<span>{' + this.displayField + '}</span>' +
29914                     '</div>' 
29915                 
29916             });
29917         }
29918  
29919         
29920         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29921         this.view.singleSelect = false;
29922         this.view.multiSelect = true;
29923         this.view.toggleSelect = true;
29924         this.pageTb.add(new Roo.Toolbar.Fill(), {
29925             
29926             text: 'Done',
29927             handler: function()
29928             {
29929                 _t.collapse();
29930             }
29931         });
29932     },
29933     
29934     onViewOver : function(e, t){
29935         // do nothing...
29936         return;
29937         
29938     },
29939     
29940     onViewClick : function(doFocus,index){
29941         return;
29942         
29943     },
29944     select: function () {
29945         //Roo.log("SELECT CALLED");
29946     },
29947      
29948     selectByValue : function(xv, scrollIntoView){
29949         var ar = this.getValueArray();
29950         var sels = [];
29951         
29952         Roo.each(ar, function(v) {
29953             if(v === undefined || v === null){
29954                 return;
29955             }
29956             var r = this.findRecord(this.valueField, v);
29957             if(r){
29958                 sels.push(this.store.indexOf(r))
29959                 
29960             }
29961         },this);
29962         this.view.select(sels);
29963         return false;
29964     },
29965     
29966     
29967     
29968     onSelect : function(record, index){
29969        // Roo.log("onselect Called");
29970        // this is only called by the clear button now..
29971         this.view.clearSelections();
29972         this.setValue('[]');
29973         if (this.value != this.valueBefore) {
29974             this.fireEvent('change', this, this.value, this.valueBefore);
29975         }
29976     },
29977     getValueArray : function()
29978     {
29979         var ar = [] ;
29980         
29981         try {
29982             //Roo.log(this.value);
29983             if (typeof(this.value) == 'undefined') {
29984                 return [];
29985             }
29986             var ar = Roo.decode(this.value);
29987             return  ar instanceof Array ? ar : []; //?? valid?
29988             
29989         } catch(e) {
29990             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29991             return [];
29992         }
29993          
29994     },
29995     expand : function ()
29996     {
29997         Roo.form.ComboCheck.superclass.expand.call(this);
29998         this.valueBefore = this.value;
29999         
30000
30001     },
30002     
30003     collapse : function(){
30004         Roo.form.ComboCheck.superclass.collapse.call(this);
30005         var sl = this.view.getSelectedIndexes();
30006         var st = this.store;
30007         var nv = [];
30008         var tv = [];
30009         var r;
30010         Roo.each(sl, function(i) {
30011             r = st.getAt(i);
30012             nv.push(r.get(this.valueField));
30013         },this);
30014         this.setValue(Roo.encode(nv));
30015         if (this.value != this.valueBefore) {
30016
30017             this.fireEvent('change', this, this.value, this.valueBefore);
30018         }
30019         
30020     },
30021     
30022     setValue : function(v){
30023         // Roo.log(v);
30024         this.value = v;
30025         
30026         var vals = this.getValueArray();
30027         var tv = [];
30028         Roo.each(vals, function(k) {
30029             var r = this.findRecord(this.valueField, k);
30030             if(r){
30031                 tv.push(r.data[this.displayField]);
30032             }else if(this.valueNotFoundText !== undefined){
30033                 tv.push( this.valueNotFoundText );
30034             }
30035         },this);
30036        // Roo.log(tv);
30037         
30038         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30039         this.hiddenField.value = v;
30040         this.value = v;
30041     }
30042     
30043 });//<script type="text/javasscript">
30044  
30045
30046 /**
30047  * @class Roo.DDView
30048  * A DnD enabled version of Roo.View.
30049  * @param {Element/String} container The Element in which to create the View.
30050  * @param {String} tpl The template string used to create the markup for each element of the View
30051  * @param {Object} config The configuration properties. These include all the config options of
30052  * {@link Roo.View} plus some specific to this class.<br>
30053  * <p>
30054  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30055  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30056  * <p>
30057  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30058 .x-view-drag-insert-above {
30059         border-top:1px dotted #3366cc;
30060 }
30061 .x-view-drag-insert-below {
30062         border-bottom:1px dotted #3366cc;
30063 }
30064 </code></pre>
30065  * 
30066  */
30067  
30068 Roo.DDView = function(container, tpl, config) {
30069     Roo.DDView.superclass.constructor.apply(this, arguments);
30070     this.getEl().setStyle("outline", "0px none");
30071     this.getEl().unselectable();
30072     if (this.dragGroup) {
30073                 this.setDraggable(this.dragGroup.split(","));
30074     }
30075     if (this.dropGroup) {
30076                 this.setDroppable(this.dropGroup.split(","));
30077     }
30078     if (this.deletable) {
30079         this.setDeletable();
30080     }
30081     this.isDirtyFlag = false;
30082         this.addEvents({
30083                 "drop" : true
30084         });
30085 };
30086
30087 Roo.extend(Roo.DDView, Roo.View, {
30088 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30089 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30090 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30091 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30092
30093         isFormField: true,
30094
30095         reset: Roo.emptyFn,
30096         
30097         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30098
30099         validate: function() {
30100                 return true;
30101         },
30102         
30103         destroy: function() {
30104                 this.purgeListeners();
30105                 this.getEl.removeAllListeners();
30106                 this.getEl().remove();
30107                 if (this.dragZone) {
30108                         if (this.dragZone.destroy) {
30109                                 this.dragZone.destroy();
30110                         }
30111                 }
30112                 if (this.dropZone) {
30113                         if (this.dropZone.destroy) {
30114                                 this.dropZone.destroy();
30115                         }
30116                 }
30117         },
30118
30119 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30120         getName: function() {
30121                 return this.name;
30122         },
30123
30124 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30125         setValue: function(v) {
30126                 if (!this.store) {
30127                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30128                 }
30129                 var data = {};
30130                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30131                 this.store.proxy = new Roo.data.MemoryProxy(data);
30132                 this.store.load();
30133         },
30134
30135 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30136         getValue: function() {
30137                 var result = '(';
30138                 this.store.each(function(rec) {
30139                         result += rec.id + ',';
30140                 });
30141                 return result.substr(0, result.length - 1) + ')';
30142         },
30143         
30144         getIds: function() {
30145                 var i = 0, result = new Array(this.store.getCount());
30146                 this.store.each(function(rec) {
30147                         result[i++] = rec.id;
30148                 });
30149                 return result;
30150         },
30151         
30152         isDirty: function() {
30153                 return this.isDirtyFlag;
30154         },
30155
30156 /**
30157  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30158  *      whole Element becomes the target, and this causes the drop gesture to append.
30159  */
30160     getTargetFromEvent : function(e) {
30161                 var target = e.getTarget();
30162                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30163                 target = target.parentNode;
30164                 }
30165                 if (!target) {
30166                         target = this.el.dom.lastChild || this.el.dom;
30167                 }
30168                 return target;
30169     },
30170
30171 /**
30172  *      Create the drag data which consists of an object which has the property "ddel" as
30173  *      the drag proxy element. 
30174  */
30175     getDragData : function(e) {
30176         var target = this.findItemFromChild(e.getTarget());
30177                 if(target) {
30178                         this.handleSelection(e);
30179                         var selNodes = this.getSelectedNodes();
30180             var dragData = {
30181                 source: this,
30182                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30183                 nodes: selNodes,
30184                 records: []
30185                         };
30186                         var selectedIndices = this.getSelectedIndexes();
30187                         for (var i = 0; i < selectedIndices.length; i++) {
30188                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30189                         }
30190                         if (selNodes.length == 1) {
30191                                 dragData.ddel = target.cloneNode(true); // the div element
30192                         } else {
30193                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30194                                 div.className = 'multi-proxy';
30195                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30196                                         div.appendChild(selNodes[i].cloneNode(true));
30197                                 }
30198                                 dragData.ddel = div;
30199                         }
30200             //console.log(dragData)
30201             //console.log(dragData.ddel.innerHTML)
30202                         return dragData;
30203                 }
30204         //console.log('nodragData')
30205                 return false;
30206     },
30207     
30208 /**     Specify to which ddGroup items in this DDView may be dragged. */
30209     setDraggable: function(ddGroup) {
30210         if (ddGroup instanceof Array) {
30211                 Roo.each(ddGroup, this.setDraggable, this);
30212                 return;
30213         }
30214         if (this.dragZone) {
30215                 this.dragZone.addToGroup(ddGroup);
30216         } else {
30217                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30218                                 containerScroll: true,
30219                                 ddGroup: ddGroup 
30220
30221                         });
30222 //                      Draggability implies selection. DragZone's mousedown selects the element.
30223                         if (!this.multiSelect) { this.singleSelect = true; }
30224
30225 //                      Wire the DragZone's handlers up to methods in *this*
30226                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30227                 }
30228     },
30229
30230 /**     Specify from which ddGroup this DDView accepts drops. */
30231     setDroppable: function(ddGroup) {
30232         if (ddGroup instanceof Array) {
30233                 Roo.each(ddGroup, this.setDroppable, this);
30234                 return;
30235         }
30236         if (this.dropZone) {
30237                 this.dropZone.addToGroup(ddGroup);
30238         } else {
30239                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30240                                 containerScroll: true,
30241                                 ddGroup: ddGroup
30242                         });
30243
30244 //                      Wire the DropZone's handlers up to methods in *this*
30245                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30246                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30247                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30248                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30249                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30250                 }
30251     },
30252
30253 /**     Decide whether to drop above or below a View node. */
30254     getDropPoint : function(e, n, dd){
30255         if (n == this.el.dom) { return "above"; }
30256                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30257                 var c = t + (b - t) / 2;
30258                 var y = Roo.lib.Event.getPageY(e);
30259                 if(y <= c) {
30260                         return "above";
30261                 }else{
30262                         return "below";
30263                 }
30264     },
30265
30266     onNodeEnter : function(n, dd, e, data){
30267                 return false;
30268     },
30269     
30270     onNodeOver : function(n, dd, e, data){
30271                 var pt = this.getDropPoint(e, n, dd);
30272                 // set the insert point style on the target node
30273                 var dragElClass = this.dropNotAllowed;
30274                 if (pt) {
30275                         var targetElClass;
30276                         if (pt == "above"){
30277                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30278                                 targetElClass = "x-view-drag-insert-above";
30279                         } else {
30280                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30281                                 targetElClass = "x-view-drag-insert-below";
30282                         }
30283                         if (this.lastInsertClass != targetElClass){
30284                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30285                                 this.lastInsertClass = targetElClass;
30286                         }
30287                 }
30288                 return dragElClass;
30289         },
30290
30291     onNodeOut : function(n, dd, e, data){
30292                 this.removeDropIndicators(n);
30293     },
30294
30295     onNodeDrop : function(n, dd, e, data){
30296         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30297                 return false;
30298         }
30299         var pt = this.getDropPoint(e, n, dd);
30300                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30301                 if (pt == "below") { insertAt++; }
30302                 for (var i = 0; i < data.records.length; i++) {
30303                         var r = data.records[i];
30304                         var dup = this.store.getById(r.id);
30305                         if (dup && (dd != this.dragZone)) {
30306                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30307                         } else {
30308                                 if (data.copy) {
30309                                         this.store.insert(insertAt++, r.copy());
30310                                 } else {
30311                                         data.source.isDirtyFlag = true;
30312                                         r.store.remove(r);
30313                                         this.store.insert(insertAt++, r);
30314                                 }
30315                                 this.isDirtyFlag = true;
30316                         }
30317                 }
30318                 this.dragZone.cachedTarget = null;
30319                 return true;
30320     },
30321
30322     removeDropIndicators : function(n){
30323                 if(n){
30324                         Roo.fly(n).removeClass([
30325                                 "x-view-drag-insert-above",
30326                                 "x-view-drag-insert-below"]);
30327                         this.lastInsertClass = "_noclass";
30328                 }
30329     },
30330
30331 /**
30332  *      Utility method. Add a delete option to the DDView's context menu.
30333  *      @param {String} imageUrl The URL of the "delete" icon image.
30334  */
30335         setDeletable: function(imageUrl) {
30336                 if (!this.singleSelect && !this.multiSelect) {
30337                         this.singleSelect = true;
30338                 }
30339                 var c = this.getContextMenu();
30340                 this.contextMenu.on("itemclick", function(item) {
30341                         switch (item.id) {
30342                                 case "delete":
30343                                         this.remove(this.getSelectedIndexes());
30344                                         break;
30345                         }
30346                 }, this);
30347                 this.contextMenu.add({
30348                         icon: imageUrl,
30349                         id: "delete",
30350                         text: 'Delete'
30351                 });
30352         },
30353         
30354 /**     Return the context menu for this DDView. */
30355         getContextMenu: function() {
30356                 if (!this.contextMenu) {
30357 //                      Create the View's context menu
30358                         this.contextMenu = new Roo.menu.Menu({
30359                                 id: this.id + "-contextmenu"
30360                         });
30361                         this.el.on("contextmenu", this.showContextMenu, this);
30362                 }
30363                 return this.contextMenu;
30364         },
30365         
30366         disableContextMenu: function() {
30367                 if (this.contextMenu) {
30368                         this.el.un("contextmenu", this.showContextMenu, this);
30369                 }
30370         },
30371
30372         showContextMenu: function(e, item) {
30373         item = this.findItemFromChild(e.getTarget());
30374                 if (item) {
30375                         e.stopEvent();
30376                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30377                         this.contextMenu.showAt(e.getXY());
30378             }
30379     },
30380
30381 /**
30382  *      Remove {@link Roo.data.Record}s at the specified indices.
30383  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30384  */
30385     remove: function(selectedIndices) {
30386                 selectedIndices = [].concat(selectedIndices);
30387                 for (var i = 0; i < selectedIndices.length; i++) {
30388                         var rec = this.store.getAt(selectedIndices[i]);
30389                         this.store.remove(rec);
30390                 }
30391     },
30392
30393 /**
30394  *      Double click fires the event, but also, if this is draggable, and there is only one other
30395  *      related DropZone, it transfers the selected node.
30396  */
30397     onDblClick : function(e){
30398         var item = this.findItemFromChild(e.getTarget());
30399         if(item){
30400             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30401                 return false;
30402             }
30403             if (this.dragGroup) {
30404                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30405                     while (targets.indexOf(this.dropZone) > -1) {
30406                             targets.remove(this.dropZone);
30407                                 }
30408                     if (targets.length == 1) {
30409                                         this.dragZone.cachedTarget = null;
30410                         var el = Roo.get(targets[0].getEl());
30411                         var box = el.getBox(true);
30412                         targets[0].onNodeDrop(el.dom, {
30413                                 target: el.dom,
30414                                 xy: [box.x, box.y + box.height - 1]
30415                         }, null, this.getDragData(e));
30416                     }
30417                 }
30418         }
30419     },
30420     
30421     handleSelection: function(e) {
30422                 this.dragZone.cachedTarget = null;
30423         var item = this.findItemFromChild(e.getTarget());
30424         if (!item) {
30425                 this.clearSelections(true);
30426                 return;
30427         }
30428                 if (item && (this.multiSelect || this.singleSelect)){
30429                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30430                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30431                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30432                                 this.unselect(item);
30433                         } else {
30434                                 this.select(item, this.multiSelect && e.ctrlKey);
30435                                 this.lastSelection = item;
30436                         }
30437                 }
30438     },
30439
30440     onItemClick : function(item, index, e){
30441                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30442                         return false;
30443                 }
30444                 return true;
30445     },
30446
30447     unselect : function(nodeInfo, suppressEvent){
30448                 var node = this.getNode(nodeInfo);
30449                 if(node && this.isSelected(node)){
30450                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30451                                 Roo.fly(node).removeClass(this.selectedClass);
30452                                 this.selections.remove(node);
30453                                 if(!suppressEvent){
30454                                         this.fireEvent("selectionchange", this, this.selections);
30455                                 }
30456                         }
30457                 }
30458     }
30459 });
30460 /*
30461  * Based on:
30462  * Ext JS Library 1.1.1
30463  * Copyright(c) 2006-2007, Ext JS, LLC.
30464  *
30465  * Originally Released Under LGPL - original licence link has changed is not relivant.
30466  *
30467  * Fork - LGPL
30468  * <script type="text/javascript">
30469  */
30470  
30471 /**
30472  * @class Roo.LayoutManager
30473  * @extends Roo.util.Observable
30474  * Base class for layout managers.
30475  */
30476 Roo.LayoutManager = function(container, config){
30477     Roo.LayoutManager.superclass.constructor.call(this);
30478     this.el = Roo.get(container);
30479     // ie scrollbar fix
30480     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30481         document.body.scroll = "no";
30482     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30483         this.el.position('relative');
30484     }
30485     this.id = this.el.id;
30486     this.el.addClass("x-layout-container");
30487     /** false to disable window resize monitoring @type Boolean */
30488     this.monitorWindowResize = true;
30489     this.regions = {};
30490     this.addEvents({
30491         /**
30492          * @event layout
30493          * Fires when a layout is performed. 
30494          * @param {Roo.LayoutManager} this
30495          */
30496         "layout" : true,
30497         /**
30498          * @event regionresized
30499          * Fires when the user resizes a region. 
30500          * @param {Roo.LayoutRegion} region The resized region
30501          * @param {Number} newSize The new size (width for east/west, height for north/south)
30502          */
30503         "regionresized" : true,
30504         /**
30505          * @event regioncollapsed
30506          * Fires when a region is collapsed. 
30507          * @param {Roo.LayoutRegion} region The collapsed region
30508          */
30509         "regioncollapsed" : true,
30510         /**
30511          * @event regionexpanded
30512          * Fires when a region is expanded.  
30513          * @param {Roo.LayoutRegion} region The expanded region
30514          */
30515         "regionexpanded" : true
30516     });
30517     this.updating = false;
30518     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30519 };
30520
30521 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30522     /**
30523      * Returns true if this layout is currently being updated
30524      * @return {Boolean}
30525      */
30526     isUpdating : function(){
30527         return this.updating; 
30528     },
30529     
30530     /**
30531      * Suspend the LayoutManager from doing auto-layouts while
30532      * making multiple add or remove calls
30533      */
30534     beginUpdate : function(){
30535         this.updating = true;    
30536     },
30537     
30538     /**
30539      * Restore auto-layouts and optionally disable the manager from performing a layout
30540      * @param {Boolean} noLayout true to disable a layout update 
30541      */
30542     endUpdate : function(noLayout){
30543         this.updating = false;
30544         if(!noLayout){
30545             this.layout();
30546         }    
30547     },
30548     
30549     layout: function(){
30550         
30551     },
30552     
30553     onRegionResized : function(region, newSize){
30554         this.fireEvent("regionresized", region, newSize);
30555         this.layout();
30556     },
30557     
30558     onRegionCollapsed : function(region){
30559         this.fireEvent("regioncollapsed", region);
30560     },
30561     
30562     onRegionExpanded : function(region){
30563         this.fireEvent("regionexpanded", region);
30564     },
30565         
30566     /**
30567      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30568      * performs box-model adjustments.
30569      * @return {Object} The size as an object {width: (the width), height: (the height)}
30570      */
30571     getViewSize : function(){
30572         var size;
30573         if(this.el.dom != document.body){
30574             size = this.el.getSize();
30575         }else{
30576             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30577         }
30578         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30579         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30580         return size;
30581     },
30582     
30583     /**
30584      * Returns the Element this layout is bound to.
30585      * @return {Roo.Element}
30586      */
30587     getEl : function(){
30588         return this.el;
30589     },
30590     
30591     /**
30592      * Returns the specified region.
30593      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30594      * @return {Roo.LayoutRegion}
30595      */
30596     getRegion : function(target){
30597         return this.regions[target.toLowerCase()];
30598     },
30599     
30600     onWindowResize : function(){
30601         if(this.monitorWindowResize){
30602             this.layout();
30603         }
30604     }
30605 });/*
30606  * Based on:
30607  * Ext JS Library 1.1.1
30608  * Copyright(c) 2006-2007, Ext JS, LLC.
30609  *
30610  * Originally Released Under LGPL - original licence link has changed is not relivant.
30611  *
30612  * Fork - LGPL
30613  * <script type="text/javascript">
30614  */
30615 /**
30616  * @class Roo.BorderLayout
30617  * @extends Roo.LayoutManager
30618  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30619  * please see: <br><br>
30620  * <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>
30621  * <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>
30622  * Example:
30623  <pre><code>
30624  var layout = new Roo.BorderLayout(document.body, {
30625     north: {
30626         initialSize: 25,
30627         titlebar: false
30628     },
30629     west: {
30630         split:true,
30631         initialSize: 200,
30632         minSize: 175,
30633         maxSize: 400,
30634         titlebar: true,
30635         collapsible: true
30636     },
30637     east: {
30638         split:true,
30639         initialSize: 202,
30640         minSize: 175,
30641         maxSize: 400,
30642         titlebar: true,
30643         collapsible: true
30644     },
30645     south: {
30646         split:true,
30647         initialSize: 100,
30648         minSize: 100,
30649         maxSize: 200,
30650         titlebar: true,
30651         collapsible: true
30652     },
30653     center: {
30654         titlebar: true,
30655         autoScroll:true,
30656         resizeTabs: true,
30657         minTabWidth: 50,
30658         preferredTabWidth: 150
30659     }
30660 });
30661
30662 // shorthand
30663 var CP = Roo.ContentPanel;
30664
30665 layout.beginUpdate();
30666 layout.add("north", new CP("north", "North"));
30667 layout.add("south", new CP("south", {title: "South", closable: true}));
30668 layout.add("west", new CP("west", {title: "West"}));
30669 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30670 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30671 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30672 layout.getRegion("center").showPanel("center1");
30673 layout.endUpdate();
30674 </code></pre>
30675
30676 <b>The container the layout is rendered into can be either the body element or any other element.
30677 If it is not the body element, the container needs to either be an absolute positioned element,
30678 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30679 the container size if it is not the body element.</b>
30680
30681 * @constructor
30682 * Create a new BorderLayout
30683 * @param {String/HTMLElement/Element} container The container this layout is bound to
30684 * @param {Object} config Configuration options
30685  */
30686 Roo.BorderLayout = function(container, config){
30687     config = config || {};
30688     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30689     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30690     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30691         var target = this.factory.validRegions[i];
30692         if(config[target]){
30693             this.addRegion(target, config[target]);
30694         }
30695     }
30696 };
30697
30698 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30699     /**
30700      * Creates and adds a new region if it doesn't already exist.
30701      * @param {String} target The target region key (north, south, east, west or center).
30702      * @param {Object} config The regions config object
30703      * @return {BorderLayoutRegion} The new region
30704      */
30705     addRegion : function(target, config){
30706         if(!this.regions[target]){
30707             var r = this.factory.create(target, this, config);
30708             this.bindRegion(target, r);
30709         }
30710         return this.regions[target];
30711     },
30712
30713     // private (kinda)
30714     bindRegion : function(name, r){
30715         this.regions[name] = r;
30716         r.on("visibilitychange", this.layout, this);
30717         r.on("paneladded", this.layout, this);
30718         r.on("panelremoved", this.layout, this);
30719         r.on("invalidated", this.layout, this);
30720         r.on("resized", this.onRegionResized, this);
30721         r.on("collapsed", this.onRegionCollapsed, this);
30722         r.on("expanded", this.onRegionExpanded, this);
30723     },
30724
30725     /**
30726      * Performs a layout update.
30727      */
30728     layout : function(){
30729         if(this.updating) return;
30730         var size = this.getViewSize();
30731         var w = size.width;
30732         var h = size.height;
30733         var centerW = w;
30734         var centerH = h;
30735         var centerY = 0;
30736         var centerX = 0;
30737         //var x = 0, y = 0;
30738
30739         var rs = this.regions;
30740         var north = rs["north"];
30741         var south = rs["south"]; 
30742         var west = rs["west"];
30743         var east = rs["east"];
30744         var center = rs["center"];
30745         //if(this.hideOnLayout){ // not supported anymore
30746             //c.el.setStyle("display", "none");
30747         //}
30748         if(north && north.isVisible()){
30749             var b = north.getBox();
30750             var m = north.getMargins();
30751             b.width = w - (m.left+m.right);
30752             b.x = m.left;
30753             b.y = m.top;
30754             centerY = b.height + b.y + m.bottom;
30755             centerH -= centerY;
30756             north.updateBox(this.safeBox(b));
30757         }
30758         if(south && south.isVisible()){
30759             var b = south.getBox();
30760             var m = south.getMargins();
30761             b.width = w - (m.left+m.right);
30762             b.x = m.left;
30763             var totalHeight = (b.height + m.top + m.bottom);
30764             b.y = h - totalHeight + m.top;
30765             centerH -= totalHeight;
30766             south.updateBox(this.safeBox(b));
30767         }
30768         if(west && west.isVisible()){
30769             var b = west.getBox();
30770             var m = west.getMargins();
30771             b.height = centerH - (m.top+m.bottom);
30772             b.x = m.left;
30773             b.y = centerY + m.top;
30774             var totalWidth = (b.width + m.left + m.right);
30775             centerX += totalWidth;
30776             centerW -= totalWidth;
30777             west.updateBox(this.safeBox(b));
30778         }
30779         if(east && east.isVisible()){
30780             var b = east.getBox();
30781             var m = east.getMargins();
30782             b.height = centerH - (m.top+m.bottom);
30783             var totalWidth = (b.width + m.left + m.right);
30784             b.x = w - totalWidth + m.left;
30785             b.y = centerY + m.top;
30786             centerW -= totalWidth;
30787             east.updateBox(this.safeBox(b));
30788         }
30789         if(center){
30790             var m = center.getMargins();
30791             var centerBox = {
30792                 x: centerX + m.left,
30793                 y: centerY + m.top,
30794                 width: centerW - (m.left+m.right),
30795                 height: centerH - (m.top+m.bottom)
30796             };
30797             //if(this.hideOnLayout){
30798                 //center.el.setStyle("display", "block");
30799             //}
30800             center.updateBox(this.safeBox(centerBox));
30801         }
30802         this.el.repaint();
30803         this.fireEvent("layout", this);
30804     },
30805
30806     // private
30807     safeBox : function(box){
30808         box.width = Math.max(0, box.width);
30809         box.height = Math.max(0, box.height);
30810         return box;
30811     },
30812
30813     /**
30814      * Adds a ContentPanel (or subclass) to this layout.
30815      * @param {String} target The target region key (north, south, east, west or center).
30816      * @param {Roo.ContentPanel} panel The panel to add
30817      * @return {Roo.ContentPanel} The added panel
30818      */
30819     add : function(target, panel){
30820          
30821         target = target.toLowerCase();
30822         return this.regions[target].add(panel);
30823     },
30824
30825     /**
30826      * Remove a ContentPanel (or subclass) to this layout.
30827      * @param {String} target The target region key (north, south, east, west or center).
30828      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30829      * @return {Roo.ContentPanel} The removed panel
30830      */
30831     remove : function(target, panel){
30832         target = target.toLowerCase();
30833         return this.regions[target].remove(panel);
30834     },
30835
30836     /**
30837      * Searches all regions for a panel with the specified id
30838      * @param {String} panelId
30839      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30840      */
30841     findPanel : function(panelId){
30842         var rs = this.regions;
30843         for(var target in rs){
30844             if(typeof rs[target] != "function"){
30845                 var p = rs[target].getPanel(panelId);
30846                 if(p){
30847                     return p;
30848                 }
30849             }
30850         }
30851         return null;
30852     },
30853
30854     /**
30855      * Searches all regions for a panel with the specified id and activates (shows) it.
30856      * @param {String/ContentPanel} panelId The panels id or the panel itself
30857      * @return {Roo.ContentPanel} The shown panel or null
30858      */
30859     showPanel : function(panelId) {
30860       var rs = this.regions;
30861       for(var target in rs){
30862          var r = rs[target];
30863          if(typeof r != "function"){
30864             if(r.hasPanel(panelId)){
30865                return r.showPanel(panelId);
30866             }
30867          }
30868       }
30869       return null;
30870    },
30871
30872    /**
30873      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30874      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30875      */
30876     restoreState : function(provider){
30877         if(!provider){
30878             provider = Roo.state.Manager;
30879         }
30880         var sm = new Roo.LayoutStateManager();
30881         sm.init(this, provider);
30882     },
30883
30884     /**
30885      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30886      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30887      * a valid ContentPanel config object.  Example:
30888      * <pre><code>
30889 // Create the main layout
30890 var layout = new Roo.BorderLayout('main-ct', {
30891     west: {
30892         split:true,
30893         minSize: 175,
30894         titlebar: true
30895     },
30896     center: {
30897         title:'Components'
30898     }
30899 }, 'main-ct');
30900
30901 // Create and add multiple ContentPanels at once via configs
30902 layout.batchAdd({
30903    west: {
30904        id: 'source-files',
30905        autoCreate:true,
30906        title:'Ext Source Files',
30907        autoScroll:true,
30908        fitToFrame:true
30909    },
30910    center : {
30911        el: cview,
30912        autoScroll:true,
30913        fitToFrame:true,
30914        toolbar: tb,
30915        resizeEl:'cbody'
30916    }
30917 });
30918 </code></pre>
30919      * @param {Object} regions An object containing ContentPanel configs by region name
30920      */
30921     batchAdd : function(regions){
30922         this.beginUpdate();
30923         for(var rname in regions){
30924             var lr = this.regions[rname];
30925             if(lr){
30926                 this.addTypedPanels(lr, regions[rname]);
30927             }
30928         }
30929         this.endUpdate();
30930     },
30931
30932     // private
30933     addTypedPanels : function(lr, ps){
30934         if(typeof ps == 'string'){
30935             lr.add(new Roo.ContentPanel(ps));
30936         }
30937         else if(ps instanceof Array){
30938             for(var i =0, len = ps.length; i < len; i++){
30939                 this.addTypedPanels(lr, ps[i]);
30940             }
30941         }
30942         else if(!ps.events){ // raw config?
30943             var el = ps.el;
30944             delete ps.el; // prevent conflict
30945             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30946         }
30947         else {  // panel object assumed!
30948             lr.add(ps);
30949         }
30950     },
30951     /**
30952      * Adds a xtype elements to the layout.
30953      * <pre><code>
30954
30955 layout.addxtype({
30956        xtype : 'ContentPanel',
30957        region: 'west',
30958        items: [ .... ]
30959    }
30960 );
30961
30962 layout.addxtype({
30963         xtype : 'NestedLayoutPanel',
30964         region: 'west',
30965         layout: {
30966            center: { },
30967            west: { }   
30968         },
30969         items : [ ... list of content panels or nested layout panels.. ]
30970    }
30971 );
30972 </code></pre>
30973      * @param {Object} cfg Xtype definition of item to add.
30974      */
30975     addxtype : function(cfg)
30976     {
30977         // basically accepts a pannel...
30978         // can accept a layout region..!?!?
30979         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30980         
30981         if (!cfg.xtype.match(/Panel$/)) {
30982             return false;
30983         }
30984         var ret = false;
30985         
30986         if (typeof(cfg.region) == 'undefined') {
30987             Roo.log("Failed to add Panel, region was not set");
30988             Roo.log(cfg);
30989             return false;
30990         }
30991         var region = cfg.region;
30992         delete cfg.region;
30993         
30994           
30995         var xitems = [];
30996         if (cfg.items) {
30997             xitems = cfg.items;
30998             delete cfg.items;
30999         }
31000         var nb = false;
31001         
31002         switch(cfg.xtype) 
31003         {
31004             case 'ContentPanel':  // ContentPanel (el, cfg)
31005             case 'ScrollPanel':  // ContentPanel (el, cfg)
31006                 if(cfg.autoCreate) {
31007                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31008                 } else {
31009                     var el = this.el.createChild();
31010                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31011                 }
31012                 
31013                 this.add(region, ret);
31014                 break;
31015             
31016             
31017             case 'TreePanel': // our new panel!
31018                 cfg.el = this.el.createChild();
31019                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31020                 this.add(region, ret);
31021                 break;
31022             
31023             case 'NestedLayoutPanel': 
31024                 // create a new Layout (which is  a Border Layout...
31025                 var el = this.el.createChild();
31026                 var clayout = cfg.layout;
31027                 delete cfg.layout;
31028                 clayout.items   = clayout.items  || [];
31029                 // replace this exitems with the clayout ones..
31030                 xitems = clayout.items;
31031                  
31032                 
31033                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31034                     cfg.background = false;
31035                 }
31036                 var layout = new Roo.BorderLayout(el, clayout);
31037                 
31038                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31039                 //console.log('adding nested layout panel '  + cfg.toSource());
31040                 this.add(region, ret);
31041                 nb = {}; /// find first...
31042                 break;
31043                 
31044             case 'GridPanel': 
31045             
31046                 // needs grid and region
31047                 
31048                 //var el = this.getRegion(region).el.createChild();
31049                 var el = this.el.createChild();
31050                 // create the grid first...
31051                 
31052                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31053                 delete cfg.grid;
31054                 if (region == 'center' && this.active ) {
31055                     cfg.background = false;
31056                 }
31057                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31058                 
31059                 this.add(region, ret);
31060                 if (cfg.background) {
31061                     ret.on('activate', function(gp) {
31062                         if (!gp.grid.rendered) {
31063                             gp.grid.render();
31064                         }
31065                     });
31066                 } else {
31067                     grid.render();
31068                 }
31069                 break;
31070            
31071                
31072                 
31073                 
31074             default: 
31075                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31076                 return null;
31077              // GridPanel (grid, cfg)
31078             
31079         }
31080         this.beginUpdate();
31081         // add children..
31082         var region = '';
31083         var abn = {};
31084         Roo.each(xitems, function(i)  {
31085             region = nb && i.region ? i.region : false;
31086             
31087             var add = ret.addxtype(i);
31088            
31089             if (region) {
31090                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31091                 if (!i.background) {
31092                     abn[region] = nb[region] ;
31093                 }
31094             }
31095             
31096         });
31097         this.endUpdate();
31098
31099         // make the last non-background panel active..
31100         //if (nb) { Roo.log(abn); }
31101         if (nb) {
31102             
31103             for(var r in abn) {
31104                 region = this.getRegion(r);
31105                 if (region) {
31106                     // tried using nb[r], but it does not work..
31107                      
31108                     region.showPanel(abn[r]);
31109                    
31110                 }
31111             }
31112         }
31113         return ret;
31114         
31115     }
31116 });
31117
31118 /**
31119  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31120  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31121  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31122  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31123  * <pre><code>
31124 // shorthand
31125 var CP = Roo.ContentPanel;
31126
31127 var layout = Roo.BorderLayout.create({
31128     north: {
31129         initialSize: 25,
31130         titlebar: false,
31131         panels: [new CP("north", "North")]
31132     },
31133     west: {
31134         split:true,
31135         initialSize: 200,
31136         minSize: 175,
31137         maxSize: 400,
31138         titlebar: true,
31139         collapsible: true,
31140         panels: [new CP("west", {title: "West"})]
31141     },
31142     east: {
31143         split:true,
31144         initialSize: 202,
31145         minSize: 175,
31146         maxSize: 400,
31147         titlebar: true,
31148         collapsible: true,
31149         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31150     },
31151     south: {
31152         split:true,
31153         initialSize: 100,
31154         minSize: 100,
31155         maxSize: 200,
31156         titlebar: true,
31157         collapsible: true,
31158         panels: [new CP("south", {title: "South", closable: true})]
31159     },
31160     center: {
31161         titlebar: true,
31162         autoScroll:true,
31163         resizeTabs: true,
31164         minTabWidth: 50,
31165         preferredTabWidth: 150,
31166         panels: [
31167             new CP("center1", {title: "Close Me", closable: true}),
31168             new CP("center2", {title: "Center Panel", closable: false})
31169         ]
31170     }
31171 }, document.body);
31172
31173 layout.getRegion("center").showPanel("center1");
31174 </code></pre>
31175  * @param config
31176  * @param targetEl
31177  */
31178 Roo.BorderLayout.create = function(config, targetEl){
31179     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31180     layout.beginUpdate();
31181     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31182     for(var j = 0, jlen = regions.length; j < jlen; j++){
31183         var lr = regions[j];
31184         if(layout.regions[lr] && config[lr].panels){
31185             var r = layout.regions[lr];
31186             var ps = config[lr].panels;
31187             layout.addTypedPanels(r, ps);
31188         }
31189     }
31190     layout.endUpdate();
31191     return layout;
31192 };
31193
31194 // private
31195 Roo.BorderLayout.RegionFactory = {
31196     // private
31197     validRegions : ["north","south","east","west","center"],
31198
31199     // private
31200     create : function(target, mgr, config){
31201         target = target.toLowerCase();
31202         if(config.lightweight || config.basic){
31203             return new Roo.BasicLayoutRegion(mgr, config, target);
31204         }
31205         switch(target){
31206             case "north":
31207                 return new Roo.NorthLayoutRegion(mgr, config);
31208             case "south":
31209                 return new Roo.SouthLayoutRegion(mgr, config);
31210             case "east":
31211                 return new Roo.EastLayoutRegion(mgr, config);
31212             case "west":
31213                 return new Roo.WestLayoutRegion(mgr, config);
31214             case "center":
31215                 return new Roo.CenterLayoutRegion(mgr, config);
31216         }
31217         throw 'Layout region "'+target+'" not supported.';
31218     }
31219 };/*
31220  * Based on:
31221  * Ext JS Library 1.1.1
31222  * Copyright(c) 2006-2007, Ext JS, LLC.
31223  *
31224  * Originally Released Under LGPL - original licence link has changed is not relivant.
31225  *
31226  * Fork - LGPL
31227  * <script type="text/javascript">
31228  */
31229  
31230 /**
31231  * @class Roo.BasicLayoutRegion
31232  * @extends Roo.util.Observable
31233  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31234  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31235  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31236  */
31237 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31238     this.mgr = mgr;
31239     this.position  = pos;
31240     this.events = {
31241         /**
31242          * @scope Roo.BasicLayoutRegion
31243          */
31244         
31245         /**
31246          * @event beforeremove
31247          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31248          * @param {Roo.LayoutRegion} this
31249          * @param {Roo.ContentPanel} panel The panel
31250          * @param {Object} e The cancel event object
31251          */
31252         "beforeremove" : true,
31253         /**
31254          * @event invalidated
31255          * Fires when the layout for this region is changed.
31256          * @param {Roo.LayoutRegion} this
31257          */
31258         "invalidated" : true,
31259         /**
31260          * @event visibilitychange
31261          * Fires when this region is shown or hidden 
31262          * @param {Roo.LayoutRegion} this
31263          * @param {Boolean} visibility true or false
31264          */
31265         "visibilitychange" : true,
31266         /**
31267          * @event paneladded
31268          * Fires when a panel is added. 
31269          * @param {Roo.LayoutRegion} this
31270          * @param {Roo.ContentPanel} panel The panel
31271          */
31272         "paneladded" : true,
31273         /**
31274          * @event panelremoved
31275          * Fires when a panel is removed. 
31276          * @param {Roo.LayoutRegion} this
31277          * @param {Roo.ContentPanel} panel The panel
31278          */
31279         "panelremoved" : true,
31280         /**
31281          * @event collapsed
31282          * Fires when this region is collapsed.
31283          * @param {Roo.LayoutRegion} this
31284          */
31285         "collapsed" : true,
31286         /**
31287          * @event expanded
31288          * Fires when this region is expanded.
31289          * @param {Roo.LayoutRegion} this
31290          */
31291         "expanded" : true,
31292         /**
31293          * @event slideshow
31294          * Fires when this region is slid into view.
31295          * @param {Roo.LayoutRegion} this
31296          */
31297         "slideshow" : true,
31298         /**
31299          * @event slidehide
31300          * Fires when this region slides out of view. 
31301          * @param {Roo.LayoutRegion} this
31302          */
31303         "slidehide" : true,
31304         /**
31305          * @event panelactivated
31306          * Fires when a panel is activated. 
31307          * @param {Roo.LayoutRegion} this
31308          * @param {Roo.ContentPanel} panel The activated panel
31309          */
31310         "panelactivated" : true,
31311         /**
31312          * @event resized
31313          * Fires when the user resizes this region. 
31314          * @param {Roo.LayoutRegion} this
31315          * @param {Number} newSize The new size (width for east/west, height for north/south)
31316          */
31317         "resized" : true
31318     };
31319     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31320     this.panels = new Roo.util.MixedCollection();
31321     this.panels.getKey = this.getPanelId.createDelegate(this);
31322     this.box = null;
31323     this.activePanel = null;
31324     // ensure listeners are added...
31325     
31326     if (config.listeners || config.events) {
31327         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31328             listeners : config.listeners || {},
31329             events : config.events || {}
31330         });
31331     }
31332     
31333     if(skipConfig !== true){
31334         this.applyConfig(config);
31335     }
31336 };
31337
31338 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31339     getPanelId : function(p){
31340         return p.getId();
31341     },
31342     
31343     applyConfig : function(config){
31344         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31345         this.config = config;
31346         
31347     },
31348     
31349     /**
31350      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31351      * the width, for horizontal (north, south) the height.
31352      * @param {Number} newSize The new width or height
31353      */
31354     resizeTo : function(newSize){
31355         var el = this.el ? this.el :
31356                  (this.activePanel ? this.activePanel.getEl() : null);
31357         if(el){
31358             switch(this.position){
31359                 case "east":
31360                 case "west":
31361                     el.setWidth(newSize);
31362                     this.fireEvent("resized", this, newSize);
31363                 break;
31364                 case "north":
31365                 case "south":
31366                     el.setHeight(newSize);
31367                     this.fireEvent("resized", this, newSize);
31368                 break;                
31369             }
31370         }
31371     },
31372     
31373     getBox : function(){
31374         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31375     },
31376     
31377     getMargins : function(){
31378         return this.margins;
31379     },
31380     
31381     updateBox : function(box){
31382         this.box = box;
31383         var el = this.activePanel.getEl();
31384         el.dom.style.left = box.x + "px";
31385         el.dom.style.top = box.y + "px";
31386         this.activePanel.setSize(box.width, box.height);
31387     },
31388     
31389     /**
31390      * Returns the container element for this region.
31391      * @return {Roo.Element}
31392      */
31393     getEl : function(){
31394         return this.activePanel;
31395     },
31396     
31397     /**
31398      * Returns true if this region is currently visible.
31399      * @return {Boolean}
31400      */
31401     isVisible : function(){
31402         return this.activePanel ? true : false;
31403     },
31404     
31405     setActivePanel : function(panel){
31406         panel = this.getPanel(panel);
31407         if(this.activePanel && this.activePanel != panel){
31408             this.activePanel.setActiveState(false);
31409             this.activePanel.getEl().setLeftTop(-10000,-10000);
31410         }
31411         this.activePanel = panel;
31412         panel.setActiveState(true);
31413         if(this.box){
31414             panel.setSize(this.box.width, this.box.height);
31415         }
31416         this.fireEvent("panelactivated", this, panel);
31417         this.fireEvent("invalidated");
31418     },
31419     
31420     /**
31421      * Show the specified panel.
31422      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31423      * @return {Roo.ContentPanel} The shown panel or null
31424      */
31425     showPanel : function(panel){
31426         if(panel = this.getPanel(panel)){
31427             this.setActivePanel(panel);
31428         }
31429         return panel;
31430     },
31431     
31432     /**
31433      * Get the active panel for this region.
31434      * @return {Roo.ContentPanel} The active panel or null
31435      */
31436     getActivePanel : function(){
31437         return this.activePanel;
31438     },
31439     
31440     /**
31441      * Add the passed ContentPanel(s)
31442      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31443      * @return {Roo.ContentPanel} The panel added (if only one was added)
31444      */
31445     add : function(panel){
31446         if(arguments.length > 1){
31447             for(var i = 0, len = arguments.length; i < len; i++) {
31448                 this.add(arguments[i]);
31449             }
31450             return null;
31451         }
31452         if(this.hasPanel(panel)){
31453             this.showPanel(panel);
31454             return panel;
31455         }
31456         var el = panel.getEl();
31457         if(el.dom.parentNode != this.mgr.el.dom){
31458             this.mgr.el.dom.appendChild(el.dom);
31459         }
31460         if(panel.setRegion){
31461             panel.setRegion(this);
31462         }
31463         this.panels.add(panel);
31464         el.setStyle("position", "absolute");
31465         if(!panel.background){
31466             this.setActivePanel(panel);
31467             if(this.config.initialSize && this.panels.getCount()==1){
31468                 this.resizeTo(this.config.initialSize);
31469             }
31470         }
31471         this.fireEvent("paneladded", this, panel);
31472         return panel;
31473     },
31474     
31475     /**
31476      * Returns true if the panel is in this region.
31477      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31478      * @return {Boolean}
31479      */
31480     hasPanel : function(panel){
31481         if(typeof panel == "object"){ // must be panel obj
31482             panel = panel.getId();
31483         }
31484         return this.getPanel(panel) ? true : false;
31485     },
31486     
31487     /**
31488      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31489      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31490      * @param {Boolean} preservePanel Overrides the config preservePanel option
31491      * @return {Roo.ContentPanel} The panel that was removed
31492      */
31493     remove : function(panel, preservePanel){
31494         panel = this.getPanel(panel);
31495         if(!panel){
31496             return null;
31497         }
31498         var e = {};
31499         this.fireEvent("beforeremove", this, panel, e);
31500         if(e.cancel === true){
31501             return null;
31502         }
31503         var panelId = panel.getId();
31504         this.panels.removeKey(panelId);
31505         return panel;
31506     },
31507     
31508     /**
31509      * Returns the panel specified or null if it's not in this region.
31510      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31511      * @return {Roo.ContentPanel}
31512      */
31513     getPanel : function(id){
31514         if(typeof id == "object"){ // must be panel obj
31515             return id;
31516         }
31517         return this.panels.get(id);
31518     },
31519     
31520     /**
31521      * Returns this regions position (north/south/east/west/center).
31522      * @return {String} 
31523      */
31524     getPosition: function(){
31525         return this.position;    
31526     }
31527 });/*
31528  * Based on:
31529  * Ext JS Library 1.1.1
31530  * Copyright(c) 2006-2007, Ext JS, LLC.
31531  *
31532  * Originally Released Under LGPL - original licence link has changed is not relivant.
31533  *
31534  * Fork - LGPL
31535  * <script type="text/javascript">
31536  */
31537  
31538 /**
31539  * @class Roo.LayoutRegion
31540  * @extends Roo.BasicLayoutRegion
31541  * This class represents a region in a layout manager.
31542  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31543  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31544  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31545  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31546  * @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})
31547  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31548  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31549  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31550  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31551  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31552  * @cfg {String}    title           The title for the region (overrides panel titles)
31553  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31554  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31555  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31556  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31557  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31558  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31559  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31560  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31561  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31562  * @cfg {Boolean}   showPin         True to show a pin button
31563  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31564  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31565  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31566  * @cfg {Number}    width           For East/West panels
31567  * @cfg {Number}    height          For North/South panels
31568  * @cfg {Boolean}   split           To show the splitter
31569  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31570  */
31571 Roo.LayoutRegion = function(mgr, config, pos){
31572     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31573     var dh = Roo.DomHelper;
31574     /** This region's container element 
31575     * @type Roo.Element */
31576     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31577     /** This region's title element 
31578     * @type Roo.Element */
31579
31580     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31581         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31582         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31583     ]}, true);
31584     this.titleEl.enableDisplayMode();
31585     /** This region's title text element 
31586     * @type HTMLElement */
31587     this.titleTextEl = this.titleEl.dom.firstChild;
31588     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31589     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31590     this.closeBtn.enableDisplayMode();
31591     this.closeBtn.on("click", this.closeClicked, this);
31592     this.closeBtn.hide();
31593
31594     this.createBody(config);
31595     this.visible = true;
31596     this.collapsed = false;
31597
31598     if(config.hideWhenEmpty){
31599         this.hide();
31600         this.on("paneladded", this.validateVisibility, this);
31601         this.on("panelremoved", this.validateVisibility, this);
31602     }
31603     this.applyConfig(config);
31604 };
31605
31606 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31607
31608     createBody : function(){
31609         /** This region's body element 
31610         * @type Roo.Element */
31611         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31612     },
31613
31614     applyConfig : function(c){
31615         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31616             var dh = Roo.DomHelper;
31617             if(c.titlebar !== false){
31618                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31619                 this.collapseBtn.on("click", this.collapse, this);
31620                 this.collapseBtn.enableDisplayMode();
31621
31622                 if(c.showPin === true || this.showPin){
31623                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31624                     this.stickBtn.enableDisplayMode();
31625                     this.stickBtn.on("click", this.expand, this);
31626                     this.stickBtn.hide();
31627                 }
31628             }
31629             /** This region's collapsed element
31630             * @type Roo.Element */
31631             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31632                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31633             ]}, true);
31634             if(c.floatable !== false){
31635                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31636                this.collapsedEl.on("click", this.collapseClick, this);
31637             }
31638
31639             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31640                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31641                    id: "message", unselectable: "on", style:{"float":"left"}});
31642                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31643              }
31644             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31645             this.expandBtn.on("click", this.expand, this);
31646         }
31647         if(this.collapseBtn){
31648             this.collapseBtn.setVisible(c.collapsible == true);
31649         }
31650         this.cmargins = c.cmargins || this.cmargins ||
31651                          (this.position == "west" || this.position == "east" ?
31652                              {top: 0, left: 2, right:2, bottom: 0} :
31653                              {top: 2, left: 0, right:0, bottom: 2});
31654         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31655         this.bottomTabs = c.tabPosition != "top";
31656         this.autoScroll = c.autoScroll || false;
31657         if(this.autoScroll){
31658             this.bodyEl.setStyle("overflow", "auto");
31659         }else{
31660             this.bodyEl.setStyle("overflow", "hidden");
31661         }
31662         //if(c.titlebar !== false){
31663             if((!c.titlebar && !c.title) || c.titlebar === false){
31664                 this.titleEl.hide();
31665             }else{
31666                 this.titleEl.show();
31667                 if(c.title){
31668                     this.titleTextEl.innerHTML = c.title;
31669                 }
31670             }
31671         //}
31672         this.duration = c.duration || .30;
31673         this.slideDuration = c.slideDuration || .45;
31674         this.config = c;
31675         if(c.collapsed){
31676             this.collapse(true);
31677         }
31678         if(c.hidden){
31679             this.hide();
31680         }
31681     },
31682     /**
31683      * Returns true if this region is currently visible.
31684      * @return {Boolean}
31685      */
31686     isVisible : function(){
31687         return this.visible;
31688     },
31689
31690     /**
31691      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31692      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31693      */
31694     setCollapsedTitle : function(title){
31695         title = title || "&#160;";
31696         if(this.collapsedTitleTextEl){
31697             this.collapsedTitleTextEl.innerHTML = title;
31698         }
31699     },
31700
31701     getBox : function(){
31702         var b;
31703         if(!this.collapsed){
31704             b = this.el.getBox(false, true);
31705         }else{
31706             b = this.collapsedEl.getBox(false, true);
31707         }
31708         return b;
31709     },
31710
31711     getMargins : function(){
31712         return this.collapsed ? this.cmargins : this.margins;
31713     },
31714
31715     highlight : function(){
31716         this.el.addClass("x-layout-panel-dragover");
31717     },
31718
31719     unhighlight : function(){
31720         this.el.removeClass("x-layout-panel-dragover");
31721     },
31722
31723     updateBox : function(box){
31724         this.box = box;
31725         if(!this.collapsed){
31726             this.el.dom.style.left = box.x + "px";
31727             this.el.dom.style.top = box.y + "px";
31728             this.updateBody(box.width, box.height);
31729         }else{
31730             this.collapsedEl.dom.style.left = box.x + "px";
31731             this.collapsedEl.dom.style.top = box.y + "px";
31732             this.collapsedEl.setSize(box.width, box.height);
31733         }
31734         if(this.tabs){
31735             this.tabs.autoSizeTabs();
31736         }
31737     },
31738
31739     updateBody : function(w, h){
31740         if(w !== null){
31741             this.el.setWidth(w);
31742             w -= this.el.getBorderWidth("rl");
31743             if(this.config.adjustments){
31744                 w += this.config.adjustments[0];
31745             }
31746         }
31747         if(h !== null){
31748             this.el.setHeight(h);
31749             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31750             h -= this.el.getBorderWidth("tb");
31751             if(this.config.adjustments){
31752                 h += this.config.adjustments[1];
31753             }
31754             this.bodyEl.setHeight(h);
31755             if(this.tabs){
31756                 h = this.tabs.syncHeight(h);
31757             }
31758         }
31759         if(this.panelSize){
31760             w = w !== null ? w : this.panelSize.width;
31761             h = h !== null ? h : this.panelSize.height;
31762         }
31763         if(this.activePanel){
31764             var el = this.activePanel.getEl();
31765             w = w !== null ? w : el.getWidth();
31766             h = h !== null ? h : el.getHeight();
31767             this.panelSize = {width: w, height: h};
31768             this.activePanel.setSize(w, h);
31769         }
31770         if(Roo.isIE && this.tabs){
31771             this.tabs.el.repaint();
31772         }
31773     },
31774
31775     /**
31776      * Returns the container element for this region.
31777      * @return {Roo.Element}
31778      */
31779     getEl : function(){
31780         return this.el;
31781     },
31782
31783     /**
31784      * Hides this region.
31785      */
31786     hide : function(){
31787         if(!this.collapsed){
31788             this.el.dom.style.left = "-2000px";
31789             this.el.hide();
31790         }else{
31791             this.collapsedEl.dom.style.left = "-2000px";
31792             this.collapsedEl.hide();
31793         }
31794         this.visible = false;
31795         this.fireEvent("visibilitychange", this, false);
31796     },
31797
31798     /**
31799      * Shows this region if it was previously hidden.
31800      */
31801     show : function(){
31802         if(!this.collapsed){
31803             this.el.show();
31804         }else{
31805             this.collapsedEl.show();
31806         }
31807         this.visible = true;
31808         this.fireEvent("visibilitychange", this, true);
31809     },
31810
31811     closeClicked : function(){
31812         if(this.activePanel){
31813             this.remove(this.activePanel);
31814         }
31815     },
31816
31817     collapseClick : function(e){
31818         if(this.isSlid){
31819            e.stopPropagation();
31820            this.slideIn();
31821         }else{
31822            e.stopPropagation();
31823            this.slideOut();
31824         }
31825     },
31826
31827     /**
31828      * Collapses this region.
31829      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31830      */
31831     collapse : function(skipAnim){
31832         if(this.collapsed) return;
31833         this.collapsed = true;
31834         if(this.split){
31835             this.split.el.hide();
31836         }
31837         if(this.config.animate && skipAnim !== true){
31838             this.fireEvent("invalidated", this);
31839             this.animateCollapse();
31840         }else{
31841             this.el.setLocation(-20000,-20000);
31842             this.el.hide();
31843             this.collapsedEl.show();
31844             this.fireEvent("collapsed", this);
31845             this.fireEvent("invalidated", this);
31846         }
31847     },
31848
31849     animateCollapse : function(){
31850         // overridden
31851     },
31852
31853     /**
31854      * Expands this region if it was previously collapsed.
31855      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31856      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31857      */
31858     expand : function(e, skipAnim){
31859         if(e) e.stopPropagation();
31860         if(!this.collapsed || this.el.hasActiveFx()) return;
31861         if(this.isSlid){
31862             this.afterSlideIn();
31863             skipAnim = true;
31864         }
31865         this.collapsed = false;
31866         if(this.config.animate && skipAnim !== true){
31867             this.animateExpand();
31868         }else{
31869             this.el.show();
31870             if(this.split){
31871                 this.split.el.show();
31872             }
31873             this.collapsedEl.setLocation(-2000,-2000);
31874             this.collapsedEl.hide();
31875             this.fireEvent("invalidated", this);
31876             this.fireEvent("expanded", this);
31877         }
31878     },
31879
31880     animateExpand : function(){
31881         // overridden
31882     },
31883
31884     initTabs : function()
31885     {
31886         this.bodyEl.setStyle("overflow", "hidden");
31887         var ts = new Roo.TabPanel(
31888                 this.bodyEl.dom,
31889                 {
31890                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31891                     disableTooltips: this.config.disableTabTips,
31892                     toolbar : this.config.toolbar
31893                 }
31894         );
31895         if(this.config.hideTabs){
31896             ts.stripWrap.setDisplayed(false);
31897         }
31898         this.tabs = ts;
31899         ts.resizeTabs = this.config.resizeTabs === true;
31900         ts.minTabWidth = this.config.minTabWidth || 40;
31901         ts.maxTabWidth = this.config.maxTabWidth || 250;
31902         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31903         ts.monitorResize = false;
31904         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31905         ts.bodyEl.addClass('x-layout-tabs-body');
31906         this.panels.each(this.initPanelAsTab, this);
31907     },
31908
31909     initPanelAsTab : function(panel){
31910         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31911                     this.config.closeOnTab && panel.isClosable());
31912         if(panel.tabTip !== undefined){
31913             ti.setTooltip(panel.tabTip);
31914         }
31915         ti.on("activate", function(){
31916               this.setActivePanel(panel);
31917         }, this);
31918         if(this.config.closeOnTab){
31919             ti.on("beforeclose", function(t, e){
31920                 e.cancel = true;
31921                 this.remove(panel);
31922             }, this);
31923         }
31924         return ti;
31925     },
31926
31927     updatePanelTitle : function(panel, title){
31928         if(this.activePanel == panel){
31929             this.updateTitle(title);
31930         }
31931         if(this.tabs){
31932             var ti = this.tabs.getTab(panel.getEl().id);
31933             ti.setText(title);
31934             if(panel.tabTip !== undefined){
31935                 ti.setTooltip(panel.tabTip);
31936             }
31937         }
31938     },
31939
31940     updateTitle : function(title){
31941         if(this.titleTextEl && !this.config.title){
31942             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31943         }
31944     },
31945
31946     setActivePanel : function(panel){
31947         panel = this.getPanel(panel);
31948         if(this.activePanel && this.activePanel != panel){
31949             this.activePanel.setActiveState(false);
31950         }
31951         this.activePanel = panel;
31952         panel.setActiveState(true);
31953         if(this.panelSize){
31954             panel.setSize(this.panelSize.width, this.panelSize.height);
31955         }
31956         if(this.closeBtn){
31957             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31958         }
31959         this.updateTitle(panel.getTitle());
31960         if(this.tabs){
31961             this.fireEvent("invalidated", this);
31962         }
31963         this.fireEvent("panelactivated", this, panel);
31964     },
31965
31966     /**
31967      * Shows the specified panel.
31968      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31969      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31970      */
31971     showPanel : function(panel){
31972         if(panel = this.getPanel(panel)){
31973             if(this.tabs){
31974                 var tab = this.tabs.getTab(panel.getEl().id);
31975                 if(tab.isHidden()){
31976                     this.tabs.unhideTab(tab.id);
31977                 }
31978                 tab.activate();
31979             }else{
31980                 this.setActivePanel(panel);
31981             }
31982         }
31983         return panel;
31984     },
31985
31986     /**
31987      * Get the active panel for this region.
31988      * @return {Roo.ContentPanel} The active panel or null
31989      */
31990     getActivePanel : function(){
31991         return this.activePanel;
31992     },
31993
31994     validateVisibility : function(){
31995         if(this.panels.getCount() < 1){
31996             this.updateTitle("&#160;");
31997             this.closeBtn.hide();
31998             this.hide();
31999         }else{
32000             if(!this.isVisible()){
32001                 this.show();
32002             }
32003         }
32004     },
32005
32006     /**
32007      * Adds the passed ContentPanel(s) to this region.
32008      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32009      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32010      */
32011     add : function(panel){
32012         if(arguments.length > 1){
32013             for(var i = 0, len = arguments.length; i < len; i++) {
32014                 this.add(arguments[i]);
32015             }
32016             return null;
32017         }
32018         if(this.hasPanel(panel)){
32019             this.showPanel(panel);
32020             return panel;
32021         }
32022         panel.setRegion(this);
32023         this.panels.add(panel);
32024         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32025             this.bodyEl.dom.appendChild(panel.getEl().dom);
32026             if(panel.background !== true){
32027                 this.setActivePanel(panel);
32028             }
32029             this.fireEvent("paneladded", this, panel);
32030             return panel;
32031         }
32032         if(!this.tabs){
32033             this.initTabs();
32034         }else{
32035             this.initPanelAsTab(panel);
32036         }
32037         if(panel.background !== true){
32038             this.tabs.activate(panel.getEl().id);
32039         }
32040         this.fireEvent("paneladded", this, panel);
32041         return panel;
32042     },
32043
32044     /**
32045      * Hides the tab for the specified panel.
32046      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32047      */
32048     hidePanel : function(panel){
32049         if(this.tabs && (panel = this.getPanel(panel))){
32050             this.tabs.hideTab(panel.getEl().id);
32051         }
32052     },
32053
32054     /**
32055      * Unhides the tab for a previously hidden panel.
32056      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32057      */
32058     unhidePanel : function(panel){
32059         if(this.tabs && (panel = this.getPanel(panel))){
32060             this.tabs.unhideTab(panel.getEl().id);
32061         }
32062     },
32063
32064     clearPanels : function(){
32065         while(this.panels.getCount() > 0){
32066              this.remove(this.panels.first());
32067         }
32068     },
32069
32070     /**
32071      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32072      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32073      * @param {Boolean} preservePanel Overrides the config preservePanel option
32074      * @return {Roo.ContentPanel} The panel that was removed
32075      */
32076     remove : function(panel, preservePanel){
32077         panel = this.getPanel(panel);
32078         if(!panel){
32079             return null;
32080         }
32081         var e = {};
32082         this.fireEvent("beforeremove", this, panel, e);
32083         if(e.cancel === true){
32084             return null;
32085         }
32086         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32087         var panelId = panel.getId();
32088         this.panels.removeKey(panelId);
32089         if(preservePanel){
32090             document.body.appendChild(panel.getEl().dom);
32091         }
32092         if(this.tabs){
32093             this.tabs.removeTab(panel.getEl().id);
32094         }else if (!preservePanel){
32095             this.bodyEl.dom.removeChild(panel.getEl().dom);
32096         }
32097         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32098             var p = this.panels.first();
32099             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32100             tempEl.appendChild(p.getEl().dom);
32101             this.bodyEl.update("");
32102             this.bodyEl.dom.appendChild(p.getEl().dom);
32103             tempEl = null;
32104             this.updateTitle(p.getTitle());
32105             this.tabs = null;
32106             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32107             this.setActivePanel(p);
32108         }
32109         panel.setRegion(null);
32110         if(this.activePanel == panel){
32111             this.activePanel = null;
32112         }
32113         if(this.config.autoDestroy !== false && preservePanel !== true){
32114             try{panel.destroy();}catch(e){}
32115         }
32116         this.fireEvent("panelremoved", this, panel);
32117         return panel;
32118     },
32119
32120     /**
32121      * Returns the TabPanel component used by this region
32122      * @return {Roo.TabPanel}
32123      */
32124     getTabs : function(){
32125         return this.tabs;
32126     },
32127
32128     createTool : function(parentEl, className){
32129         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32130             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32131         btn.addClassOnOver("x-layout-tools-button-over");
32132         return btn;
32133     }
32134 });/*
32135  * Based on:
32136  * Ext JS Library 1.1.1
32137  * Copyright(c) 2006-2007, Ext JS, LLC.
32138  *
32139  * Originally Released Under LGPL - original licence link has changed is not relivant.
32140  *
32141  * Fork - LGPL
32142  * <script type="text/javascript">
32143  */
32144  
32145
32146
32147 /**
32148  * @class Roo.SplitLayoutRegion
32149  * @extends Roo.LayoutRegion
32150  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32151  */
32152 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32153     this.cursor = cursor;
32154     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32155 };
32156
32157 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32158     splitTip : "Drag to resize.",
32159     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32160     useSplitTips : false,
32161
32162     applyConfig : function(config){
32163         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32164         if(config.split){
32165             if(!this.split){
32166                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32167                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32168                 /** The SplitBar for this region 
32169                 * @type Roo.SplitBar */
32170                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32171                 this.split.on("moved", this.onSplitMove, this);
32172                 this.split.useShim = config.useShim === true;
32173                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32174                 if(this.useSplitTips){
32175                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32176                 }
32177                 if(config.collapsible){
32178                     this.split.el.on("dblclick", this.collapse,  this);
32179                 }
32180             }
32181             if(typeof config.minSize != "undefined"){
32182                 this.split.minSize = config.minSize;
32183             }
32184             if(typeof config.maxSize != "undefined"){
32185                 this.split.maxSize = config.maxSize;
32186             }
32187             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32188                 this.hideSplitter();
32189             }
32190         }
32191     },
32192
32193     getHMaxSize : function(){
32194          var cmax = this.config.maxSize || 10000;
32195          var center = this.mgr.getRegion("center");
32196          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32197     },
32198
32199     getVMaxSize : function(){
32200          var cmax = this.config.maxSize || 10000;
32201          var center = this.mgr.getRegion("center");
32202          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32203     },
32204
32205     onSplitMove : function(split, newSize){
32206         this.fireEvent("resized", this, newSize);
32207     },
32208     
32209     /** 
32210      * Returns the {@link Roo.SplitBar} for this region.
32211      * @return {Roo.SplitBar}
32212      */
32213     getSplitBar : function(){
32214         return this.split;
32215     },
32216     
32217     hide : function(){
32218         this.hideSplitter();
32219         Roo.SplitLayoutRegion.superclass.hide.call(this);
32220     },
32221
32222     hideSplitter : function(){
32223         if(this.split){
32224             this.split.el.setLocation(-2000,-2000);
32225             this.split.el.hide();
32226         }
32227     },
32228
32229     show : function(){
32230         if(this.split){
32231             this.split.el.show();
32232         }
32233         Roo.SplitLayoutRegion.superclass.show.call(this);
32234     },
32235     
32236     beforeSlide: function(){
32237         if(Roo.isGecko){// firefox overflow auto bug workaround
32238             this.bodyEl.clip();
32239             if(this.tabs) this.tabs.bodyEl.clip();
32240             if(this.activePanel){
32241                 this.activePanel.getEl().clip();
32242                 
32243                 if(this.activePanel.beforeSlide){
32244                     this.activePanel.beforeSlide();
32245                 }
32246             }
32247         }
32248     },
32249     
32250     afterSlide : function(){
32251         if(Roo.isGecko){// firefox overflow auto bug workaround
32252             this.bodyEl.unclip();
32253             if(this.tabs) this.tabs.bodyEl.unclip();
32254             if(this.activePanel){
32255                 this.activePanel.getEl().unclip();
32256                 if(this.activePanel.afterSlide){
32257                     this.activePanel.afterSlide();
32258                 }
32259             }
32260         }
32261     },
32262
32263     initAutoHide : function(){
32264         if(this.autoHide !== false){
32265             if(!this.autoHideHd){
32266                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32267                 this.autoHideHd = {
32268                     "mouseout": function(e){
32269                         if(!e.within(this.el, true)){
32270                             st.delay(500);
32271                         }
32272                     },
32273                     "mouseover" : function(e){
32274                         st.cancel();
32275                     },
32276                     scope : this
32277                 };
32278             }
32279             this.el.on(this.autoHideHd);
32280         }
32281     },
32282
32283     clearAutoHide : function(){
32284         if(this.autoHide !== false){
32285             this.el.un("mouseout", this.autoHideHd.mouseout);
32286             this.el.un("mouseover", this.autoHideHd.mouseover);
32287         }
32288     },
32289
32290     clearMonitor : function(){
32291         Roo.get(document).un("click", this.slideInIf, this);
32292     },
32293
32294     // these names are backwards but not changed for compat
32295     slideOut : function(){
32296         if(this.isSlid || this.el.hasActiveFx()){
32297             return;
32298         }
32299         this.isSlid = true;
32300         if(this.collapseBtn){
32301             this.collapseBtn.hide();
32302         }
32303         this.closeBtnState = this.closeBtn.getStyle('display');
32304         this.closeBtn.hide();
32305         if(this.stickBtn){
32306             this.stickBtn.show();
32307         }
32308         this.el.show();
32309         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32310         this.beforeSlide();
32311         this.el.setStyle("z-index", 10001);
32312         this.el.slideIn(this.getSlideAnchor(), {
32313             callback: function(){
32314                 this.afterSlide();
32315                 this.initAutoHide();
32316                 Roo.get(document).on("click", this.slideInIf, this);
32317                 this.fireEvent("slideshow", this);
32318             },
32319             scope: this,
32320             block: true
32321         });
32322     },
32323
32324     afterSlideIn : function(){
32325         this.clearAutoHide();
32326         this.isSlid = false;
32327         this.clearMonitor();
32328         this.el.setStyle("z-index", "");
32329         if(this.collapseBtn){
32330             this.collapseBtn.show();
32331         }
32332         this.closeBtn.setStyle('display', this.closeBtnState);
32333         if(this.stickBtn){
32334             this.stickBtn.hide();
32335         }
32336         this.fireEvent("slidehide", this);
32337     },
32338
32339     slideIn : function(cb){
32340         if(!this.isSlid || this.el.hasActiveFx()){
32341             Roo.callback(cb);
32342             return;
32343         }
32344         this.isSlid = false;
32345         this.beforeSlide();
32346         this.el.slideOut(this.getSlideAnchor(), {
32347             callback: function(){
32348                 this.el.setLeftTop(-10000, -10000);
32349                 this.afterSlide();
32350                 this.afterSlideIn();
32351                 Roo.callback(cb);
32352             },
32353             scope: this,
32354             block: true
32355         });
32356     },
32357     
32358     slideInIf : function(e){
32359         if(!e.within(this.el)){
32360             this.slideIn();
32361         }
32362     },
32363
32364     animateCollapse : function(){
32365         this.beforeSlide();
32366         this.el.setStyle("z-index", 20000);
32367         var anchor = this.getSlideAnchor();
32368         this.el.slideOut(anchor, {
32369             callback : function(){
32370                 this.el.setStyle("z-index", "");
32371                 this.collapsedEl.slideIn(anchor, {duration:.3});
32372                 this.afterSlide();
32373                 this.el.setLocation(-10000,-10000);
32374                 this.el.hide();
32375                 this.fireEvent("collapsed", this);
32376             },
32377             scope: this,
32378             block: true
32379         });
32380     },
32381
32382     animateExpand : function(){
32383         this.beforeSlide();
32384         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32385         this.el.setStyle("z-index", 20000);
32386         this.collapsedEl.hide({
32387             duration:.1
32388         });
32389         this.el.slideIn(this.getSlideAnchor(), {
32390             callback : function(){
32391                 this.el.setStyle("z-index", "");
32392                 this.afterSlide();
32393                 if(this.split){
32394                     this.split.el.show();
32395                 }
32396                 this.fireEvent("invalidated", this);
32397                 this.fireEvent("expanded", this);
32398             },
32399             scope: this,
32400             block: true
32401         });
32402     },
32403
32404     anchors : {
32405         "west" : "left",
32406         "east" : "right",
32407         "north" : "top",
32408         "south" : "bottom"
32409     },
32410
32411     sanchors : {
32412         "west" : "l",
32413         "east" : "r",
32414         "north" : "t",
32415         "south" : "b"
32416     },
32417
32418     canchors : {
32419         "west" : "tl-tr",
32420         "east" : "tr-tl",
32421         "north" : "tl-bl",
32422         "south" : "bl-tl"
32423     },
32424
32425     getAnchor : function(){
32426         return this.anchors[this.position];
32427     },
32428
32429     getCollapseAnchor : function(){
32430         return this.canchors[this.position];
32431     },
32432
32433     getSlideAnchor : function(){
32434         return this.sanchors[this.position];
32435     },
32436
32437     getAlignAdj : function(){
32438         var cm = this.cmargins;
32439         switch(this.position){
32440             case "west":
32441                 return [0, 0];
32442             break;
32443             case "east":
32444                 return [0, 0];
32445             break;
32446             case "north":
32447                 return [0, 0];
32448             break;
32449             case "south":
32450                 return [0, 0];
32451             break;
32452         }
32453     },
32454
32455     getExpandAdj : function(){
32456         var c = this.collapsedEl, cm = this.cmargins;
32457         switch(this.position){
32458             case "west":
32459                 return [-(cm.right+c.getWidth()+cm.left), 0];
32460             break;
32461             case "east":
32462                 return [cm.right+c.getWidth()+cm.left, 0];
32463             break;
32464             case "north":
32465                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32466             break;
32467             case "south":
32468                 return [0, cm.top+cm.bottom+c.getHeight()];
32469             break;
32470         }
32471     }
32472 });/*
32473  * Based on:
32474  * Ext JS Library 1.1.1
32475  * Copyright(c) 2006-2007, Ext JS, LLC.
32476  *
32477  * Originally Released Under LGPL - original licence link has changed is not relivant.
32478  *
32479  * Fork - LGPL
32480  * <script type="text/javascript">
32481  */
32482 /*
32483  * These classes are private internal classes
32484  */
32485 Roo.CenterLayoutRegion = function(mgr, config){
32486     Roo.LayoutRegion.call(this, mgr, config, "center");
32487     this.visible = true;
32488     this.minWidth = config.minWidth || 20;
32489     this.minHeight = config.minHeight || 20;
32490 };
32491
32492 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32493     hide : function(){
32494         // center panel can't be hidden
32495     },
32496     
32497     show : function(){
32498         // center panel can't be hidden
32499     },
32500     
32501     getMinWidth: function(){
32502         return this.minWidth;
32503     },
32504     
32505     getMinHeight: function(){
32506         return this.minHeight;
32507     }
32508 });
32509
32510
32511 Roo.NorthLayoutRegion = function(mgr, config){
32512     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32513     if(this.split){
32514         this.split.placement = Roo.SplitBar.TOP;
32515         this.split.orientation = Roo.SplitBar.VERTICAL;
32516         this.split.el.addClass("x-layout-split-v");
32517     }
32518     var size = config.initialSize || config.height;
32519     if(typeof size != "undefined"){
32520         this.el.setHeight(size);
32521     }
32522 };
32523 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32524     orientation: Roo.SplitBar.VERTICAL,
32525     getBox : function(){
32526         if(this.collapsed){
32527             return this.collapsedEl.getBox();
32528         }
32529         var box = this.el.getBox();
32530         if(this.split){
32531             box.height += this.split.el.getHeight();
32532         }
32533         return box;
32534     },
32535     
32536     updateBox : function(box){
32537         if(this.split && !this.collapsed){
32538             box.height -= this.split.el.getHeight();
32539             this.split.el.setLeft(box.x);
32540             this.split.el.setTop(box.y+box.height);
32541             this.split.el.setWidth(box.width);
32542         }
32543         if(this.collapsed){
32544             this.updateBody(box.width, null);
32545         }
32546         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32547     }
32548 });
32549
32550 Roo.SouthLayoutRegion = function(mgr, config){
32551     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32552     if(this.split){
32553         this.split.placement = Roo.SplitBar.BOTTOM;
32554         this.split.orientation = Roo.SplitBar.VERTICAL;
32555         this.split.el.addClass("x-layout-split-v");
32556     }
32557     var size = config.initialSize || config.height;
32558     if(typeof size != "undefined"){
32559         this.el.setHeight(size);
32560     }
32561 };
32562 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32563     orientation: Roo.SplitBar.VERTICAL,
32564     getBox : function(){
32565         if(this.collapsed){
32566             return this.collapsedEl.getBox();
32567         }
32568         var box = this.el.getBox();
32569         if(this.split){
32570             var sh = this.split.el.getHeight();
32571             box.height += sh;
32572             box.y -= sh;
32573         }
32574         return box;
32575     },
32576     
32577     updateBox : function(box){
32578         if(this.split && !this.collapsed){
32579             var sh = this.split.el.getHeight();
32580             box.height -= sh;
32581             box.y += sh;
32582             this.split.el.setLeft(box.x);
32583             this.split.el.setTop(box.y-sh);
32584             this.split.el.setWidth(box.width);
32585         }
32586         if(this.collapsed){
32587             this.updateBody(box.width, null);
32588         }
32589         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32590     }
32591 });
32592
32593 Roo.EastLayoutRegion = function(mgr, config){
32594     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32595     if(this.split){
32596         this.split.placement = Roo.SplitBar.RIGHT;
32597         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32598         this.split.el.addClass("x-layout-split-h");
32599     }
32600     var size = config.initialSize || config.width;
32601     if(typeof size != "undefined"){
32602         this.el.setWidth(size);
32603     }
32604 };
32605 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32606     orientation: Roo.SplitBar.HORIZONTAL,
32607     getBox : function(){
32608         if(this.collapsed){
32609             return this.collapsedEl.getBox();
32610         }
32611         var box = this.el.getBox();
32612         if(this.split){
32613             var sw = this.split.el.getWidth();
32614             box.width += sw;
32615             box.x -= sw;
32616         }
32617         return box;
32618     },
32619
32620     updateBox : function(box){
32621         if(this.split && !this.collapsed){
32622             var sw = this.split.el.getWidth();
32623             box.width -= sw;
32624             this.split.el.setLeft(box.x);
32625             this.split.el.setTop(box.y);
32626             this.split.el.setHeight(box.height);
32627             box.x += sw;
32628         }
32629         if(this.collapsed){
32630             this.updateBody(null, box.height);
32631         }
32632         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32633     }
32634 });
32635
32636 Roo.WestLayoutRegion = function(mgr, config){
32637     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32638     if(this.split){
32639         this.split.placement = Roo.SplitBar.LEFT;
32640         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32641         this.split.el.addClass("x-layout-split-h");
32642     }
32643     var size = config.initialSize || config.width;
32644     if(typeof size != "undefined"){
32645         this.el.setWidth(size);
32646     }
32647 };
32648 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32649     orientation: Roo.SplitBar.HORIZONTAL,
32650     getBox : function(){
32651         if(this.collapsed){
32652             return this.collapsedEl.getBox();
32653         }
32654         var box = this.el.getBox();
32655         if(this.split){
32656             box.width += this.split.el.getWidth();
32657         }
32658         return box;
32659     },
32660     
32661     updateBox : function(box){
32662         if(this.split && !this.collapsed){
32663             var sw = this.split.el.getWidth();
32664             box.width -= sw;
32665             this.split.el.setLeft(box.x+box.width);
32666             this.split.el.setTop(box.y);
32667             this.split.el.setHeight(box.height);
32668         }
32669         if(this.collapsed){
32670             this.updateBody(null, box.height);
32671         }
32672         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32673     }
32674 });
32675 /*
32676  * Based on:
32677  * Ext JS Library 1.1.1
32678  * Copyright(c) 2006-2007, Ext JS, LLC.
32679  *
32680  * Originally Released Under LGPL - original licence link has changed is not relivant.
32681  *
32682  * Fork - LGPL
32683  * <script type="text/javascript">
32684  */
32685  
32686  
32687 /*
32688  * Private internal class for reading and applying state
32689  */
32690 Roo.LayoutStateManager = function(layout){
32691      // default empty state
32692      this.state = {
32693         north: {},
32694         south: {},
32695         east: {},
32696         west: {}       
32697     };
32698 };
32699
32700 Roo.LayoutStateManager.prototype = {
32701     init : function(layout, provider){
32702         this.provider = provider;
32703         var state = provider.get(layout.id+"-layout-state");
32704         if(state){
32705             var wasUpdating = layout.isUpdating();
32706             if(!wasUpdating){
32707                 layout.beginUpdate();
32708             }
32709             for(var key in state){
32710                 if(typeof state[key] != "function"){
32711                     var rstate = state[key];
32712                     var r = layout.getRegion(key);
32713                     if(r && rstate){
32714                         if(rstate.size){
32715                             r.resizeTo(rstate.size);
32716                         }
32717                         if(rstate.collapsed == true){
32718                             r.collapse(true);
32719                         }else{
32720                             r.expand(null, true);
32721                         }
32722                     }
32723                 }
32724             }
32725             if(!wasUpdating){
32726                 layout.endUpdate();
32727             }
32728             this.state = state; 
32729         }
32730         this.layout = layout;
32731         layout.on("regionresized", this.onRegionResized, this);
32732         layout.on("regioncollapsed", this.onRegionCollapsed, this);
32733         layout.on("regionexpanded", this.onRegionExpanded, this);
32734     },
32735     
32736     storeState : function(){
32737         this.provider.set(this.layout.id+"-layout-state", this.state);
32738     },
32739     
32740     onRegionResized : function(region, newSize){
32741         this.state[region.getPosition()].size = newSize;
32742         this.storeState();
32743     },
32744     
32745     onRegionCollapsed : function(region){
32746         this.state[region.getPosition()].collapsed = true;
32747         this.storeState();
32748     },
32749     
32750     onRegionExpanded : function(region){
32751         this.state[region.getPosition()].collapsed = false;
32752         this.storeState();
32753     }
32754 };/*
32755  * Based on:
32756  * Ext JS Library 1.1.1
32757  * Copyright(c) 2006-2007, Ext JS, LLC.
32758  *
32759  * Originally Released Under LGPL - original licence link has changed is not relivant.
32760  *
32761  * Fork - LGPL
32762  * <script type="text/javascript">
32763  */
32764 /**
32765  * @class Roo.ContentPanel
32766  * @extends Roo.util.Observable
32767  * A basic ContentPanel element.
32768  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
32769  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
32770  * @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
32771  * @cfg {Boolean}   closable      True if the panel can be closed/removed
32772  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
32773  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
32774  * @cfg {Toolbar}   toolbar       A toolbar for this panel
32775  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
32776  * @cfg {String} title          The title for this panel
32777  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
32778  * @cfg {String} url            Calls {@link #setUrl} with this value
32779  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
32780  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
32781  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
32782  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
32783
32784  * @constructor
32785  * Create a new ContentPanel.
32786  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
32787  * @param {String/Object} config A string to set only the title or a config object
32788  * @param {String} content (optional) Set the HTML content for this panel
32789  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
32790  */
32791 Roo.ContentPanel = function(el, config, content){
32792     
32793      
32794     /*
32795     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
32796         config = el;
32797         el = Roo.id();
32798     }
32799     if (config && config.parentLayout) { 
32800         el = config.parentLayout.el.createChild(); 
32801     }
32802     */
32803     if(el.autoCreate){ // xtype is available if this is called from factory
32804         config = el;
32805         el = Roo.id();
32806     }
32807     this.el = Roo.get(el);
32808     if(!this.el && config && config.autoCreate){
32809         if(typeof config.autoCreate == "object"){
32810             if(!config.autoCreate.id){
32811                 config.autoCreate.id = config.id||el;
32812             }
32813             this.el = Roo.DomHelper.append(document.body,
32814                         config.autoCreate, true);
32815         }else{
32816             this.el = Roo.DomHelper.append(document.body,
32817                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
32818         }
32819     }
32820     this.closable = false;
32821     this.loaded = false;
32822     this.active = false;
32823     if(typeof config == "string"){
32824         this.title = config;
32825     }else{
32826         Roo.apply(this, config);
32827     }
32828     
32829     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
32830         this.wrapEl = this.el.wrap();
32831         this.toolbar.container = this.el.insertSibling(false, 'before');
32832         this.toolbar = new Roo.Toolbar(this.toolbar);
32833     }
32834     
32835     
32836     
32837     if(this.resizeEl){
32838         this.resizeEl = Roo.get(this.resizeEl, true);
32839     }else{
32840         this.resizeEl = this.el;
32841     }
32842     this.addEvents({
32843         /**
32844          * @event activate
32845          * Fires when this panel is activated. 
32846          * @param {Roo.ContentPanel} this
32847          */
32848         "activate" : true,
32849         /**
32850          * @event deactivate
32851          * Fires when this panel is activated. 
32852          * @param {Roo.ContentPanel} this
32853          */
32854         "deactivate" : true,
32855
32856         /**
32857          * @event resize
32858          * Fires when this panel is resized if fitToFrame is true.
32859          * @param {Roo.ContentPanel} this
32860          * @param {Number} width The width after any component adjustments
32861          * @param {Number} height The height after any component adjustments
32862          */
32863         "resize" : true,
32864         
32865          /**
32866          * @event render
32867          * Fires when this tab is created
32868          * @param {Roo.ContentPanel} this
32869          */
32870         "render" : true
32871         
32872         
32873         
32874     });
32875     if(this.autoScroll){
32876         this.resizeEl.setStyle("overflow", "auto");
32877     } else {
32878         // fix randome scrolling
32879         this.el.on('scroll', function() {
32880             Roo.log('fix random scolling');
32881             this.scrollTo('top',0); 
32882         });
32883     }
32884     content = content || this.content;
32885     if(content){
32886         this.setContent(content);
32887     }
32888     if(config && config.url){
32889         this.setUrl(this.url, this.params, this.loadOnce);
32890     }
32891     
32892     
32893     
32894     Roo.ContentPanel.superclass.constructor.call(this);
32895     
32896     this.fireEvent('render', this);
32897 };
32898
32899 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
32900     tabTip:'',
32901     setRegion : function(region){
32902         this.region = region;
32903         if(region){
32904            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
32905         }else{
32906            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
32907         } 
32908     },
32909     
32910     /**
32911      * Returns the toolbar for this Panel if one was configured. 
32912      * @return {Roo.Toolbar} 
32913      */
32914     getToolbar : function(){
32915         return this.toolbar;
32916     },
32917     
32918     setActiveState : function(active){
32919         this.active = active;
32920         if(!active){
32921             this.fireEvent("deactivate", this);
32922         }else{
32923             this.fireEvent("activate", this);
32924         }
32925     },
32926     /**
32927      * Updates this panel's element
32928      * @param {String} content The new content
32929      * @param {Boolean} loadScripts (optional) true to look for and process scripts
32930     */
32931     setContent : function(content, loadScripts){
32932         this.el.update(content, loadScripts);
32933     },
32934
32935     ignoreResize : function(w, h){
32936         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
32937             return true;
32938         }else{
32939             this.lastSize = {width: w, height: h};
32940             return false;
32941         }
32942     },
32943     /**
32944      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
32945      * @return {Roo.UpdateManager} The UpdateManager
32946      */
32947     getUpdateManager : function(){
32948         return this.el.getUpdateManager();
32949     },
32950      /**
32951      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
32952      * @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:
32953 <pre><code>
32954 panel.load({
32955     url: "your-url.php",
32956     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
32957     callback: yourFunction,
32958     scope: yourObject, //(optional scope)
32959     discardUrl: false,
32960     nocache: false,
32961     text: "Loading...",
32962     timeout: 30,
32963     scripts: false
32964 });
32965 </code></pre>
32966      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
32967      * 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.
32968      * @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}
32969      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
32970      * @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.
32971      * @return {Roo.ContentPanel} this
32972      */
32973     load : function(){
32974         var um = this.el.getUpdateManager();
32975         um.update.apply(um, arguments);
32976         return this;
32977     },
32978
32979
32980     /**
32981      * 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.
32982      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
32983      * @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)
32984      * @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)
32985      * @return {Roo.UpdateManager} The UpdateManager
32986      */
32987     setUrl : function(url, params, loadOnce){
32988         if(this.refreshDelegate){
32989             this.removeListener("activate", this.refreshDelegate);
32990         }
32991         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
32992         this.on("activate", this.refreshDelegate);
32993         return this.el.getUpdateManager();
32994     },
32995     
32996     _handleRefresh : function(url, params, loadOnce){
32997         if(!loadOnce || !this.loaded){
32998             var updater = this.el.getUpdateManager();
32999             updater.update(url, params, this._setLoaded.createDelegate(this));
33000         }
33001     },
33002     
33003     _setLoaded : function(){
33004         this.loaded = true;
33005     }, 
33006     
33007     /**
33008      * Returns this panel's id
33009      * @return {String} 
33010      */
33011     getId : function(){
33012         return this.el.id;
33013     },
33014     
33015     /** 
33016      * Returns this panel's element - used by regiosn to add.
33017      * @return {Roo.Element} 
33018      */
33019     getEl : function(){
33020         return this.wrapEl || this.el;
33021     },
33022     
33023     adjustForComponents : function(width, height){
33024         if(this.resizeEl != this.el){
33025             width -= this.el.getFrameWidth('lr');
33026             height -= this.el.getFrameWidth('tb');
33027         }
33028         if(this.toolbar){
33029             var te = this.toolbar.getEl();
33030             height -= te.getHeight();
33031             te.setWidth(width);
33032         }
33033         if(this.adjustments){
33034             width += this.adjustments[0];
33035             height += this.adjustments[1];
33036         }
33037         return {"width": width, "height": height};
33038     },
33039     
33040     setSize : function(width, height){
33041         if(this.fitToFrame && !this.ignoreResize(width, height)){
33042             if(this.fitContainer && this.resizeEl != this.el){
33043                 this.el.setSize(width, height);
33044             }
33045             var size = this.adjustForComponents(width, height);
33046             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33047             this.fireEvent('resize', this, size.width, size.height);
33048         }
33049     },
33050     
33051     /**
33052      * Returns this panel's title
33053      * @return {String} 
33054      */
33055     getTitle : function(){
33056         return this.title;
33057     },
33058     
33059     /**
33060      * Set this panel's title
33061      * @param {String} title
33062      */
33063     setTitle : function(title){
33064         this.title = title;
33065         if(this.region){
33066             this.region.updatePanelTitle(this, title);
33067         }
33068     },
33069     
33070     /**
33071      * Returns true is this panel was configured to be closable
33072      * @return {Boolean} 
33073      */
33074     isClosable : function(){
33075         return this.closable;
33076     },
33077     
33078     beforeSlide : function(){
33079         this.el.clip();
33080         this.resizeEl.clip();
33081     },
33082     
33083     afterSlide : function(){
33084         this.el.unclip();
33085         this.resizeEl.unclip();
33086     },
33087     
33088     /**
33089      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33090      *   Will fail silently if the {@link #setUrl} method has not been called.
33091      *   This does not activate the panel, just updates its content.
33092      */
33093     refresh : function(){
33094         if(this.refreshDelegate){
33095            this.loaded = false;
33096            this.refreshDelegate();
33097         }
33098     },
33099     
33100     /**
33101      * Destroys this panel
33102      */
33103     destroy : function(){
33104         this.el.removeAllListeners();
33105         var tempEl = document.createElement("span");
33106         tempEl.appendChild(this.el.dom);
33107         tempEl.innerHTML = "";
33108         this.el.remove();
33109         this.el = null;
33110     },
33111     
33112     /**
33113      * form - if the content panel contains a form - this is a reference to it.
33114      * @type {Roo.form.Form}
33115      */
33116     form : false,
33117     /**
33118      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33119      *    This contains a reference to it.
33120      * @type {Roo.View}
33121      */
33122     view : false,
33123     
33124       /**
33125      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33126      * <pre><code>
33127
33128 layout.addxtype({
33129        xtype : 'Form',
33130        items: [ .... ]
33131    }
33132 );
33133
33134 </code></pre>
33135      * @param {Object} cfg Xtype definition of item to add.
33136      */
33137     
33138     addxtype : function(cfg) {
33139         // add form..
33140         if (cfg.xtype.match(/^Form$/)) {
33141             var el = this.el.createChild();
33142
33143             this.form = new  Roo.form.Form(cfg);
33144             
33145             
33146             if ( this.form.allItems.length) this.form.render(el.dom);
33147             return this.form;
33148         }
33149         // should only have one of theses..
33150         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33151             // views..
33152             cfg.el = this.el.appendChild(document.createElement("div"));
33153             // factory?
33154             
33155             var ret = new Roo.factory(cfg);
33156             ret.render && ret.render(false, ''); // render blank..
33157             this.view = ret;
33158             return ret;
33159         }
33160         return false;
33161     }
33162 });
33163
33164 /**
33165  * @class Roo.GridPanel
33166  * @extends Roo.ContentPanel
33167  * @constructor
33168  * Create a new GridPanel.
33169  * @param {Roo.grid.Grid} grid The grid for this panel
33170  * @param {String/Object} config A string to set only the panel's title, or a config object
33171  */
33172 Roo.GridPanel = function(grid, config){
33173     
33174   
33175     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33176         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33177         
33178     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33179     
33180     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33181     
33182     if(this.toolbar){
33183         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33184     }
33185     // xtype created footer. - not sure if will work as we normally have to render first..
33186     if (this.footer && !this.footer.el && this.footer.xtype) {
33187         
33188         this.footer.container = this.grid.getView().getFooterPanel(true);
33189         this.footer.dataSource = this.grid.dataSource;
33190         this.footer = Roo.factory(this.footer, Roo);
33191         
33192     }
33193     
33194     grid.monitorWindowResize = false; // turn off autosizing
33195     grid.autoHeight = false;
33196     grid.autoWidth = false;
33197     this.grid = grid;
33198     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33199 };
33200
33201 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33202     getId : function(){
33203         return this.grid.id;
33204     },
33205     
33206     /**
33207      * Returns the grid for this panel
33208      * @return {Roo.grid.Grid} 
33209      */
33210     getGrid : function(){
33211         return this.grid;    
33212     },
33213     
33214     setSize : function(width, height){
33215         if(!this.ignoreResize(width, height)){
33216             var grid = this.grid;
33217             var size = this.adjustForComponents(width, height);
33218             grid.getGridEl().setSize(size.width, size.height);
33219             grid.autoSize();
33220         }
33221     },
33222     
33223     beforeSlide : function(){
33224         this.grid.getView().scroller.clip();
33225     },
33226     
33227     afterSlide : function(){
33228         this.grid.getView().scroller.unclip();
33229     },
33230     
33231     destroy : function(){
33232         this.grid.destroy();
33233         delete this.grid;
33234         Roo.GridPanel.superclass.destroy.call(this); 
33235     }
33236 });
33237
33238
33239 /**
33240  * @class Roo.NestedLayoutPanel
33241  * @extends Roo.ContentPanel
33242  * @constructor
33243  * Create a new NestedLayoutPanel.
33244  * 
33245  * 
33246  * @param {Roo.BorderLayout} layout The layout for this panel
33247  * @param {String/Object} config A string to set only the title or a config object
33248  */
33249 Roo.NestedLayoutPanel = function(layout, config)
33250 {
33251     // construct with only one argument..
33252     /* FIXME - implement nicer consturctors
33253     if (layout.layout) {
33254         config = layout;
33255         layout = config.layout;
33256         delete config.layout;
33257     }
33258     if (layout.xtype && !layout.getEl) {
33259         // then layout needs constructing..
33260         layout = Roo.factory(layout, Roo);
33261     }
33262     */
33263     
33264     
33265     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33266     
33267     layout.monitorWindowResize = false; // turn off autosizing
33268     this.layout = layout;
33269     this.layout.getEl().addClass("x-layout-nested-layout");
33270     
33271     
33272     
33273     
33274 };
33275
33276 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33277
33278     setSize : function(width, height){
33279         if(!this.ignoreResize(width, height)){
33280             var size = this.adjustForComponents(width, height);
33281             var el = this.layout.getEl();
33282             el.setSize(size.width, size.height);
33283             var touch = el.dom.offsetWidth;
33284             this.layout.layout();
33285             // ie requires a double layout on the first pass
33286             if(Roo.isIE && !this.initialized){
33287                 this.initialized = true;
33288                 this.layout.layout();
33289             }
33290         }
33291     },
33292     
33293     // activate all subpanels if not currently active..
33294     
33295     setActiveState : function(active){
33296         this.active = active;
33297         if(!active){
33298             this.fireEvent("deactivate", this);
33299             return;
33300         }
33301         
33302         this.fireEvent("activate", this);
33303         // not sure if this should happen before or after..
33304         if (!this.layout) {
33305             return; // should not happen..
33306         }
33307         var reg = false;
33308         for (var r in this.layout.regions) {
33309             reg = this.layout.getRegion(r);
33310             if (reg.getActivePanel()) {
33311                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33312                 reg.setActivePanel(reg.getActivePanel());
33313                 continue;
33314             }
33315             if (!reg.panels.length) {
33316                 continue;
33317             }
33318             reg.showPanel(reg.getPanel(0));
33319         }
33320         
33321         
33322         
33323         
33324     },
33325     
33326     /**
33327      * Returns the nested BorderLayout for this panel
33328      * @return {Roo.BorderLayout} 
33329      */
33330     getLayout : function(){
33331         return this.layout;
33332     },
33333     
33334      /**
33335      * Adds a xtype elements to the layout of the nested panel
33336      * <pre><code>
33337
33338 panel.addxtype({
33339        xtype : 'ContentPanel',
33340        region: 'west',
33341        items: [ .... ]
33342    }
33343 );
33344
33345 panel.addxtype({
33346         xtype : 'NestedLayoutPanel',
33347         region: 'west',
33348         layout: {
33349            center: { },
33350            west: { }   
33351         },
33352         items : [ ... list of content panels or nested layout panels.. ]
33353    }
33354 );
33355 </code></pre>
33356      * @param {Object} cfg Xtype definition of item to add.
33357      */
33358     addxtype : function(cfg) {
33359         return this.layout.addxtype(cfg);
33360     
33361     }
33362 });
33363
33364 Roo.ScrollPanel = function(el, config, content){
33365     config = config || {};
33366     config.fitToFrame = true;
33367     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33368     
33369     this.el.dom.style.overflow = "hidden";
33370     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33371     this.el.removeClass("x-layout-inactive-content");
33372     this.el.on("mousewheel", this.onWheel, this);
33373
33374     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33375     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33376     up.unselectable(); down.unselectable();
33377     up.on("click", this.scrollUp, this);
33378     down.on("click", this.scrollDown, this);
33379     up.addClassOnOver("x-scroller-btn-over");
33380     down.addClassOnOver("x-scroller-btn-over");
33381     up.addClassOnClick("x-scroller-btn-click");
33382     down.addClassOnClick("x-scroller-btn-click");
33383     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33384
33385     this.resizeEl = this.el;
33386     this.el = wrap; this.up = up; this.down = down;
33387 };
33388
33389 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33390     increment : 100,
33391     wheelIncrement : 5,
33392     scrollUp : function(){
33393         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33394     },
33395
33396     scrollDown : function(){
33397         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33398     },
33399
33400     afterScroll : function(){
33401         var el = this.resizeEl;
33402         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33403         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33404         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33405     },
33406
33407     setSize : function(){
33408         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33409         this.afterScroll();
33410     },
33411
33412     onWheel : function(e){
33413         var d = e.getWheelDelta();
33414         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33415         this.afterScroll();
33416         e.stopEvent();
33417     },
33418
33419     setContent : function(content, loadScripts){
33420         this.resizeEl.update(content, loadScripts);
33421     }
33422
33423 });
33424
33425
33426
33427
33428
33429
33430
33431
33432
33433 /**
33434  * @class Roo.TreePanel
33435  * @extends Roo.ContentPanel
33436  * @constructor
33437  * Create a new TreePanel. - defaults to fit/scoll contents.
33438  * @param {String/Object} config A string to set only the panel's title, or a config object
33439  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
33440  */
33441 Roo.TreePanel = function(config){
33442     var el = config.el;
33443     var tree = config.tree;
33444     delete config.tree; 
33445     delete config.el; // hopefull!
33446     
33447     // wrapper for IE7 strict & safari scroll issue
33448     
33449     var treeEl = el.createChild();
33450     config.resizeEl = treeEl;
33451     
33452     
33453     
33454     Roo.TreePanel.superclass.constructor.call(this, el, config);
33455  
33456  
33457     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33458     //console.log(tree);
33459     this.on('activate', function()
33460     {
33461         if (this.tree.rendered) {
33462             return;
33463         }
33464         //console.log('render tree');
33465         this.tree.render();
33466     });
33467     
33468     this.on('resize',  function (cp, w, h) {
33469             this.tree.innerCt.setWidth(w);
33470             this.tree.innerCt.setHeight(h);
33471             this.tree.innerCt.setStyle('overflow-y', 'auto');
33472     });
33473
33474         
33475     
33476 };
33477
33478 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33479     fitToFrame : true,
33480     autoScroll : true
33481 });
33482
33483
33484
33485
33486
33487
33488
33489
33490
33491
33492
33493 /*
33494  * Based on:
33495  * Ext JS Library 1.1.1
33496  * Copyright(c) 2006-2007, Ext JS, LLC.
33497  *
33498  * Originally Released Under LGPL - original licence link has changed is not relivant.
33499  *
33500  * Fork - LGPL
33501  * <script type="text/javascript">
33502  */
33503  
33504
33505 /**
33506  * @class Roo.ReaderLayout
33507  * @extends Roo.BorderLayout
33508  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33509  * center region containing two nested regions (a top one for a list view and one for item preview below),
33510  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33511  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33512  * expedites the setup of the overall layout and regions for this common application style.
33513  * Example:
33514  <pre><code>
33515 var reader = new Roo.ReaderLayout();
33516 var CP = Roo.ContentPanel;  // shortcut for adding
33517
33518 reader.beginUpdate();
33519 reader.add("north", new CP("north", "North"));
33520 reader.add("west", new CP("west", {title: "West"}));
33521 reader.add("east", new CP("east", {title: "East"}));
33522
33523 reader.regions.listView.add(new CP("listView", "List"));
33524 reader.regions.preview.add(new CP("preview", "Preview"));
33525 reader.endUpdate();
33526 </code></pre>
33527 * @constructor
33528 * Create a new ReaderLayout
33529 * @param {Object} config Configuration options
33530 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33531 * document.body if omitted)
33532 */
33533 Roo.ReaderLayout = function(config, renderTo){
33534     var c = config || {size:{}};
33535     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33536         north: c.north !== false ? Roo.apply({
33537             split:false,
33538             initialSize: 32,
33539             titlebar: false
33540         }, c.north) : false,
33541         west: c.west !== false ? Roo.apply({
33542             split:true,
33543             initialSize: 200,
33544             minSize: 175,
33545             maxSize: 400,
33546             titlebar: true,
33547             collapsible: true,
33548             animate: true,
33549             margins:{left:5,right:0,bottom:5,top:5},
33550             cmargins:{left:5,right:5,bottom:5,top:5}
33551         }, c.west) : false,
33552         east: c.east !== false ? Roo.apply({
33553             split:true,
33554             initialSize: 200,
33555             minSize: 175,
33556             maxSize: 400,
33557             titlebar: true,
33558             collapsible: true,
33559             animate: true,
33560             margins:{left:0,right:5,bottom:5,top:5},
33561             cmargins:{left:5,right:5,bottom:5,top:5}
33562         }, c.east) : false,
33563         center: Roo.apply({
33564             tabPosition: 'top',
33565             autoScroll:false,
33566             closeOnTab: true,
33567             titlebar:false,
33568             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33569         }, c.center)
33570     });
33571
33572     this.el.addClass('x-reader');
33573
33574     this.beginUpdate();
33575
33576     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33577         south: c.preview !== false ? Roo.apply({
33578             split:true,
33579             initialSize: 200,
33580             minSize: 100,
33581             autoScroll:true,
33582             collapsible:true,
33583             titlebar: true,
33584             cmargins:{top:5,left:0, right:0, bottom:0}
33585         }, c.preview) : false,
33586         center: Roo.apply({
33587             autoScroll:false,
33588             titlebar:false,
33589             minHeight:200
33590         }, c.listView)
33591     });
33592     this.add('center', new Roo.NestedLayoutPanel(inner,
33593             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33594
33595     this.endUpdate();
33596
33597     this.regions.preview = inner.getRegion('south');
33598     this.regions.listView = inner.getRegion('center');
33599 };
33600
33601 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33602  * Based on:
33603  * Ext JS Library 1.1.1
33604  * Copyright(c) 2006-2007, Ext JS, LLC.
33605  *
33606  * Originally Released Under LGPL - original licence link has changed is not relivant.
33607  *
33608  * Fork - LGPL
33609  * <script type="text/javascript">
33610  */
33611  
33612 /**
33613  * @class Roo.grid.Grid
33614  * @extends Roo.util.Observable
33615  * This class represents the primary interface of a component based grid control.
33616  * <br><br>Usage:<pre><code>
33617  var grid = new Roo.grid.Grid("my-container-id", {
33618      ds: myDataStore,
33619      cm: myColModel,
33620      selModel: mySelectionModel,
33621      autoSizeColumns: true,
33622      monitorWindowResize: false,
33623      trackMouseOver: true
33624  });
33625  // set any options
33626  grid.render();
33627  * </code></pre>
33628  * <b>Common Problems:</b><br/>
33629  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33630  * element will correct this<br/>
33631  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33632  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33633  * are unpredictable.<br/>
33634  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33635  * grid to calculate dimensions/offsets.<br/>
33636   * @constructor
33637  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33638  * The container MUST have some type of size defined for the grid to fill. The container will be
33639  * automatically set to position relative if it isn't already.
33640  * @param {Object} config A config object that sets properties on this grid.
33641  */
33642 Roo.grid.Grid = function(container, config){
33643         // initialize the container
33644         this.container = Roo.get(container);
33645         this.container.update("");
33646         this.container.setStyle("overflow", "hidden");
33647     this.container.addClass('x-grid-container');
33648
33649     this.id = this.container.id;
33650
33651     Roo.apply(this, config);
33652     // check and correct shorthanded configs
33653     if(this.ds){
33654         this.dataSource = this.ds;
33655         delete this.ds;
33656     }
33657     if(this.cm){
33658         this.colModel = this.cm;
33659         delete this.cm;
33660     }
33661     if(this.sm){
33662         this.selModel = this.sm;
33663         delete this.sm;
33664     }
33665
33666     if (this.selModel) {
33667         this.selModel = Roo.factory(this.selModel, Roo.grid);
33668         this.sm = this.selModel;
33669         this.sm.xmodule = this.xmodule || false;
33670     }
33671     if (typeof(this.colModel.config) == 'undefined') {
33672         this.colModel = new Roo.grid.ColumnModel(this.colModel);
33673         this.cm = this.colModel;
33674         this.cm.xmodule = this.xmodule || false;
33675     }
33676     if (this.dataSource) {
33677         this.dataSource= Roo.factory(this.dataSource, Roo.data);
33678         this.ds = this.dataSource;
33679         this.ds.xmodule = this.xmodule || false;
33680          
33681     }
33682     
33683     
33684     
33685     if(this.width){
33686         this.container.setWidth(this.width);
33687     }
33688
33689     if(this.height){
33690         this.container.setHeight(this.height);
33691     }
33692     /** @private */
33693         this.addEvents({
33694         // raw events
33695         /**
33696          * @event click
33697          * The raw click event for the entire grid.
33698          * @param {Roo.EventObject} e
33699          */
33700         "click" : true,
33701         /**
33702          * @event dblclick
33703          * The raw dblclick event for the entire grid.
33704          * @param {Roo.EventObject} e
33705          */
33706         "dblclick" : true,
33707         /**
33708          * @event contextmenu
33709          * The raw contextmenu event for the entire grid.
33710          * @param {Roo.EventObject} e
33711          */
33712         "contextmenu" : true,
33713         /**
33714          * @event mousedown
33715          * The raw mousedown event for the entire grid.
33716          * @param {Roo.EventObject} e
33717          */
33718         "mousedown" : true,
33719         /**
33720          * @event mouseup
33721          * The raw mouseup event for the entire grid.
33722          * @param {Roo.EventObject} e
33723          */
33724         "mouseup" : true,
33725         /**
33726          * @event mouseover
33727          * The raw mouseover event for the entire grid.
33728          * @param {Roo.EventObject} e
33729          */
33730         "mouseover" : true,
33731         /**
33732          * @event mouseout
33733          * The raw mouseout event for the entire grid.
33734          * @param {Roo.EventObject} e
33735          */
33736         "mouseout" : true,
33737         /**
33738          * @event keypress
33739          * The raw keypress event for the entire grid.
33740          * @param {Roo.EventObject} e
33741          */
33742         "keypress" : true,
33743         /**
33744          * @event keydown
33745          * The raw keydown event for the entire grid.
33746          * @param {Roo.EventObject} e
33747          */
33748         "keydown" : true,
33749
33750         // custom events
33751
33752         /**
33753          * @event cellclick
33754          * Fires when a cell is clicked
33755          * @param {Grid} this
33756          * @param {Number} rowIndex
33757          * @param {Number} columnIndex
33758          * @param {Roo.EventObject} e
33759          */
33760         "cellclick" : true,
33761         /**
33762          * @event celldblclick
33763          * Fires when a cell is double clicked
33764          * @param {Grid} this
33765          * @param {Number} rowIndex
33766          * @param {Number} columnIndex
33767          * @param {Roo.EventObject} e
33768          */
33769         "celldblclick" : true,
33770         /**
33771          * @event rowclick
33772          * Fires when a row is clicked
33773          * @param {Grid} this
33774          * @param {Number} rowIndex
33775          * @param {Roo.EventObject} e
33776          */
33777         "rowclick" : true,
33778         /**
33779          * @event rowdblclick
33780          * Fires when a row is double clicked
33781          * @param {Grid} this
33782          * @param {Number} rowIndex
33783          * @param {Roo.EventObject} e
33784          */
33785         "rowdblclick" : true,
33786         /**
33787          * @event headerclick
33788          * Fires when a header is clicked
33789          * @param {Grid} this
33790          * @param {Number} columnIndex
33791          * @param {Roo.EventObject} e
33792          */
33793         "headerclick" : true,
33794         /**
33795          * @event headerdblclick
33796          * Fires when a header cell is double clicked
33797          * @param {Grid} this
33798          * @param {Number} columnIndex
33799          * @param {Roo.EventObject} e
33800          */
33801         "headerdblclick" : true,
33802         /**
33803          * @event rowcontextmenu
33804          * Fires when a row is right clicked
33805          * @param {Grid} this
33806          * @param {Number} rowIndex
33807          * @param {Roo.EventObject} e
33808          */
33809         "rowcontextmenu" : true,
33810         /**
33811          * @event cellcontextmenu
33812          * Fires when a cell is right clicked
33813          * @param {Grid} this
33814          * @param {Number} rowIndex
33815          * @param {Number} cellIndex
33816          * @param {Roo.EventObject} e
33817          */
33818          "cellcontextmenu" : true,
33819         /**
33820          * @event headercontextmenu
33821          * Fires when a header is right clicked
33822          * @param {Grid} this
33823          * @param {Number} columnIndex
33824          * @param {Roo.EventObject} e
33825          */
33826         "headercontextmenu" : true,
33827         /**
33828          * @event bodyscroll
33829          * Fires when the body element is scrolled
33830          * @param {Number} scrollLeft
33831          * @param {Number} scrollTop
33832          */
33833         "bodyscroll" : true,
33834         /**
33835          * @event columnresize
33836          * Fires when the user resizes a column
33837          * @param {Number} columnIndex
33838          * @param {Number} newSize
33839          */
33840         "columnresize" : true,
33841         /**
33842          * @event columnmove
33843          * Fires when the user moves a column
33844          * @param {Number} oldIndex
33845          * @param {Number} newIndex
33846          */
33847         "columnmove" : true,
33848         /**
33849          * @event startdrag
33850          * Fires when row(s) start being dragged
33851          * @param {Grid} this
33852          * @param {Roo.GridDD} dd The drag drop object
33853          * @param {event} e The raw browser event
33854          */
33855         "startdrag" : true,
33856         /**
33857          * @event enddrag
33858          * Fires when a drag operation is complete
33859          * @param {Grid} this
33860          * @param {Roo.GridDD} dd The drag drop object
33861          * @param {event} e The raw browser event
33862          */
33863         "enddrag" : true,
33864         /**
33865          * @event dragdrop
33866          * Fires when dragged row(s) are dropped on a valid DD target
33867          * @param {Grid} this
33868          * @param {Roo.GridDD} dd The drag drop object
33869          * @param {String} targetId The target drag drop object
33870          * @param {event} e The raw browser event
33871          */
33872         "dragdrop" : true,
33873         /**
33874          * @event dragover
33875          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
33876          * @param {Grid} this
33877          * @param {Roo.GridDD} dd The drag drop object
33878          * @param {String} targetId The target drag drop object
33879          * @param {event} e The raw browser event
33880          */
33881         "dragover" : true,
33882         /**
33883          * @event dragenter
33884          *  Fires when the dragged row(s) first cross another DD target while being dragged
33885          * @param {Grid} this
33886          * @param {Roo.GridDD} dd The drag drop object
33887          * @param {String} targetId The target drag drop object
33888          * @param {event} e The raw browser event
33889          */
33890         "dragenter" : true,
33891         /**
33892          * @event dragout
33893          * Fires when the dragged row(s) leave another DD target while being dragged
33894          * @param {Grid} this
33895          * @param {Roo.GridDD} dd The drag drop object
33896          * @param {String} targetId The target drag drop object
33897          * @param {event} e The raw browser event
33898          */
33899         "dragout" : true,
33900         /**
33901          * @event rowclass
33902          * Fires when a row is rendered, so you can change add a style to it.
33903          * @param {GridView} gridview   The grid view
33904          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
33905          */
33906         'rowclass' : true,
33907
33908         /**
33909          * @event render
33910          * Fires when the grid is rendered
33911          * @param {Grid} grid
33912          */
33913         'render' : true
33914     });
33915
33916     Roo.grid.Grid.superclass.constructor.call(this);
33917 };
33918 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
33919     
33920     /**
33921      * @cfg {String} ddGroup - drag drop group.
33922      */
33923
33924     /**
33925      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
33926      */
33927     minColumnWidth : 25,
33928
33929     /**
33930      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
33931      * <b>on initial render.</b> It is more efficient to explicitly size the columns
33932      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
33933      */
33934     autoSizeColumns : false,
33935
33936     /**
33937      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
33938      */
33939     autoSizeHeaders : true,
33940
33941     /**
33942      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
33943      */
33944     monitorWindowResize : true,
33945
33946     /**
33947      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
33948      * rows measured to get a columns size. Default is 0 (all rows).
33949      */
33950     maxRowsToMeasure : 0,
33951
33952     /**
33953      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
33954      */
33955     trackMouseOver : true,
33956
33957     /**
33958     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
33959     */
33960     
33961     /**
33962     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
33963     */
33964     enableDragDrop : false,
33965     
33966     /**
33967     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
33968     */
33969     enableColumnMove : true,
33970     
33971     /**
33972     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
33973     */
33974     enableColumnHide : true,
33975     
33976     /**
33977     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
33978     */
33979     enableRowHeightSync : false,
33980     
33981     /**
33982     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
33983     */
33984     stripeRows : true,
33985     
33986     /**
33987     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
33988     */
33989     autoHeight : false,
33990
33991     /**
33992      * @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.
33993      */
33994     autoExpandColumn : false,
33995
33996     /**
33997     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
33998     * Default is 50.
33999     */
34000     autoExpandMin : 50,
34001
34002     /**
34003     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34004     */
34005     autoExpandMax : 1000,
34006
34007     /**
34008     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34009     */
34010     view : null,
34011
34012     /**
34013     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34014     */
34015     loadMask : false,
34016     /**
34017     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
34018     */
34019     dropTarget: false,
34020     
34021    
34022     
34023     // private
34024     rendered : false,
34025
34026     /**
34027     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34028     * of a fixed width. Default is false.
34029     */
34030     /**
34031     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34032     */
34033     /**
34034      * Called once after all setup has been completed and the grid is ready to be rendered.
34035      * @return {Roo.grid.Grid} this
34036      */
34037     render : function()
34038     {
34039         var c = this.container;
34040         // try to detect autoHeight/width mode
34041         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34042             this.autoHeight = true;
34043         }
34044         var view = this.getView();
34045         view.init(this);
34046
34047         c.on("click", this.onClick, this);
34048         c.on("dblclick", this.onDblClick, this);
34049         c.on("contextmenu", this.onContextMenu, this);
34050         c.on("keydown", this.onKeyDown, this);
34051
34052         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34053
34054         this.getSelectionModel().init(this);
34055
34056         view.render();
34057
34058         if(this.loadMask){
34059             this.loadMask = new Roo.LoadMask(this.container,
34060                     Roo.apply({store:this.dataSource}, this.loadMask));
34061         }
34062         
34063         
34064         if (this.toolbar && this.toolbar.xtype) {
34065             this.toolbar.container = this.getView().getHeaderPanel(true);
34066             this.toolbar = new Roo.Toolbar(this.toolbar);
34067         }
34068         if (this.footer && this.footer.xtype) {
34069             this.footer.dataSource = this.getDataSource();
34070             this.footer.container = this.getView().getFooterPanel(true);
34071             this.footer = Roo.factory(this.footer, Roo);
34072         }
34073         if (this.dropTarget && this.dropTarget.xtype) {
34074             delete this.dropTarget.xtype;
34075             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34076         }
34077         
34078         
34079         this.rendered = true;
34080         this.fireEvent('render', this);
34081         return this;
34082     },
34083
34084         /**
34085          * Reconfigures the grid to use a different Store and Column Model.
34086          * The View will be bound to the new objects and refreshed.
34087          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34088          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34089          */
34090     reconfigure : function(dataSource, colModel){
34091         if(this.loadMask){
34092             this.loadMask.destroy();
34093             this.loadMask = new Roo.LoadMask(this.container,
34094                     Roo.apply({store:dataSource}, this.loadMask));
34095         }
34096         this.view.bind(dataSource, colModel);
34097         this.dataSource = dataSource;
34098         this.colModel = colModel;
34099         this.view.refresh(true);
34100     },
34101
34102     // private
34103     onKeyDown : function(e){
34104         this.fireEvent("keydown", e);
34105     },
34106
34107     /**
34108      * Destroy this grid.
34109      * @param {Boolean} removeEl True to remove the element
34110      */
34111     destroy : function(removeEl, keepListeners){
34112         if(this.loadMask){
34113             this.loadMask.destroy();
34114         }
34115         var c = this.container;
34116         c.removeAllListeners();
34117         this.view.destroy();
34118         this.colModel.purgeListeners();
34119         if(!keepListeners){
34120             this.purgeListeners();
34121         }
34122         c.update("");
34123         if(removeEl === true){
34124             c.remove();
34125         }
34126     },
34127
34128     // private
34129     processEvent : function(name, e){
34130         this.fireEvent(name, e);
34131         var t = e.getTarget();
34132         var v = this.view;
34133         var header = v.findHeaderIndex(t);
34134         if(header !== false){
34135             this.fireEvent("header" + name, this, header, e);
34136         }else{
34137             var row = v.findRowIndex(t);
34138             var cell = v.findCellIndex(t);
34139             if(row !== false){
34140                 this.fireEvent("row" + name, this, row, e);
34141                 if(cell !== false){
34142                     this.fireEvent("cell" + name, this, row, cell, e);
34143                 }
34144             }
34145         }
34146     },
34147
34148     // private
34149     onClick : function(e){
34150         this.processEvent("click", e);
34151     },
34152
34153     // private
34154     onContextMenu : function(e, t){
34155         this.processEvent("contextmenu", e);
34156     },
34157
34158     // private
34159     onDblClick : function(e){
34160         this.processEvent("dblclick", e);
34161     },
34162
34163     // private
34164     walkCells : function(row, col, step, fn, scope){
34165         var cm = this.colModel, clen = cm.getColumnCount();
34166         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34167         if(step < 0){
34168             if(col < 0){
34169                 row--;
34170                 first = false;
34171             }
34172             while(row >= 0){
34173                 if(!first){
34174                     col = clen-1;
34175                 }
34176                 first = false;
34177                 while(col >= 0){
34178                     if(fn.call(scope || this, row, col, cm) === true){
34179                         return [row, col];
34180                     }
34181                     col--;
34182                 }
34183                 row--;
34184             }
34185         } else {
34186             if(col >= clen){
34187                 row++;
34188                 first = false;
34189             }
34190             while(row < rlen){
34191                 if(!first){
34192                     col = 0;
34193                 }
34194                 first = false;
34195                 while(col < clen){
34196                     if(fn.call(scope || this, row, col, cm) === true){
34197                         return [row, col];
34198                     }
34199                     col++;
34200                 }
34201                 row++;
34202             }
34203         }
34204         return null;
34205     },
34206
34207     // private
34208     getSelections : function(){
34209         return this.selModel.getSelections();
34210     },
34211
34212     /**
34213      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34214      * but if manual update is required this method will initiate it.
34215      */
34216     autoSize : function(){
34217         if(this.rendered){
34218             this.view.layout();
34219             if(this.view.adjustForScroll){
34220                 this.view.adjustForScroll();
34221             }
34222         }
34223     },
34224
34225     /**
34226      * Returns the grid's underlying element.
34227      * @return {Element} The element
34228      */
34229     getGridEl : function(){
34230         return this.container;
34231     },
34232
34233     // private for compatibility, overridden by editor grid
34234     stopEditing : function(){},
34235
34236     /**
34237      * Returns the grid's SelectionModel.
34238      * @return {SelectionModel}
34239      */
34240     getSelectionModel : function(){
34241         if(!this.selModel){
34242             this.selModel = new Roo.grid.RowSelectionModel();
34243         }
34244         return this.selModel;
34245     },
34246
34247     /**
34248      * Returns the grid's DataSource.
34249      * @return {DataSource}
34250      */
34251     getDataSource : function(){
34252         return this.dataSource;
34253     },
34254
34255     /**
34256      * Returns the grid's ColumnModel.
34257      * @return {ColumnModel}
34258      */
34259     getColumnModel : function(){
34260         return this.colModel;
34261     },
34262
34263     /**
34264      * Returns the grid's GridView object.
34265      * @return {GridView}
34266      */
34267     getView : function(){
34268         if(!this.view){
34269             this.view = new Roo.grid.GridView(this.viewConfig);
34270         }
34271         return this.view;
34272     },
34273     /**
34274      * Called to get grid's drag proxy text, by default returns this.ddText.
34275      * @return {String}
34276      */
34277     getDragDropText : function(){
34278         var count = this.selModel.getCount();
34279         return String.format(this.ddText, count, count == 1 ? '' : 's');
34280     }
34281 });
34282 /**
34283  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34284  * %0 is replaced with the number of selected rows.
34285  * @type String
34286  */
34287 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34288  * Based on:
34289  * Ext JS Library 1.1.1
34290  * Copyright(c) 2006-2007, Ext JS, LLC.
34291  *
34292  * Originally Released Under LGPL - original licence link has changed is not relivant.
34293  *
34294  * Fork - LGPL
34295  * <script type="text/javascript">
34296  */
34297  
34298 Roo.grid.AbstractGridView = function(){
34299         this.grid = null;
34300         
34301         this.events = {
34302             "beforerowremoved" : true,
34303             "beforerowsinserted" : true,
34304             "beforerefresh" : true,
34305             "rowremoved" : true,
34306             "rowsinserted" : true,
34307             "rowupdated" : true,
34308             "refresh" : true
34309         };
34310     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34311 };
34312
34313 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34314     rowClass : "x-grid-row",
34315     cellClass : "x-grid-cell",
34316     tdClass : "x-grid-td",
34317     hdClass : "x-grid-hd",
34318     splitClass : "x-grid-hd-split",
34319     
34320         init: function(grid){
34321         this.grid = grid;
34322                 var cid = this.grid.getGridEl().id;
34323         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34324         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34325         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34326         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34327         },
34328         
34329         getColumnRenderers : function(){
34330         var renderers = [];
34331         var cm = this.grid.colModel;
34332         var colCount = cm.getColumnCount();
34333         for(var i = 0; i < colCount; i++){
34334             renderers[i] = cm.getRenderer(i);
34335         }
34336         return renderers;
34337     },
34338     
34339     getColumnIds : function(){
34340         var ids = [];
34341         var cm = this.grid.colModel;
34342         var colCount = cm.getColumnCount();
34343         for(var i = 0; i < colCount; i++){
34344             ids[i] = cm.getColumnId(i);
34345         }
34346         return ids;
34347     },
34348     
34349     getDataIndexes : function(){
34350         if(!this.indexMap){
34351             this.indexMap = this.buildIndexMap();
34352         }
34353         return this.indexMap.colToData;
34354     },
34355     
34356     getColumnIndexByDataIndex : function(dataIndex){
34357         if(!this.indexMap){
34358             this.indexMap = this.buildIndexMap();
34359         }
34360         return this.indexMap.dataToCol[dataIndex];
34361     },
34362     
34363     /**
34364      * Set a css style for a column dynamically. 
34365      * @param {Number} colIndex The index of the column
34366      * @param {String} name The css property name
34367      * @param {String} value The css value
34368      */
34369     setCSSStyle : function(colIndex, name, value){
34370         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34371         Roo.util.CSS.updateRule(selector, name, value);
34372     },
34373     
34374     generateRules : function(cm){
34375         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34376         Roo.util.CSS.removeStyleSheet(rulesId);
34377         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34378             var cid = cm.getColumnId(i);
34379             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34380                          this.tdSelector, cid, " {\n}\n",
34381                          this.hdSelector, cid, " {\n}\n",
34382                          this.splitSelector, cid, " {\n}\n");
34383         }
34384         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34385     }
34386 });/*
34387  * Based on:
34388  * Ext JS Library 1.1.1
34389  * Copyright(c) 2006-2007, Ext JS, LLC.
34390  *
34391  * Originally Released Under LGPL - original licence link has changed is not relivant.
34392  *
34393  * Fork - LGPL
34394  * <script type="text/javascript">
34395  */
34396
34397 // private
34398 // This is a support class used internally by the Grid components
34399 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34400     this.grid = grid;
34401     this.view = grid.getView();
34402     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34403     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34404     if(hd2){
34405         this.setHandleElId(Roo.id(hd));
34406         this.setOuterHandleElId(Roo.id(hd2));
34407     }
34408     this.scroll = false;
34409 };
34410 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34411     maxDragWidth: 120,
34412     getDragData : function(e){
34413         var t = Roo.lib.Event.getTarget(e);
34414         var h = this.view.findHeaderCell(t);
34415         if(h){
34416             return {ddel: h.firstChild, header:h};
34417         }
34418         return false;
34419     },
34420
34421     onInitDrag : function(e){
34422         this.view.headersDisabled = true;
34423         var clone = this.dragData.ddel.cloneNode(true);
34424         clone.id = Roo.id();
34425         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34426         this.proxy.update(clone);
34427         return true;
34428     },
34429
34430     afterValidDrop : function(){
34431         var v = this.view;
34432         setTimeout(function(){
34433             v.headersDisabled = false;
34434         }, 50);
34435     },
34436
34437     afterInvalidDrop : function(){
34438         var v = this.view;
34439         setTimeout(function(){
34440             v.headersDisabled = false;
34441         }, 50);
34442     }
34443 });
34444 /*
34445  * Based on:
34446  * Ext JS Library 1.1.1
34447  * Copyright(c) 2006-2007, Ext JS, LLC.
34448  *
34449  * Originally Released Under LGPL - original licence link has changed is not relivant.
34450  *
34451  * Fork - LGPL
34452  * <script type="text/javascript">
34453  */
34454 // private
34455 // This is a support class used internally by the Grid components
34456 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34457     this.grid = grid;
34458     this.view = grid.getView();
34459     // split the proxies so they don't interfere with mouse events
34460     this.proxyTop = Roo.DomHelper.append(document.body, {
34461         cls:"col-move-top", html:"&#160;"
34462     }, true);
34463     this.proxyBottom = Roo.DomHelper.append(document.body, {
34464         cls:"col-move-bottom", html:"&#160;"
34465     }, true);
34466     this.proxyTop.hide = this.proxyBottom.hide = function(){
34467         this.setLeftTop(-100,-100);
34468         this.setStyle("visibility", "hidden");
34469     };
34470     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34471     // temporarily disabled
34472     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34473     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34474 };
34475 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34476     proxyOffsets : [-4, -9],
34477     fly: Roo.Element.fly,
34478
34479     getTargetFromEvent : function(e){
34480         var t = Roo.lib.Event.getTarget(e);
34481         var cindex = this.view.findCellIndex(t);
34482         if(cindex !== false){
34483             return this.view.getHeaderCell(cindex);
34484         }
34485         return null;
34486     },
34487
34488     nextVisible : function(h){
34489         var v = this.view, cm = this.grid.colModel;
34490         h = h.nextSibling;
34491         while(h){
34492             if(!cm.isHidden(v.getCellIndex(h))){
34493                 return h;
34494             }
34495             h = h.nextSibling;
34496         }
34497         return null;
34498     },
34499
34500     prevVisible : function(h){
34501         var v = this.view, cm = this.grid.colModel;
34502         h = h.prevSibling;
34503         while(h){
34504             if(!cm.isHidden(v.getCellIndex(h))){
34505                 return h;
34506             }
34507             h = h.prevSibling;
34508         }
34509         return null;
34510     },
34511
34512     positionIndicator : function(h, n, e){
34513         var x = Roo.lib.Event.getPageX(e);
34514         var r = Roo.lib.Dom.getRegion(n.firstChild);
34515         var px, pt, py = r.top + this.proxyOffsets[1];
34516         if((r.right - x) <= (r.right-r.left)/2){
34517             px = r.right+this.view.borderWidth;
34518             pt = "after";
34519         }else{
34520             px = r.left;
34521             pt = "before";
34522         }
34523         var oldIndex = this.view.getCellIndex(h);
34524         var newIndex = this.view.getCellIndex(n);
34525
34526         if(this.grid.colModel.isFixed(newIndex)){
34527             return false;
34528         }
34529
34530         var locked = this.grid.colModel.isLocked(newIndex);
34531
34532         if(pt == "after"){
34533             newIndex++;
34534         }
34535         if(oldIndex < newIndex){
34536             newIndex--;
34537         }
34538         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34539             return false;
34540         }
34541         px +=  this.proxyOffsets[0];
34542         this.proxyTop.setLeftTop(px, py);
34543         this.proxyTop.show();
34544         if(!this.bottomOffset){
34545             this.bottomOffset = this.view.mainHd.getHeight();
34546         }
34547         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34548         this.proxyBottom.show();
34549         return pt;
34550     },
34551
34552     onNodeEnter : function(n, dd, e, data){
34553         if(data.header != n){
34554             this.positionIndicator(data.header, n, e);
34555         }
34556     },
34557
34558     onNodeOver : function(n, dd, e, data){
34559         var result = false;
34560         if(data.header != n){
34561             result = this.positionIndicator(data.header, n, e);
34562         }
34563         if(!result){
34564             this.proxyTop.hide();
34565             this.proxyBottom.hide();
34566         }
34567         return result ? this.dropAllowed : this.dropNotAllowed;
34568     },
34569
34570     onNodeOut : function(n, dd, e, data){
34571         this.proxyTop.hide();
34572         this.proxyBottom.hide();
34573     },
34574
34575     onNodeDrop : function(n, dd, e, data){
34576         var h = data.header;
34577         if(h != n){
34578             var cm = this.grid.colModel;
34579             var x = Roo.lib.Event.getPageX(e);
34580             var r = Roo.lib.Dom.getRegion(n.firstChild);
34581             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34582             var oldIndex = this.view.getCellIndex(h);
34583             var newIndex = this.view.getCellIndex(n);
34584             var locked = cm.isLocked(newIndex);
34585             if(pt == "after"){
34586                 newIndex++;
34587             }
34588             if(oldIndex < newIndex){
34589                 newIndex--;
34590             }
34591             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34592                 return false;
34593             }
34594             cm.setLocked(oldIndex, locked, true);
34595             cm.moveColumn(oldIndex, newIndex);
34596             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34597             return true;
34598         }
34599         return false;
34600     }
34601 });
34602 /*
34603  * Based on:
34604  * Ext JS Library 1.1.1
34605  * Copyright(c) 2006-2007, Ext JS, LLC.
34606  *
34607  * Originally Released Under LGPL - original licence link has changed is not relivant.
34608  *
34609  * Fork - LGPL
34610  * <script type="text/javascript">
34611  */
34612   
34613 /**
34614  * @class Roo.grid.GridView
34615  * @extends Roo.util.Observable
34616  *
34617  * @constructor
34618  * @param {Object} config
34619  */
34620 Roo.grid.GridView = function(config){
34621     Roo.grid.GridView.superclass.constructor.call(this);
34622     this.el = null;
34623
34624     Roo.apply(this, config);
34625 };
34626
34627 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34628
34629     
34630     rowClass : "x-grid-row",
34631
34632     cellClass : "x-grid-col",
34633
34634     tdClass : "x-grid-td",
34635
34636     hdClass : "x-grid-hd",
34637
34638     splitClass : "x-grid-split",
34639
34640     sortClasses : ["sort-asc", "sort-desc"],
34641
34642     enableMoveAnim : false,
34643
34644     hlColor: "C3DAF9",
34645
34646     dh : Roo.DomHelper,
34647
34648     fly : Roo.Element.fly,
34649
34650     css : Roo.util.CSS,
34651
34652     borderWidth: 1,
34653
34654     splitOffset: 3,
34655
34656     scrollIncrement : 22,
34657
34658     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34659
34660     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34661
34662     bind : function(ds, cm){
34663         if(this.ds){
34664             this.ds.un("load", this.onLoad, this);
34665             this.ds.un("datachanged", this.onDataChange, this);
34666             this.ds.un("add", this.onAdd, this);
34667             this.ds.un("remove", this.onRemove, this);
34668             this.ds.un("update", this.onUpdate, this);
34669             this.ds.un("clear", this.onClear, this);
34670         }
34671         if(ds){
34672             ds.on("load", this.onLoad, this);
34673             ds.on("datachanged", this.onDataChange, this);
34674             ds.on("add", this.onAdd, this);
34675             ds.on("remove", this.onRemove, this);
34676             ds.on("update", this.onUpdate, this);
34677             ds.on("clear", this.onClear, this);
34678         }
34679         this.ds = ds;
34680
34681         if(this.cm){
34682             this.cm.un("widthchange", this.onColWidthChange, this);
34683             this.cm.un("headerchange", this.onHeaderChange, this);
34684             this.cm.un("hiddenchange", this.onHiddenChange, this);
34685             this.cm.un("columnmoved", this.onColumnMove, this);
34686             this.cm.un("columnlockchange", this.onColumnLock, this);
34687         }
34688         if(cm){
34689             this.generateRules(cm);
34690             cm.on("widthchange", this.onColWidthChange, this);
34691             cm.on("headerchange", this.onHeaderChange, this);
34692             cm.on("hiddenchange", this.onHiddenChange, this);
34693             cm.on("columnmoved", this.onColumnMove, this);
34694             cm.on("columnlockchange", this.onColumnLock, this);
34695         }
34696         this.cm = cm;
34697     },
34698
34699     init: function(grid){
34700         Roo.grid.GridView.superclass.init.call(this, grid);
34701
34702         this.bind(grid.dataSource, grid.colModel);
34703
34704         grid.on("headerclick", this.handleHeaderClick, this);
34705
34706         if(grid.trackMouseOver){
34707             grid.on("mouseover", this.onRowOver, this);
34708             grid.on("mouseout", this.onRowOut, this);
34709         }
34710         grid.cancelTextSelection = function(){};
34711         this.gridId = grid.id;
34712
34713         var tpls = this.templates || {};
34714
34715         if(!tpls.master){
34716             tpls.master = new Roo.Template(
34717                '<div class="x-grid" hidefocus="true">',
34718                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
34719                   '<div class="x-grid-topbar"></div>',
34720                   '<div class="x-grid-scroller"><div></div></div>',
34721                   '<div class="x-grid-locked">',
34722                       '<div class="x-grid-header">{lockedHeader}</div>',
34723                       '<div class="x-grid-body">{lockedBody}</div>',
34724                   "</div>",
34725                   '<div class="x-grid-viewport">',
34726                       '<div class="x-grid-header">{header}</div>',
34727                       '<div class="x-grid-body">{body}</div>',
34728                   "</div>",
34729                   '<div class="x-grid-bottombar"></div>',
34730                  
34731                   '<div class="x-grid-resize-proxy">&#160;</div>',
34732                "</div>"
34733             );
34734             tpls.master.disableformats = true;
34735         }
34736
34737         if(!tpls.header){
34738             tpls.header = new Roo.Template(
34739                '<table border="0" cellspacing="0" cellpadding="0">',
34740                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
34741                "</table>{splits}"
34742             );
34743             tpls.header.disableformats = true;
34744         }
34745         tpls.header.compile();
34746
34747         if(!tpls.hcell){
34748             tpls.hcell = new Roo.Template(
34749                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
34750                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
34751                 "</div></td>"
34752              );
34753              tpls.hcell.disableFormats = true;
34754         }
34755         tpls.hcell.compile();
34756
34757         if(!tpls.hsplit){
34758             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
34759             tpls.hsplit.disableFormats = true;
34760         }
34761         tpls.hsplit.compile();
34762
34763         if(!tpls.body){
34764             tpls.body = new Roo.Template(
34765                '<table border="0" cellspacing="0" cellpadding="0">',
34766                "<tbody>{rows}</tbody>",
34767                "</table>"
34768             );
34769             tpls.body.disableFormats = true;
34770         }
34771         tpls.body.compile();
34772
34773         if(!tpls.row){
34774             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
34775             tpls.row.disableFormats = true;
34776         }
34777         tpls.row.compile();
34778
34779         if(!tpls.cell){
34780             tpls.cell = new Roo.Template(
34781                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
34782                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
34783                 "</td>"
34784             );
34785             tpls.cell.disableFormats = true;
34786         }
34787         tpls.cell.compile();
34788
34789         this.templates = tpls;
34790     },
34791
34792     // remap these for backwards compat
34793     onColWidthChange : function(){
34794         this.updateColumns.apply(this, arguments);
34795     },
34796     onHeaderChange : function(){
34797         this.updateHeaders.apply(this, arguments);
34798     }, 
34799     onHiddenChange : function(){
34800         this.handleHiddenChange.apply(this, arguments);
34801     },
34802     onColumnMove : function(){
34803         this.handleColumnMove.apply(this, arguments);
34804     },
34805     onColumnLock : function(){
34806         this.handleLockChange.apply(this, arguments);
34807     },
34808
34809     onDataChange : function(){
34810         this.refresh();
34811         this.updateHeaderSortState();
34812     },
34813
34814     onClear : function(){
34815         this.refresh();
34816     },
34817
34818     onUpdate : function(ds, record){
34819         this.refreshRow(record);
34820     },
34821
34822     refreshRow : function(record){
34823         var ds = this.ds, index;
34824         if(typeof record == 'number'){
34825             index = record;
34826             record = ds.getAt(index);
34827         }else{
34828             index = ds.indexOf(record);
34829         }
34830         this.insertRows(ds, index, index, true);
34831         this.onRemove(ds, record, index+1, true);
34832         this.syncRowHeights(index, index);
34833         this.layout();
34834         this.fireEvent("rowupdated", this, index, record);
34835     },
34836
34837     onAdd : function(ds, records, index){
34838         this.insertRows(ds, index, index + (records.length-1));
34839     },
34840
34841     onRemove : function(ds, record, index, isUpdate){
34842         if(isUpdate !== true){
34843             this.fireEvent("beforerowremoved", this, index, record);
34844         }
34845         var bt = this.getBodyTable(), lt = this.getLockedTable();
34846         if(bt.rows[index]){
34847             bt.firstChild.removeChild(bt.rows[index]);
34848         }
34849         if(lt.rows[index]){
34850             lt.firstChild.removeChild(lt.rows[index]);
34851         }
34852         if(isUpdate !== true){
34853             this.stripeRows(index);
34854             this.syncRowHeights(index, index);
34855             this.layout();
34856             this.fireEvent("rowremoved", this, index, record);
34857         }
34858     },
34859
34860     onLoad : function(){
34861         this.scrollToTop();
34862     },
34863
34864     /**
34865      * Scrolls the grid to the top
34866      */
34867     scrollToTop : function(){
34868         if(this.scroller){
34869             this.scroller.dom.scrollTop = 0;
34870             this.syncScroll();
34871         }
34872     },
34873
34874     /**
34875      * Gets a panel in the header of the grid that can be used for toolbars etc.
34876      * After modifying the contents of this panel a call to grid.autoSize() may be
34877      * required to register any changes in size.
34878      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
34879      * @return Roo.Element
34880      */
34881     getHeaderPanel : function(doShow){
34882         if(doShow){
34883             this.headerPanel.show();
34884         }
34885         return this.headerPanel;
34886     },
34887
34888     /**
34889      * Gets a panel in the footer of the grid that can be used for toolbars etc.
34890      * After modifying the contents of this panel a call to grid.autoSize() may be
34891      * required to register any changes in size.
34892      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
34893      * @return Roo.Element
34894      */
34895     getFooterPanel : function(doShow){
34896         if(doShow){
34897             this.footerPanel.show();
34898         }
34899         return this.footerPanel;
34900     },
34901
34902     initElements : function(){
34903         var E = Roo.Element;
34904         var el = this.grid.getGridEl().dom.firstChild;
34905         var cs = el.childNodes;
34906
34907         this.el = new E(el);
34908         
34909          this.focusEl = new E(el.firstChild);
34910         this.focusEl.swallowEvent("click", true);
34911         
34912         this.headerPanel = new E(cs[1]);
34913         this.headerPanel.enableDisplayMode("block");
34914
34915         this.scroller = new E(cs[2]);
34916         this.scrollSizer = new E(this.scroller.dom.firstChild);
34917
34918         this.lockedWrap = new E(cs[3]);
34919         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
34920         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
34921
34922         this.mainWrap = new E(cs[4]);
34923         this.mainHd = new E(this.mainWrap.dom.firstChild);
34924         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
34925
34926         this.footerPanel = new E(cs[5]);
34927         this.footerPanel.enableDisplayMode("block");
34928
34929         this.resizeProxy = new E(cs[6]);
34930
34931         this.headerSelector = String.format(
34932            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
34933            this.lockedHd.id, this.mainHd.id
34934         );
34935
34936         this.splitterSelector = String.format(
34937            '#{0} div.x-grid-split, #{1} div.x-grid-split',
34938            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
34939         );
34940     },
34941     idToCssName : function(s)
34942     {
34943         return s.replace(/[^a-z0-9]+/ig, '-');
34944     },
34945
34946     getHeaderCell : function(index){
34947         return Roo.DomQuery.select(this.headerSelector)[index];
34948     },
34949
34950     getHeaderCellMeasure : function(index){
34951         return this.getHeaderCell(index).firstChild;
34952     },
34953
34954     getHeaderCellText : function(index){
34955         return this.getHeaderCell(index).firstChild.firstChild;
34956     },
34957
34958     getLockedTable : function(){
34959         return this.lockedBody.dom.firstChild;
34960     },
34961
34962     getBodyTable : function(){
34963         return this.mainBody.dom.firstChild;
34964     },
34965
34966     getLockedRow : function(index){
34967         return this.getLockedTable().rows[index];
34968     },
34969
34970     getRow : function(index){
34971         return this.getBodyTable().rows[index];
34972     },
34973
34974     getRowComposite : function(index){
34975         if(!this.rowEl){
34976             this.rowEl = new Roo.CompositeElementLite();
34977         }
34978         var els = [], lrow, mrow;
34979         if(lrow = this.getLockedRow(index)){
34980             els.push(lrow);
34981         }
34982         if(mrow = this.getRow(index)){
34983             els.push(mrow);
34984         }
34985         this.rowEl.elements = els;
34986         return this.rowEl;
34987     },
34988     /**
34989      * Gets the 'td' of the cell
34990      * 
34991      * @param {Integer} rowIndex row to select
34992      * @param {Integer} colIndex column to select
34993      * 
34994      * @return {Object} 
34995      */
34996     getCell : function(rowIndex, colIndex){
34997         var locked = this.cm.getLockedCount();
34998         var source;
34999         if(colIndex < locked){
35000             source = this.lockedBody.dom.firstChild;
35001         }else{
35002             source = this.mainBody.dom.firstChild;
35003             colIndex -= locked;
35004         }
35005         return source.rows[rowIndex].childNodes[colIndex];
35006     },
35007
35008     getCellText : function(rowIndex, colIndex){
35009         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35010     },
35011
35012     getCellBox : function(cell){
35013         var b = this.fly(cell).getBox();
35014         if(Roo.isOpera){ // opera fails to report the Y
35015             b.y = cell.offsetTop + this.mainBody.getY();
35016         }
35017         return b;
35018     },
35019
35020     getCellIndex : function(cell){
35021         var id = String(cell.className).match(this.cellRE);
35022         if(id){
35023             return parseInt(id[1], 10);
35024         }
35025         return 0;
35026     },
35027
35028     findHeaderIndex : function(n){
35029         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35030         return r ? this.getCellIndex(r) : false;
35031     },
35032
35033     findHeaderCell : function(n){
35034         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35035         return r ? r : false;
35036     },
35037
35038     findRowIndex : function(n){
35039         if(!n){
35040             return false;
35041         }
35042         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35043         return r ? r.rowIndex : false;
35044     },
35045
35046     findCellIndex : function(node){
35047         var stop = this.el.dom;
35048         while(node && node != stop){
35049             if(this.findRE.test(node.className)){
35050                 return this.getCellIndex(node);
35051             }
35052             node = node.parentNode;
35053         }
35054         return false;
35055     },
35056
35057     getColumnId : function(index){
35058         return this.cm.getColumnId(index);
35059     },
35060
35061     getSplitters : function()
35062     {
35063         if(this.splitterSelector){
35064            return Roo.DomQuery.select(this.splitterSelector);
35065         }else{
35066             return null;
35067       }
35068     },
35069
35070     getSplitter : function(index){
35071         return this.getSplitters()[index];
35072     },
35073
35074     onRowOver : function(e, t){
35075         var row;
35076         if((row = this.findRowIndex(t)) !== false){
35077             this.getRowComposite(row).addClass("x-grid-row-over");
35078         }
35079     },
35080
35081     onRowOut : function(e, t){
35082         var row;
35083         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35084             this.getRowComposite(row).removeClass("x-grid-row-over");
35085         }
35086     },
35087
35088     renderHeaders : function(){
35089         var cm = this.cm;
35090         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35091         var cb = [], lb = [], sb = [], lsb = [], p = {};
35092         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35093             p.cellId = "x-grid-hd-0-" + i;
35094             p.splitId = "x-grid-csplit-0-" + i;
35095             p.id = cm.getColumnId(i);
35096             p.title = cm.getColumnTooltip(i) || "";
35097             p.value = cm.getColumnHeader(i) || "";
35098             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35099             if(!cm.isLocked(i)){
35100                 cb[cb.length] = ct.apply(p);
35101                 sb[sb.length] = st.apply(p);
35102             }else{
35103                 lb[lb.length] = ct.apply(p);
35104                 lsb[lsb.length] = st.apply(p);
35105             }
35106         }
35107         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35108                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35109     },
35110
35111     updateHeaders : function(){
35112         var html = this.renderHeaders();
35113         this.lockedHd.update(html[0]);
35114         this.mainHd.update(html[1]);
35115     },
35116
35117     /**
35118      * Focuses the specified row.
35119      * @param {Number} row The row index
35120      */
35121     focusRow : function(row)
35122     {
35123         //Roo.log('GridView.focusRow');
35124         var x = this.scroller.dom.scrollLeft;
35125         this.focusCell(row, 0, false);
35126         this.scroller.dom.scrollLeft = x;
35127     },
35128
35129     /**
35130      * Focuses the specified cell.
35131      * @param {Number} row The row index
35132      * @param {Number} col The column index
35133      * @param {Boolean} hscroll false to disable horizontal scrolling
35134      */
35135     focusCell : function(row, col, hscroll)
35136     {
35137         //Roo.log('GridView.focusCell');
35138         var el = this.ensureVisible(row, col, hscroll);
35139         this.focusEl.alignTo(el, "tl-tl");
35140         if(Roo.isGecko){
35141             this.focusEl.focus();
35142         }else{
35143             this.focusEl.focus.defer(1, this.focusEl);
35144         }
35145     },
35146
35147     /**
35148      * Scrolls the specified cell into view
35149      * @param {Number} row The row index
35150      * @param {Number} col The column index
35151      * @param {Boolean} hscroll false to disable horizontal scrolling
35152      */
35153     ensureVisible : function(row, col, hscroll)
35154     {
35155         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35156         //return null; //disable for testing.
35157         if(typeof row != "number"){
35158             row = row.rowIndex;
35159         }
35160         if(row < 0 && row >= this.ds.getCount()){
35161             return  null;
35162         }
35163         col = (col !== undefined ? col : 0);
35164         var cm = this.grid.colModel;
35165         while(cm.isHidden(col)){
35166             col++;
35167         }
35168
35169         var el = this.getCell(row, col);
35170         if(!el){
35171             return null;
35172         }
35173         var c = this.scroller.dom;
35174
35175         var ctop = parseInt(el.offsetTop, 10);
35176         var cleft = parseInt(el.offsetLeft, 10);
35177         var cbot = ctop + el.offsetHeight;
35178         var cright = cleft + el.offsetWidth;
35179         
35180         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35181         var stop = parseInt(c.scrollTop, 10);
35182         var sleft = parseInt(c.scrollLeft, 10);
35183         var sbot = stop + ch;
35184         var sright = sleft + c.clientWidth;
35185         /*
35186         Roo.log('GridView.ensureVisible:' +
35187                 ' ctop:' + ctop +
35188                 ' c.clientHeight:' + c.clientHeight +
35189                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35190                 ' stop:' + stop +
35191                 ' cbot:' + cbot +
35192                 ' sbot:' + sbot +
35193                 ' ch:' + ch  
35194                 );
35195         */
35196         if(ctop < stop){
35197              c.scrollTop = ctop;
35198             //Roo.log("set scrolltop to ctop DISABLE?");
35199         }else if(cbot > sbot){
35200             //Roo.log("set scrolltop to cbot-ch");
35201             c.scrollTop = cbot-ch;
35202         }
35203         
35204         if(hscroll !== false){
35205             if(cleft < sleft){
35206                 c.scrollLeft = cleft;
35207             }else if(cright > sright){
35208                 c.scrollLeft = cright-c.clientWidth;
35209             }
35210         }
35211          
35212         return el;
35213     },
35214
35215     updateColumns : function(){
35216         this.grid.stopEditing();
35217         var cm = this.grid.colModel, colIds = this.getColumnIds();
35218         //var totalWidth = cm.getTotalWidth();
35219         var pos = 0;
35220         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35221             //if(cm.isHidden(i)) continue;
35222             var w = cm.getColumnWidth(i);
35223             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35224             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35225         }
35226         this.updateSplitters();
35227     },
35228
35229     generateRules : function(cm){
35230         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35231         Roo.util.CSS.removeStyleSheet(rulesId);
35232         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35233             var cid = cm.getColumnId(i);
35234             var align = '';
35235             if(cm.config[i].align){
35236                 align = 'text-align:'+cm.config[i].align+';';
35237             }
35238             var hidden = '';
35239             if(cm.isHidden(i)){
35240                 hidden = 'display:none;';
35241             }
35242             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35243             ruleBuf.push(
35244                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35245                     this.hdSelector, cid, " {\n", align, width, "}\n",
35246                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35247                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35248         }
35249         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35250     },
35251
35252     updateSplitters : function(){
35253         var cm = this.cm, s = this.getSplitters();
35254         if(s){ // splitters not created yet
35255             var pos = 0, locked = true;
35256             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35257                 if(cm.isHidden(i)) continue;
35258                 var w = cm.getColumnWidth(i); // make sure it's a number
35259                 if(!cm.isLocked(i) && locked){
35260                     pos = 0;
35261                     locked = false;
35262                 }
35263                 pos += w;
35264                 s[i].style.left = (pos-this.splitOffset) + "px";
35265             }
35266         }
35267     },
35268
35269     handleHiddenChange : function(colModel, colIndex, hidden){
35270         if(hidden){
35271             this.hideColumn(colIndex);
35272         }else{
35273             this.unhideColumn(colIndex);
35274         }
35275     },
35276
35277     hideColumn : function(colIndex){
35278         var cid = this.getColumnId(colIndex);
35279         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35280         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35281         if(Roo.isSafari){
35282             this.updateHeaders();
35283         }
35284         this.updateSplitters();
35285         this.layout();
35286     },
35287
35288     unhideColumn : function(colIndex){
35289         var cid = this.getColumnId(colIndex);
35290         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35291         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35292
35293         if(Roo.isSafari){
35294             this.updateHeaders();
35295         }
35296         this.updateSplitters();
35297         this.layout();
35298     },
35299
35300     insertRows : function(dm, firstRow, lastRow, isUpdate){
35301         if(firstRow == 0 && lastRow == dm.getCount()-1){
35302             this.refresh();
35303         }else{
35304             if(!isUpdate){
35305                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35306             }
35307             var s = this.getScrollState();
35308             var markup = this.renderRows(firstRow, lastRow);
35309             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35310             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35311             this.restoreScroll(s);
35312             if(!isUpdate){
35313                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35314                 this.syncRowHeights(firstRow, lastRow);
35315                 this.stripeRows(firstRow);
35316                 this.layout();
35317             }
35318         }
35319     },
35320
35321     bufferRows : function(markup, target, index){
35322         var before = null, trows = target.rows, tbody = target.tBodies[0];
35323         if(index < trows.length){
35324             before = trows[index];
35325         }
35326         var b = document.createElement("div");
35327         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35328         var rows = b.firstChild.rows;
35329         for(var i = 0, len = rows.length; i < len; i++){
35330             if(before){
35331                 tbody.insertBefore(rows[0], before);
35332             }else{
35333                 tbody.appendChild(rows[0]);
35334             }
35335         }
35336         b.innerHTML = "";
35337         b = null;
35338     },
35339
35340     deleteRows : function(dm, firstRow, lastRow){
35341         if(dm.getRowCount()<1){
35342             this.fireEvent("beforerefresh", this);
35343             this.mainBody.update("");
35344             this.lockedBody.update("");
35345             this.fireEvent("refresh", this);
35346         }else{
35347             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35348             var bt = this.getBodyTable();
35349             var tbody = bt.firstChild;
35350             var rows = bt.rows;
35351             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35352                 tbody.removeChild(rows[firstRow]);
35353             }
35354             this.stripeRows(firstRow);
35355             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35356         }
35357     },
35358
35359     updateRows : function(dataSource, firstRow, lastRow){
35360         var s = this.getScrollState();
35361         this.refresh();
35362         this.restoreScroll(s);
35363     },
35364
35365     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35366         if(!noRefresh){
35367            this.refresh();
35368         }
35369         this.updateHeaderSortState();
35370     },
35371
35372     getScrollState : function(){
35373         
35374         var sb = this.scroller.dom;
35375         return {left: sb.scrollLeft, top: sb.scrollTop};
35376     },
35377
35378     stripeRows : function(startRow){
35379         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35380             return;
35381         }
35382         startRow = startRow || 0;
35383         var rows = this.getBodyTable().rows;
35384         var lrows = this.getLockedTable().rows;
35385         var cls = ' x-grid-row-alt ';
35386         for(var i = startRow, len = rows.length; i < len; i++){
35387             var row = rows[i], lrow = lrows[i];
35388             var isAlt = ((i+1) % 2 == 0);
35389             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35390             if(isAlt == hasAlt){
35391                 continue;
35392             }
35393             if(isAlt){
35394                 row.className += " x-grid-row-alt";
35395             }else{
35396                 row.className = row.className.replace("x-grid-row-alt", "");
35397             }
35398             if(lrow){
35399                 lrow.className = row.className;
35400             }
35401         }
35402     },
35403
35404     restoreScroll : function(state){
35405         //Roo.log('GridView.restoreScroll');
35406         var sb = this.scroller.dom;
35407         sb.scrollLeft = state.left;
35408         sb.scrollTop = state.top;
35409         this.syncScroll();
35410     },
35411
35412     syncScroll : function(){
35413         //Roo.log('GridView.syncScroll');
35414         var sb = this.scroller.dom;
35415         var sh = this.mainHd.dom;
35416         var bs = this.mainBody.dom;
35417         var lv = this.lockedBody.dom;
35418         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35419         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35420     },
35421
35422     handleScroll : function(e){
35423         this.syncScroll();
35424         var sb = this.scroller.dom;
35425         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35426         e.stopEvent();
35427     },
35428
35429     handleWheel : function(e){
35430         var d = e.getWheelDelta();
35431         this.scroller.dom.scrollTop -= d*22;
35432         // set this here to prevent jumpy scrolling on large tables
35433         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35434         e.stopEvent();
35435     },
35436
35437     renderRows : function(startRow, endRow){
35438         // pull in all the crap needed to render rows
35439         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35440         var colCount = cm.getColumnCount();
35441
35442         if(ds.getCount() < 1){
35443             return ["", ""];
35444         }
35445
35446         // build a map for all the columns
35447         var cs = [];
35448         for(var i = 0; i < colCount; i++){
35449             var name = cm.getDataIndex(i);
35450             cs[i] = {
35451                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35452                 renderer : cm.getRenderer(i),
35453                 id : cm.getColumnId(i),
35454                 locked : cm.isLocked(i)
35455             };
35456         }
35457
35458         startRow = startRow || 0;
35459         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35460
35461         // records to render
35462         var rs = ds.getRange(startRow, endRow);
35463
35464         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35465     },
35466
35467     // As much as I hate to duplicate code, this was branched because FireFox really hates
35468     // [].join("") on strings. The performance difference was substantial enough to
35469     // branch this function
35470     doRender : Roo.isGecko ?
35471             function(cs, rs, ds, startRow, colCount, stripe){
35472                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35473                 // buffers
35474                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35475                 
35476                 var hasListener = this.grid.hasListener('rowclass');
35477                 var rowcfg = {};
35478                 for(var j = 0, len = rs.length; j < len; j++){
35479                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35480                     for(var i = 0; i < colCount; i++){
35481                         c = cs[i];
35482                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35483                         p.id = c.id;
35484                         p.css = p.attr = "";
35485                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35486                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35487                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35488                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35489                         }
35490                         var markup = ct.apply(p);
35491                         if(!c.locked){
35492                             cb+= markup;
35493                         }else{
35494                             lcb+= markup;
35495                         }
35496                     }
35497                     var alt = [];
35498                     if(stripe && ((rowIndex+1) % 2 == 0)){
35499                         alt.push("x-grid-row-alt")
35500                     }
35501                     if(r.dirty){
35502                         alt.push(  " x-grid-dirty-row");
35503                     }
35504                     rp.cells = lcb;
35505                     if(this.getRowClass){
35506                         alt.push(this.getRowClass(r, rowIndex));
35507                     }
35508                     if (hasListener) {
35509                         rowcfg = {
35510                              
35511                             record: r,
35512                             rowIndex : rowIndex,
35513                             rowClass : ''
35514                         }
35515                         this.grid.fireEvent('rowclass', this, rowcfg);
35516                         alt.push(rowcfg.rowClass);
35517                     }
35518                     rp.alt = alt.join(" ");
35519                     lbuf+= rt.apply(rp);
35520                     rp.cells = cb;
35521                     buf+=  rt.apply(rp);
35522                 }
35523                 return [lbuf, buf];
35524             } :
35525             function(cs, rs, ds, startRow, colCount, stripe){
35526                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35527                 // buffers
35528                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35529                 var hasListener = this.grid.hasListener('rowclass');
35530  
35531                 var rowcfg = {};
35532                 for(var j = 0, len = rs.length; j < len; j++){
35533                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35534                     for(var i = 0; i < colCount; i++){
35535                         c = cs[i];
35536                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35537                         p.id = c.id;
35538                         p.css = p.attr = "";
35539                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35540                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35541                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35542                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35543                         }
35544                         
35545                         var markup = ct.apply(p);
35546                         if(!c.locked){
35547                             cb[cb.length] = markup;
35548                         }else{
35549                             lcb[lcb.length] = markup;
35550                         }
35551                     }
35552                     var alt = [];
35553                     if(stripe && ((rowIndex+1) % 2 == 0)){
35554                         alt.push( "x-grid-row-alt");
35555                     }
35556                     if(r.dirty){
35557                         alt.push(" x-grid-dirty-row");
35558                     }
35559                     rp.cells = lcb;
35560                     if(this.getRowClass){
35561                         alt.push( this.getRowClass(r, rowIndex));
35562                     }
35563                     if (hasListener) {
35564                         rowcfg = {
35565                              
35566                             record: r,
35567                             rowIndex : rowIndex,
35568                             rowClass : ''
35569                         }
35570                         this.grid.fireEvent('rowclass', this, rowcfg);
35571                         alt.push(rowcfg.rowClass);
35572                     }
35573                     rp.alt = alt.join(" ");
35574                     rp.cells = lcb.join("");
35575                     lbuf[lbuf.length] = rt.apply(rp);
35576                     rp.cells = cb.join("");
35577                     buf[buf.length] =  rt.apply(rp);
35578                 }
35579                 return [lbuf.join(""), buf.join("")];
35580             },
35581
35582     renderBody : function(){
35583         var markup = this.renderRows();
35584         var bt = this.templates.body;
35585         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35586     },
35587
35588     /**
35589      * Refreshes the grid
35590      * @param {Boolean} headersToo
35591      */
35592     refresh : function(headersToo){
35593         this.fireEvent("beforerefresh", this);
35594         this.grid.stopEditing();
35595         var result = this.renderBody();
35596         this.lockedBody.update(result[0]);
35597         this.mainBody.update(result[1]);
35598         if(headersToo === true){
35599             this.updateHeaders();
35600             this.updateColumns();
35601             this.updateSplitters();
35602             this.updateHeaderSortState();
35603         }
35604         this.syncRowHeights();
35605         this.layout();
35606         this.fireEvent("refresh", this);
35607     },
35608
35609     handleColumnMove : function(cm, oldIndex, newIndex){
35610         this.indexMap = null;
35611         var s = this.getScrollState();
35612         this.refresh(true);
35613         this.restoreScroll(s);
35614         this.afterMove(newIndex);
35615     },
35616
35617     afterMove : function(colIndex){
35618         if(this.enableMoveAnim && Roo.enableFx){
35619             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35620         }
35621         // if multisort - fix sortOrder, and reload..
35622         if (this.grid.dataSource.multiSort) {
35623             // the we can call sort again..
35624             var dm = this.grid.dataSource;
35625             var cm = this.grid.colModel;
35626             var so = [];
35627             for(var i = 0; i < cm.config.length; i++ ) {
35628                 
35629                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35630                     continue; // dont' bother, it's not in sort list or being set.
35631                 }
35632                 
35633                 so.push(cm.config[i].dataIndex);
35634             };
35635             dm.sortOrder = so;
35636             dm.load(dm.lastOptions);
35637             
35638             
35639         }
35640         
35641     },
35642
35643     updateCell : function(dm, rowIndex, dataIndex){
35644         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35645         if(typeof colIndex == "undefined"){ // not present in grid
35646             return;
35647         }
35648         var cm = this.grid.colModel;
35649         var cell = this.getCell(rowIndex, colIndex);
35650         var cellText = this.getCellText(rowIndex, colIndex);
35651
35652         var p = {
35653             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35654             id : cm.getColumnId(colIndex),
35655             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35656         };
35657         var renderer = cm.getRenderer(colIndex);
35658         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35659         if(typeof val == "undefined" || val === "") val = "&#160;";
35660         cellText.innerHTML = val;
35661         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35662         this.syncRowHeights(rowIndex, rowIndex);
35663     },
35664
35665     calcColumnWidth : function(colIndex, maxRowsToMeasure){
35666         var maxWidth = 0;
35667         if(this.grid.autoSizeHeaders){
35668             var h = this.getHeaderCellMeasure(colIndex);
35669             maxWidth = Math.max(maxWidth, h.scrollWidth);
35670         }
35671         var tb, index;
35672         if(this.cm.isLocked(colIndex)){
35673             tb = this.getLockedTable();
35674             index = colIndex;
35675         }else{
35676             tb = this.getBodyTable();
35677             index = colIndex - this.cm.getLockedCount();
35678         }
35679         if(tb && tb.rows){
35680             var rows = tb.rows;
35681             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
35682             for(var i = 0; i < stopIndex; i++){
35683                 var cell = rows[i].childNodes[index].firstChild;
35684                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
35685             }
35686         }
35687         return maxWidth + /*margin for error in IE*/ 5;
35688     },
35689     /**
35690      * Autofit a column to its content.
35691      * @param {Number} colIndex
35692      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
35693      */
35694      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
35695          if(this.cm.isHidden(colIndex)){
35696              return; // can't calc a hidden column
35697          }
35698         if(forceMinSize){
35699             var cid = this.cm.getColumnId(colIndex);
35700             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
35701            if(this.grid.autoSizeHeaders){
35702                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
35703            }
35704         }
35705         var newWidth = this.calcColumnWidth(colIndex);
35706         this.cm.setColumnWidth(colIndex,
35707             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
35708         if(!suppressEvent){
35709             this.grid.fireEvent("columnresize", colIndex, newWidth);
35710         }
35711     },
35712
35713     /**
35714      * Autofits all columns to their content and then expands to fit any extra space in the grid
35715      */
35716      autoSizeColumns : function(){
35717         var cm = this.grid.colModel;
35718         var colCount = cm.getColumnCount();
35719         for(var i = 0; i < colCount; i++){
35720             this.autoSizeColumn(i, true, true);
35721         }
35722         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
35723             this.fitColumns();
35724         }else{
35725             this.updateColumns();
35726             this.layout();
35727         }
35728     },
35729
35730     /**
35731      * Autofits all columns to the grid's width proportionate with their current size
35732      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
35733      */
35734     fitColumns : function(reserveScrollSpace){
35735         var cm = this.grid.colModel;
35736         var colCount = cm.getColumnCount();
35737         var cols = [];
35738         var width = 0;
35739         var i, w;
35740         for (i = 0; i < colCount; i++){
35741             if(!cm.isHidden(i) && !cm.isFixed(i)){
35742                 w = cm.getColumnWidth(i);
35743                 cols.push(i);
35744                 cols.push(w);
35745                 width += w;
35746             }
35747         }
35748         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
35749         if(reserveScrollSpace){
35750             avail -= 17;
35751         }
35752         var frac = (avail - cm.getTotalWidth())/width;
35753         while (cols.length){
35754             w = cols.pop();
35755             i = cols.pop();
35756             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
35757         }
35758         this.updateColumns();
35759         this.layout();
35760     },
35761
35762     onRowSelect : function(rowIndex){
35763         var row = this.getRowComposite(rowIndex);
35764         row.addClass("x-grid-row-selected");
35765     },
35766
35767     onRowDeselect : function(rowIndex){
35768         var row = this.getRowComposite(rowIndex);
35769         row.removeClass("x-grid-row-selected");
35770     },
35771
35772     onCellSelect : function(row, col){
35773         var cell = this.getCell(row, col);
35774         if(cell){
35775             Roo.fly(cell).addClass("x-grid-cell-selected");
35776         }
35777     },
35778
35779     onCellDeselect : function(row, col){
35780         var cell = this.getCell(row, col);
35781         if(cell){
35782             Roo.fly(cell).removeClass("x-grid-cell-selected");
35783         }
35784     },
35785
35786     updateHeaderSortState : function(){
35787         
35788         // sort state can be single { field: xxx, direction : yyy}
35789         // or   { xxx=>ASC , yyy : DESC ..... }
35790         
35791         var mstate = {};
35792         if (!this.ds.multiSort) { 
35793             var state = this.ds.getSortState();
35794             if(!state){
35795                 return;
35796             }
35797             mstate[state.field] = state.direction;
35798             // FIXME... - this is not used here.. but might be elsewhere..
35799             this.sortState = state;
35800             
35801         } else {
35802             mstate = this.ds.sortToggle;
35803         }
35804         //remove existing sort classes..
35805         
35806         var sc = this.sortClasses;
35807         var hds = this.el.select(this.headerSelector).removeClass(sc);
35808         
35809         for(var f in mstate) {
35810         
35811             var sortColumn = this.cm.findColumnIndex(f);
35812             
35813             if(sortColumn != -1){
35814                 var sortDir = mstate[f];        
35815                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
35816             }
35817         }
35818         
35819          
35820         
35821     },
35822
35823
35824     handleHeaderClick : function(g, index){
35825         if(this.headersDisabled){
35826             return;
35827         }
35828         var dm = g.dataSource, cm = g.colModel;
35829         if(!cm.isSortable(index)){
35830             return;
35831         }
35832         g.stopEditing();
35833         
35834         if (dm.multiSort) {
35835             // update the sortOrder
35836             var so = [];
35837             for(var i = 0; i < cm.config.length; i++ ) {
35838                 
35839                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
35840                     continue; // dont' bother, it's not in sort list or being set.
35841                 }
35842                 
35843                 so.push(cm.config[i].dataIndex);
35844             };
35845             dm.sortOrder = so;
35846         }
35847         
35848         
35849         dm.sort(cm.getDataIndex(index));
35850     },
35851
35852
35853     destroy : function(){
35854         if(this.colMenu){
35855             this.colMenu.removeAll();
35856             Roo.menu.MenuMgr.unregister(this.colMenu);
35857             this.colMenu.getEl().remove();
35858             delete this.colMenu;
35859         }
35860         if(this.hmenu){
35861             this.hmenu.removeAll();
35862             Roo.menu.MenuMgr.unregister(this.hmenu);
35863             this.hmenu.getEl().remove();
35864             delete this.hmenu;
35865         }
35866         if(this.grid.enableColumnMove){
35867             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35868             if(dds){
35869                 for(var dd in dds){
35870                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
35871                         var elid = dds[dd].dragElId;
35872                         dds[dd].unreg();
35873                         Roo.get(elid).remove();
35874                     } else if(dds[dd].config.isTarget){
35875                         dds[dd].proxyTop.remove();
35876                         dds[dd].proxyBottom.remove();
35877                         dds[dd].unreg();
35878                     }
35879                     if(Roo.dd.DDM.locationCache[dd]){
35880                         delete Roo.dd.DDM.locationCache[dd];
35881                     }
35882                 }
35883                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
35884             }
35885         }
35886         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
35887         this.bind(null, null);
35888         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
35889     },
35890
35891     handleLockChange : function(){
35892         this.refresh(true);
35893     },
35894
35895     onDenyColumnLock : function(){
35896
35897     },
35898
35899     onDenyColumnHide : function(){
35900
35901     },
35902
35903     handleHdMenuClick : function(item){
35904         var index = this.hdCtxIndex;
35905         var cm = this.cm, ds = this.ds;
35906         switch(item.id){
35907             case "asc":
35908                 ds.sort(cm.getDataIndex(index), "ASC");
35909                 break;
35910             case "desc":
35911                 ds.sort(cm.getDataIndex(index), "DESC");
35912                 break;
35913             case "lock":
35914                 var lc = cm.getLockedCount();
35915                 if(cm.getColumnCount(true) <= lc+1){
35916                     this.onDenyColumnLock();
35917                     return;
35918                 }
35919                 if(lc != index){
35920                     cm.setLocked(index, true, true);
35921                     cm.moveColumn(index, lc);
35922                     this.grid.fireEvent("columnmove", index, lc);
35923                 }else{
35924                     cm.setLocked(index, true);
35925                 }
35926             break;
35927             case "unlock":
35928                 var lc = cm.getLockedCount();
35929                 if((lc-1) != index){
35930                     cm.setLocked(index, false, true);
35931                     cm.moveColumn(index, lc-1);
35932                     this.grid.fireEvent("columnmove", index, lc-1);
35933                 }else{
35934                     cm.setLocked(index, false);
35935                 }
35936             break;
35937             default:
35938                 index = cm.getIndexById(item.id.substr(4));
35939                 if(index != -1){
35940                     if(item.checked && cm.getColumnCount(true) <= 1){
35941                         this.onDenyColumnHide();
35942                         return false;
35943                     }
35944                     cm.setHidden(index, item.checked);
35945                 }
35946         }
35947         return true;
35948     },
35949
35950     beforeColMenuShow : function(){
35951         var cm = this.cm,  colCount = cm.getColumnCount();
35952         this.colMenu.removeAll();
35953         for(var i = 0; i < colCount; i++){
35954             this.colMenu.add(new Roo.menu.CheckItem({
35955                 id: "col-"+cm.getColumnId(i),
35956                 text: cm.getColumnHeader(i),
35957                 checked: !cm.isHidden(i),
35958                 hideOnClick:false
35959             }));
35960         }
35961     },
35962
35963     handleHdCtx : function(g, index, e){
35964         e.stopEvent();
35965         var hd = this.getHeaderCell(index);
35966         this.hdCtxIndex = index;
35967         var ms = this.hmenu.items, cm = this.cm;
35968         ms.get("asc").setDisabled(!cm.isSortable(index));
35969         ms.get("desc").setDisabled(!cm.isSortable(index));
35970         if(this.grid.enableColLock !== false){
35971             ms.get("lock").setDisabled(cm.isLocked(index));
35972             ms.get("unlock").setDisabled(!cm.isLocked(index));
35973         }
35974         this.hmenu.show(hd, "tl-bl");
35975     },
35976
35977     handleHdOver : function(e){
35978         var hd = this.findHeaderCell(e.getTarget());
35979         if(hd && !this.headersDisabled){
35980             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
35981                this.fly(hd).addClass("x-grid-hd-over");
35982             }
35983         }
35984     },
35985
35986     handleHdOut : function(e){
35987         var hd = this.findHeaderCell(e.getTarget());
35988         if(hd){
35989             this.fly(hd).removeClass("x-grid-hd-over");
35990         }
35991     },
35992
35993     handleSplitDblClick : function(e, t){
35994         var i = this.getCellIndex(t);
35995         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
35996             this.autoSizeColumn(i, true);
35997             this.layout();
35998         }
35999     },
36000
36001     render : function(){
36002
36003         var cm = this.cm;
36004         var colCount = cm.getColumnCount();
36005
36006         if(this.grid.monitorWindowResize === true){
36007             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36008         }
36009         var header = this.renderHeaders();
36010         var body = this.templates.body.apply({rows:""});
36011         var html = this.templates.master.apply({
36012             lockedBody: body,
36013             body: body,
36014             lockedHeader: header[0],
36015             header: header[1]
36016         });
36017
36018         //this.updateColumns();
36019
36020         this.grid.getGridEl().dom.innerHTML = html;
36021
36022         this.initElements();
36023         
36024         // a kludge to fix the random scolling effect in webkit
36025         this.el.on("scroll", function() {
36026             this.el.dom.scrollTop=0; // hopefully not recursive..
36027         },this);
36028
36029         this.scroller.on("scroll", this.handleScroll, this);
36030         this.lockedBody.on("mousewheel", this.handleWheel, this);
36031         this.mainBody.on("mousewheel", this.handleWheel, this);
36032
36033         this.mainHd.on("mouseover", this.handleHdOver, this);
36034         this.mainHd.on("mouseout", this.handleHdOut, this);
36035         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36036                 {delegate: "."+this.splitClass});
36037
36038         this.lockedHd.on("mouseover", this.handleHdOver, this);
36039         this.lockedHd.on("mouseout", this.handleHdOut, this);
36040         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36041                 {delegate: "."+this.splitClass});
36042
36043         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36044             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36045         }
36046
36047         this.updateSplitters();
36048
36049         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36050             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36051             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36052         }
36053
36054         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36055             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36056             this.hmenu.add(
36057                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36058                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36059             );
36060             if(this.grid.enableColLock !== false){
36061                 this.hmenu.add('-',
36062                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36063                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36064                 );
36065             }
36066             if(this.grid.enableColumnHide !== false){
36067
36068                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36069                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36070                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36071
36072                 this.hmenu.add('-',
36073                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36074                 );
36075             }
36076             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36077
36078             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36079         }
36080
36081         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36082             this.dd = new Roo.grid.GridDragZone(this.grid, {
36083                 ddGroup : this.grid.ddGroup || 'GridDD'
36084             });
36085         }
36086
36087         /*
36088         for(var i = 0; i < colCount; i++){
36089             if(cm.isHidden(i)){
36090                 this.hideColumn(i);
36091             }
36092             if(cm.config[i].align){
36093                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36094                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36095             }
36096         }*/
36097         
36098         this.updateHeaderSortState();
36099
36100         this.beforeInitialResize();
36101         this.layout(true);
36102
36103         // two part rendering gives faster view to the user
36104         this.renderPhase2.defer(1, this);
36105     },
36106
36107     renderPhase2 : function(){
36108         // render the rows now
36109         this.refresh();
36110         if(this.grid.autoSizeColumns){
36111             this.autoSizeColumns();
36112         }
36113     },
36114
36115     beforeInitialResize : function(){
36116
36117     },
36118
36119     onColumnSplitterMoved : function(i, w){
36120         this.userResized = true;
36121         var cm = this.grid.colModel;
36122         cm.setColumnWidth(i, w, true);
36123         var cid = cm.getColumnId(i);
36124         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36125         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36126         this.updateSplitters();
36127         this.layout();
36128         this.grid.fireEvent("columnresize", i, w);
36129     },
36130
36131     syncRowHeights : function(startIndex, endIndex){
36132         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36133             startIndex = startIndex || 0;
36134             var mrows = this.getBodyTable().rows;
36135             var lrows = this.getLockedTable().rows;
36136             var len = mrows.length-1;
36137             endIndex = Math.min(endIndex || len, len);
36138             for(var i = startIndex; i <= endIndex; i++){
36139                 var m = mrows[i], l = lrows[i];
36140                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36141                 m.style.height = l.style.height = h + "px";
36142             }
36143         }
36144     },
36145
36146     layout : function(initialRender, is2ndPass){
36147         var g = this.grid;
36148         var auto = g.autoHeight;
36149         var scrollOffset = 16;
36150         var c = g.getGridEl(), cm = this.cm,
36151                 expandCol = g.autoExpandColumn,
36152                 gv = this;
36153         //c.beginMeasure();
36154
36155         if(!c.dom.offsetWidth){ // display:none?
36156             if(initialRender){
36157                 this.lockedWrap.show();
36158                 this.mainWrap.show();
36159             }
36160             return;
36161         }
36162
36163         var hasLock = this.cm.isLocked(0);
36164
36165         var tbh = this.headerPanel.getHeight();
36166         var bbh = this.footerPanel.getHeight();
36167
36168         if(auto){
36169             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36170             var newHeight = ch + c.getBorderWidth("tb");
36171             if(g.maxHeight){
36172                 newHeight = Math.min(g.maxHeight, newHeight);
36173             }
36174             c.setHeight(newHeight);
36175         }
36176
36177         if(g.autoWidth){
36178             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36179         }
36180
36181         var s = this.scroller;
36182
36183         var csize = c.getSize(true);
36184
36185         this.el.setSize(csize.width, csize.height);
36186
36187         this.headerPanel.setWidth(csize.width);
36188         this.footerPanel.setWidth(csize.width);
36189
36190         var hdHeight = this.mainHd.getHeight();
36191         var vw = csize.width;
36192         var vh = csize.height - (tbh + bbh);
36193
36194         s.setSize(vw, vh);
36195
36196         var bt = this.getBodyTable();
36197         var ltWidth = hasLock ?
36198                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36199
36200         var scrollHeight = bt.offsetHeight;
36201         var scrollWidth = ltWidth + bt.offsetWidth;
36202         var vscroll = false, hscroll = false;
36203
36204         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36205
36206         var lw = this.lockedWrap, mw = this.mainWrap;
36207         var lb = this.lockedBody, mb = this.mainBody;
36208
36209         setTimeout(function(){
36210             var t = s.dom.offsetTop;
36211             var w = s.dom.clientWidth,
36212                 h = s.dom.clientHeight;
36213
36214             lw.setTop(t);
36215             lw.setSize(ltWidth, h);
36216
36217             mw.setLeftTop(ltWidth, t);
36218             mw.setSize(w-ltWidth, h);
36219
36220             lb.setHeight(h-hdHeight);
36221             mb.setHeight(h-hdHeight);
36222
36223             if(is2ndPass !== true && !gv.userResized && expandCol){
36224                 // high speed resize without full column calculation
36225                 
36226                 var ci = cm.getIndexById(expandCol);
36227                 if (ci < 0) {
36228                     ci = cm.findColumnIndex(expandCol);
36229                 }
36230                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36231                 var expandId = cm.getColumnId(ci);
36232                 var  tw = cm.getTotalWidth(false);
36233                 var currentWidth = cm.getColumnWidth(ci);
36234                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36235                 if(currentWidth != cw){
36236                     cm.setColumnWidth(ci, cw, true);
36237                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36238                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36239                     gv.updateSplitters();
36240                     gv.layout(false, true);
36241                 }
36242             }
36243
36244             if(initialRender){
36245                 lw.show();
36246                 mw.show();
36247             }
36248             //c.endMeasure();
36249         }, 10);
36250     },
36251
36252     onWindowResize : function(){
36253         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36254             return;
36255         }
36256         this.layout();
36257     },
36258
36259     appendFooter : function(parentEl){
36260         return null;
36261     },
36262
36263     sortAscText : "Sort Ascending",
36264     sortDescText : "Sort Descending",
36265     lockText : "Lock Column",
36266     unlockText : "Unlock Column",
36267     columnsText : "Columns"
36268 });
36269
36270
36271 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36272     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36273     this.proxy.el.addClass('x-grid3-col-dd');
36274 };
36275
36276 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36277     handleMouseDown : function(e){
36278
36279     },
36280
36281     callHandleMouseDown : function(e){
36282         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36283     }
36284 });
36285 /*
36286  * Based on:
36287  * Ext JS Library 1.1.1
36288  * Copyright(c) 2006-2007, Ext JS, LLC.
36289  *
36290  * Originally Released Under LGPL - original licence link has changed is not relivant.
36291  *
36292  * Fork - LGPL
36293  * <script type="text/javascript">
36294  */
36295  
36296 // private
36297 // This is a support class used internally by the Grid components
36298 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36299     this.grid = grid;
36300     this.view = grid.getView();
36301     this.proxy = this.view.resizeProxy;
36302     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
36303         "gridSplitters" + this.grid.getGridEl().id, {
36304         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
36305     });
36306     this.setHandleElId(Roo.id(hd));
36307     this.setOuterHandleElId(Roo.id(hd2));
36308     this.scroll = false;
36309 };
36310 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36311     fly: Roo.Element.fly,
36312
36313     b4StartDrag : function(x, y){
36314         this.view.headersDisabled = true;
36315         this.proxy.setHeight(this.view.mainWrap.getHeight());
36316         var w = this.cm.getColumnWidth(this.cellIndex);
36317         var minw = Math.max(w-this.grid.minColumnWidth, 0);
36318         this.resetConstraints();
36319         this.setXConstraint(minw, 1000);
36320         this.setYConstraint(0, 0);
36321         this.minX = x - minw;
36322         this.maxX = x + 1000;
36323         this.startPos = x;
36324         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36325     },
36326
36327
36328     handleMouseDown : function(e){
36329         ev = Roo.EventObject.setEvent(e);
36330         var t = this.fly(ev.getTarget());
36331         if(t.hasClass("x-grid-split")){
36332             this.cellIndex = this.view.getCellIndex(t.dom);
36333             this.split = t.dom;
36334             this.cm = this.grid.colModel;
36335             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36336                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36337             }
36338         }
36339     },
36340
36341     endDrag : function(e){
36342         this.view.headersDisabled = false;
36343         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36344         var diff = endX - this.startPos;
36345         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
36346     },
36347
36348     autoOffset : function(){
36349         this.setDelta(0,0);
36350     }
36351 });/*
36352  * Based on:
36353  * Ext JS Library 1.1.1
36354  * Copyright(c) 2006-2007, Ext JS, LLC.
36355  *
36356  * Originally Released Under LGPL - original licence link has changed is not relivant.
36357  *
36358  * Fork - LGPL
36359  * <script type="text/javascript">
36360  */
36361  
36362 // private
36363 // This is a support class used internally by the Grid components
36364 Roo.grid.GridDragZone = function(grid, config){
36365     this.view = grid.getView();
36366     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36367     if(this.view.lockedBody){
36368         this.setHandleElId(Roo.id(this.view.mainBody.dom));
36369         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36370     }
36371     this.scroll = false;
36372     this.grid = grid;
36373     this.ddel = document.createElement('div');
36374     this.ddel.className = 'x-grid-dd-wrap';
36375 };
36376
36377 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36378     ddGroup : "GridDD",
36379
36380     getDragData : function(e){
36381         var t = Roo.lib.Event.getTarget(e);
36382         var rowIndex = this.view.findRowIndex(t);
36383         if(rowIndex !== false){
36384             var sm = this.grid.selModel;
36385             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36386               //  sm.mouseDown(e, t);
36387             //}
36388             if (e.hasModifier()){
36389                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
36390             }
36391             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
36392         }
36393         return false;
36394     },
36395
36396     onInitDrag : function(e){
36397         var data = this.dragData;
36398         this.ddel.innerHTML = this.grid.getDragDropText();
36399         this.proxy.update(this.ddel);
36400         // fire start drag?
36401     },
36402
36403     afterRepair : function(){
36404         this.dragging = false;
36405     },
36406
36407     getRepairXY : function(e, data){
36408         return false;
36409     },
36410
36411     onEndDrag : function(data, e){
36412         // fire end drag?
36413     },
36414
36415     onValidDrop : function(dd, e, id){
36416         // fire drag drop?
36417         this.hideProxy();
36418     },
36419
36420     beforeInvalidDrop : function(e, id){
36421
36422     }
36423 });/*
36424  * Based on:
36425  * Ext JS Library 1.1.1
36426  * Copyright(c) 2006-2007, Ext JS, LLC.
36427  *
36428  * Originally Released Under LGPL - original licence link has changed is not relivant.
36429  *
36430  * Fork - LGPL
36431  * <script type="text/javascript">
36432  */
36433  
36434
36435 /**
36436  * @class Roo.grid.ColumnModel
36437  * @extends Roo.util.Observable
36438  * This is the default implementation of a ColumnModel used by the Grid. It defines
36439  * the columns in the grid.
36440  * <br>Usage:<br>
36441  <pre><code>
36442  var colModel = new Roo.grid.ColumnModel([
36443         {header: "Ticker", width: 60, sortable: true, locked: true},
36444         {header: "Company Name", width: 150, sortable: true},
36445         {header: "Market Cap.", width: 100, sortable: true},
36446         {header: "$ Sales", width: 100, sortable: true, renderer: money},
36447         {header: "Employees", width: 100, sortable: true, resizable: false}
36448  ]);
36449  </code></pre>
36450  * <p>
36451  
36452  * The config options listed for this class are options which may appear in each
36453  * individual column definition.
36454  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
36455  * @constructor
36456  * @param {Object} config An Array of column config objects. See this class's
36457  * config objects for details.
36458 */
36459 Roo.grid.ColumnModel = function(config){
36460         /**
36461      * The config passed into the constructor
36462      */
36463     this.config = config;
36464     this.lookup = {};
36465
36466     // if no id, create one
36467     // if the column does not have a dataIndex mapping,
36468     // map it to the order it is in the config
36469     for(var i = 0, len = config.length; i < len; i++){
36470         var c = config[i];
36471         if(typeof c.dataIndex == "undefined"){
36472             c.dataIndex = i;
36473         }
36474         if(typeof c.renderer == "string"){
36475             c.renderer = Roo.util.Format[c.renderer];
36476         }
36477         if(typeof c.id == "undefined"){
36478             c.id = Roo.id();
36479         }
36480         if(c.editor && c.editor.xtype){
36481             c.editor  = Roo.factory(c.editor, Roo.grid);
36482         }
36483         if(c.editor && c.editor.isFormField){
36484             c.editor = new Roo.grid.GridEditor(c.editor);
36485         }
36486         this.lookup[c.id] = c;
36487     }
36488
36489     /**
36490      * The width of columns which have no width specified (defaults to 100)
36491      * @type Number
36492      */
36493     this.defaultWidth = 100;
36494
36495     /**
36496      * Default sortable of columns which have no sortable specified (defaults to false)
36497      * @type Boolean
36498      */
36499     this.defaultSortable = false;
36500
36501     this.addEvents({
36502         /**
36503              * @event widthchange
36504              * Fires when the width of a column changes.
36505              * @param {ColumnModel} this
36506              * @param {Number} columnIndex The column index
36507              * @param {Number} newWidth The new width
36508              */
36509             "widthchange": true,
36510         /**
36511              * @event headerchange
36512              * Fires when the text of a header changes.
36513              * @param {ColumnModel} this
36514              * @param {Number} columnIndex The column index
36515              * @param {Number} newText The new header text
36516              */
36517             "headerchange": true,
36518         /**
36519              * @event hiddenchange
36520              * Fires when a column is hidden or "unhidden".
36521              * @param {ColumnModel} this
36522              * @param {Number} columnIndex The column index
36523              * @param {Boolean} hidden true if hidden, false otherwise
36524              */
36525             "hiddenchange": true,
36526             /**
36527          * @event columnmoved
36528          * Fires when a column is moved.
36529          * @param {ColumnModel} this
36530          * @param {Number} oldIndex
36531          * @param {Number} newIndex
36532          */
36533         "columnmoved" : true,
36534         /**
36535          * @event columlockchange
36536          * Fires when a column's locked state is changed
36537          * @param {ColumnModel} this
36538          * @param {Number} colIndex
36539          * @param {Boolean} locked true if locked
36540          */
36541         "columnlockchange" : true
36542     });
36543     Roo.grid.ColumnModel.superclass.constructor.call(this);
36544 };
36545 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
36546     /**
36547      * @cfg {String} header The header text to display in the Grid view.
36548      */
36549     /**
36550      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
36551      * {@link Roo.data.Record} definition from which to draw the column's value. If not
36552      * specified, the column's index is used as an index into the Record's data Array.
36553      */
36554     /**
36555      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
36556      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
36557      */
36558     /**
36559      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
36560      * Defaults to the value of the {@link #defaultSortable} property.
36561      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
36562      */
36563     /**
36564      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
36565      */
36566     /**
36567      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
36568      */
36569     /**
36570      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
36571      */
36572     /**
36573      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
36574      */
36575     /**
36576      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
36577      * given the cell's data value. See {@link #setRenderer}. If not specified, the
36578      * default renderer uses the raw data value.
36579      */
36580        /**
36581      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
36582      */
36583     /**
36584      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
36585      */
36586
36587     /**
36588      * Returns the id of the column at the specified index.
36589      * @param {Number} index The column index
36590      * @return {String} the id
36591      */
36592     getColumnId : function(index){
36593         return this.config[index].id;
36594     },
36595
36596     /**
36597      * Returns the column for a specified id.
36598      * @param {String} id The column id
36599      * @return {Object} the column
36600      */
36601     getColumnById : function(id){
36602         return this.lookup[id];
36603     },
36604
36605     
36606     /**
36607      * Returns the column for a specified dataIndex.
36608      * @param {String} dataIndex The column dataIndex
36609      * @return {Object|Boolean} the column or false if not found
36610      */
36611     getColumnByDataIndex: function(dataIndex){
36612         var index = this.findColumnIndex(dataIndex);
36613         return index > -1 ? this.config[index] : false;
36614     },
36615     
36616     /**
36617      * Returns the index for a specified column id.
36618      * @param {String} id The column id
36619      * @return {Number} the index, or -1 if not found
36620      */
36621     getIndexById : function(id){
36622         for(var i = 0, len = this.config.length; i < len; i++){
36623             if(this.config[i].id == id){
36624                 return i;
36625             }
36626         }
36627         return -1;
36628     },
36629     
36630     /**
36631      * Returns the index for a specified column dataIndex.
36632      * @param {String} dataIndex The column dataIndex
36633      * @return {Number} the index, or -1 if not found
36634      */
36635     
36636     findColumnIndex : function(dataIndex){
36637         for(var i = 0, len = this.config.length; i < len; i++){
36638             if(this.config[i].dataIndex == dataIndex){
36639                 return i;
36640             }
36641         }
36642         return -1;
36643     },
36644     
36645     
36646     moveColumn : function(oldIndex, newIndex){
36647         var c = this.config[oldIndex];
36648         this.config.splice(oldIndex, 1);
36649         this.config.splice(newIndex, 0, c);
36650         this.dataMap = null;
36651         this.fireEvent("columnmoved", this, oldIndex, newIndex);
36652     },
36653
36654     isLocked : function(colIndex){
36655         return this.config[colIndex].locked === true;
36656     },
36657
36658     setLocked : function(colIndex, value, suppressEvent){
36659         if(this.isLocked(colIndex) == value){
36660             return;
36661         }
36662         this.config[colIndex].locked = value;
36663         if(!suppressEvent){
36664             this.fireEvent("columnlockchange", this, colIndex, value);
36665         }
36666     },
36667
36668     getTotalLockedWidth : function(){
36669         var totalWidth = 0;
36670         for(var i = 0; i < this.config.length; i++){
36671             if(this.isLocked(i) && !this.isHidden(i)){
36672                 this.totalWidth += this.getColumnWidth(i);
36673             }
36674         }
36675         return totalWidth;
36676     },
36677
36678     getLockedCount : function(){
36679         for(var i = 0, len = this.config.length; i < len; i++){
36680             if(!this.isLocked(i)){
36681                 return i;
36682             }
36683         }
36684     },
36685
36686     /**
36687      * Returns the number of columns.
36688      * @return {Number}
36689      */
36690     getColumnCount : function(visibleOnly){
36691         if(visibleOnly === true){
36692             var c = 0;
36693             for(var i = 0, len = this.config.length; i < len; i++){
36694                 if(!this.isHidden(i)){
36695                     c++;
36696                 }
36697             }
36698             return c;
36699         }
36700         return this.config.length;
36701     },
36702
36703     /**
36704      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
36705      * @param {Function} fn
36706      * @param {Object} scope (optional)
36707      * @return {Array} result
36708      */
36709     getColumnsBy : function(fn, scope){
36710         var r = [];
36711         for(var i = 0, len = this.config.length; i < len; i++){
36712             var c = this.config[i];
36713             if(fn.call(scope||this, c, i) === true){
36714                 r[r.length] = c;
36715             }
36716         }
36717         return r;
36718     },
36719
36720     /**
36721      * Returns true if the specified column is sortable.
36722      * @param {Number} col The column index
36723      * @return {Boolean}
36724      */
36725     isSortable : function(col){
36726         if(typeof this.config[col].sortable == "undefined"){
36727             return this.defaultSortable;
36728         }
36729         return this.config[col].sortable;
36730     },
36731
36732     /**
36733      * Returns the rendering (formatting) function defined for the column.
36734      * @param {Number} col The column index.
36735      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
36736      */
36737     getRenderer : function(col){
36738         if(!this.config[col].renderer){
36739             return Roo.grid.ColumnModel.defaultRenderer;
36740         }
36741         return this.config[col].renderer;
36742     },
36743
36744     /**
36745      * Sets the rendering (formatting) function for a column.
36746      * @param {Number} col The column index
36747      * @param {Function} fn The function to use to process the cell's raw data
36748      * to return HTML markup for the grid view. The render function is called with
36749      * the following parameters:<ul>
36750      * <li>Data value.</li>
36751      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
36752      * <li>css A CSS style string to apply to the table cell.</li>
36753      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
36754      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
36755      * <li>Row index</li>
36756      * <li>Column index</li>
36757      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
36758      */
36759     setRenderer : function(col, fn){
36760         this.config[col].renderer = fn;
36761     },
36762
36763     /**
36764      * Returns the width for the specified column.
36765      * @param {Number} col The column index
36766      * @return {Number}
36767      */
36768     getColumnWidth : function(col){
36769         return this.config[col].width * 1 || this.defaultWidth;
36770     },
36771
36772     /**
36773      * Sets the width for a column.
36774      * @param {Number} col The column index
36775      * @param {Number} width The new width
36776      */
36777     setColumnWidth : function(col, width, suppressEvent){
36778         this.config[col].width = width;
36779         this.totalWidth = null;
36780         if(!suppressEvent){
36781              this.fireEvent("widthchange", this, col, width);
36782         }
36783     },
36784
36785     /**
36786      * Returns the total width of all columns.
36787      * @param {Boolean} includeHidden True to include hidden column widths
36788      * @return {Number}
36789      */
36790     getTotalWidth : function(includeHidden){
36791         if(!this.totalWidth){
36792             this.totalWidth = 0;
36793             for(var i = 0, len = this.config.length; i < len; i++){
36794                 if(includeHidden || !this.isHidden(i)){
36795                     this.totalWidth += this.getColumnWidth(i);
36796                 }
36797             }
36798         }
36799         return this.totalWidth;
36800     },
36801
36802     /**
36803      * Returns the header for the specified column.
36804      * @param {Number} col The column index
36805      * @return {String}
36806      */
36807     getColumnHeader : function(col){
36808         return this.config[col].header;
36809     },
36810
36811     /**
36812      * Sets the header for a column.
36813      * @param {Number} col The column index
36814      * @param {String} header The new header
36815      */
36816     setColumnHeader : function(col, header){
36817         this.config[col].header = header;
36818         this.fireEvent("headerchange", this, col, header);
36819     },
36820
36821     /**
36822      * Returns the tooltip for the specified column.
36823      * @param {Number} col The column index
36824      * @return {String}
36825      */
36826     getColumnTooltip : function(col){
36827             return this.config[col].tooltip;
36828     },
36829     /**
36830      * Sets the tooltip for a column.
36831      * @param {Number} col The column index
36832      * @param {String} tooltip The new tooltip
36833      */
36834     setColumnTooltip : function(col, tooltip){
36835             this.config[col].tooltip = tooltip;
36836     },
36837
36838     /**
36839      * Returns the dataIndex for the specified column.
36840      * @param {Number} col The column index
36841      * @return {Number}
36842      */
36843     getDataIndex : function(col){
36844         return this.config[col].dataIndex;
36845     },
36846
36847     /**
36848      * Sets the dataIndex for a column.
36849      * @param {Number} col The column index
36850      * @param {Number} dataIndex The new dataIndex
36851      */
36852     setDataIndex : function(col, dataIndex){
36853         this.config[col].dataIndex = dataIndex;
36854     },
36855
36856     
36857     
36858     /**
36859      * Returns true if the cell is editable.
36860      * @param {Number} colIndex The column index
36861      * @param {Number} rowIndex The row index
36862      * @return {Boolean}
36863      */
36864     isCellEditable : function(colIndex, rowIndex){
36865         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
36866     },
36867
36868     /**
36869      * Returns the editor defined for the cell/column.
36870      * return false or null to disable editing.
36871      * @param {Number} colIndex The column index
36872      * @param {Number} rowIndex The row index
36873      * @return {Object}
36874      */
36875     getCellEditor : function(colIndex, rowIndex){
36876         return this.config[colIndex].editor;
36877     },
36878
36879     /**
36880      * Sets if a column is editable.
36881      * @param {Number} col The column index
36882      * @param {Boolean} editable True if the column is editable
36883      */
36884     setEditable : function(col, editable){
36885         this.config[col].editable = editable;
36886     },
36887
36888
36889     /**
36890      * Returns true if the column is hidden.
36891      * @param {Number} colIndex The column index
36892      * @return {Boolean}
36893      */
36894     isHidden : function(colIndex){
36895         return this.config[colIndex].hidden;
36896     },
36897
36898
36899     /**
36900      * Returns true if the column width cannot be changed
36901      */
36902     isFixed : function(colIndex){
36903         return this.config[colIndex].fixed;
36904     },
36905
36906     /**
36907      * Returns true if the column can be resized
36908      * @return {Boolean}
36909      */
36910     isResizable : function(colIndex){
36911         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
36912     },
36913     /**
36914      * Sets if a column is hidden.
36915      * @param {Number} colIndex The column index
36916      * @param {Boolean} hidden True if the column is hidden
36917      */
36918     setHidden : function(colIndex, hidden){
36919         this.config[colIndex].hidden = hidden;
36920         this.totalWidth = null;
36921         this.fireEvent("hiddenchange", this, colIndex, hidden);
36922     },
36923
36924     /**
36925      * Sets the editor for a column.
36926      * @param {Number} col The column index
36927      * @param {Object} editor The editor object
36928      */
36929     setEditor : function(col, editor){
36930         this.config[col].editor = editor;
36931     }
36932 });
36933
36934 Roo.grid.ColumnModel.defaultRenderer = function(value){
36935         if(typeof value == "string" && value.length < 1){
36936             return "&#160;";
36937         }
36938         return value;
36939 };
36940
36941 // Alias for backwards compatibility
36942 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
36943 /*
36944  * Based on:
36945  * Ext JS Library 1.1.1
36946  * Copyright(c) 2006-2007, Ext JS, LLC.
36947  *
36948  * Originally Released Under LGPL - original licence link has changed is not relivant.
36949  *
36950  * Fork - LGPL
36951  * <script type="text/javascript">
36952  */
36953
36954 /**
36955  * @class Roo.grid.AbstractSelectionModel
36956  * @extends Roo.util.Observable
36957  * Abstract base class for grid SelectionModels.  It provides the interface that should be
36958  * implemented by descendant classes.  This class should not be directly instantiated.
36959  * @constructor
36960  */
36961 Roo.grid.AbstractSelectionModel = function(){
36962     this.locked = false;
36963     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
36964 };
36965
36966 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
36967     /** @ignore Called by the grid automatically. Do not call directly. */
36968     init : function(grid){
36969         this.grid = grid;
36970         this.initEvents();
36971     },
36972
36973     /**
36974      * Locks the selections.
36975      */
36976     lock : function(){
36977         this.locked = true;
36978     },
36979
36980     /**
36981      * Unlocks the selections.
36982      */
36983     unlock : function(){
36984         this.locked = false;
36985     },
36986
36987     /**
36988      * Returns true if the selections are locked.
36989      * @return {Boolean}
36990      */
36991     isLocked : function(){
36992         return this.locked;
36993     }
36994 });/*
36995  * Based on:
36996  * Ext JS Library 1.1.1
36997  * Copyright(c) 2006-2007, Ext JS, LLC.
36998  *
36999  * Originally Released Under LGPL - original licence link has changed is not relivant.
37000  *
37001  * Fork - LGPL
37002  * <script type="text/javascript">
37003  */
37004 /**
37005  * @extends Roo.grid.AbstractSelectionModel
37006  * @class Roo.grid.RowSelectionModel
37007  * The default SelectionModel used by {@link Roo.grid.Grid}.
37008  * It supports multiple selections and keyboard selection/navigation. 
37009  * @constructor
37010  * @param {Object} config
37011  */
37012 Roo.grid.RowSelectionModel = function(config){
37013     Roo.apply(this, config);
37014     this.selections = new Roo.util.MixedCollection(false, function(o){
37015         return o.id;
37016     });
37017
37018     this.last = false;
37019     this.lastActive = false;
37020
37021     this.addEvents({
37022         /**
37023              * @event selectionchange
37024              * Fires when the selection changes
37025              * @param {SelectionModel} this
37026              */
37027             "selectionchange" : true,
37028         /**
37029              * @event afterselectionchange
37030              * Fires after the selection changes (eg. by key press or clicking)
37031              * @param {SelectionModel} this
37032              */
37033             "afterselectionchange" : true,
37034         /**
37035              * @event beforerowselect
37036              * Fires when a row is selected being selected, return false to cancel.
37037              * @param {SelectionModel} this
37038              * @param {Number} rowIndex The selected index
37039              * @param {Boolean} keepExisting False if other selections will be cleared
37040              */
37041             "beforerowselect" : true,
37042         /**
37043              * @event rowselect
37044              * Fires when a row is selected.
37045              * @param {SelectionModel} this
37046              * @param {Number} rowIndex The selected index
37047              * @param {Roo.data.Record} r The record
37048              */
37049             "rowselect" : true,
37050         /**
37051              * @event rowdeselect
37052              * Fires when a row is deselected.
37053              * @param {SelectionModel} this
37054              * @param {Number} rowIndex The selected index
37055              */
37056         "rowdeselect" : true
37057     });
37058     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
37059     this.locked = false;
37060 };
37061
37062 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
37063     /**
37064      * @cfg {Boolean} singleSelect
37065      * True to allow selection of only one row at a time (defaults to false)
37066      */
37067     singleSelect : false,
37068
37069     // private
37070     initEvents : function(){
37071
37072         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
37073             this.grid.on("mousedown", this.handleMouseDown, this);
37074         }else{ // allow click to work like normal
37075             this.grid.on("rowclick", this.handleDragableRowClick, this);
37076         }
37077
37078         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
37079             "up" : function(e){
37080                 if(!e.shiftKey){
37081                     this.selectPrevious(e.shiftKey);
37082                 }else if(this.last !== false && this.lastActive !== false){
37083                     var last = this.last;
37084                     this.selectRange(this.last,  this.lastActive-1);
37085                     this.grid.getView().focusRow(this.lastActive);
37086                     if(last !== false){
37087                         this.last = last;
37088                     }
37089                 }else{
37090                     this.selectFirstRow();
37091                 }
37092                 this.fireEvent("afterselectionchange", this);
37093             },
37094             "down" : function(e){
37095                 if(!e.shiftKey){
37096                     this.selectNext(e.shiftKey);
37097                 }else if(this.last !== false && this.lastActive !== false){
37098                     var last = this.last;
37099                     this.selectRange(this.last,  this.lastActive+1);
37100                     this.grid.getView().focusRow(this.lastActive);
37101                     if(last !== false){
37102                         this.last = last;
37103                     }
37104                 }else{
37105                     this.selectFirstRow();
37106                 }
37107                 this.fireEvent("afterselectionchange", this);
37108             },
37109             scope: this
37110         });
37111
37112         var view = this.grid.view;
37113         view.on("refresh", this.onRefresh, this);
37114         view.on("rowupdated", this.onRowUpdated, this);
37115         view.on("rowremoved", this.onRemove, this);
37116     },
37117
37118     // private
37119     onRefresh : function(){
37120         var ds = this.grid.dataSource, i, v = this.grid.view;
37121         var s = this.selections;
37122         s.each(function(r){
37123             if((i = ds.indexOfId(r.id)) != -1){
37124                 v.onRowSelect(i);
37125             }else{
37126                 s.remove(r);
37127             }
37128         });
37129     },
37130
37131     // private
37132     onRemove : function(v, index, r){
37133         this.selections.remove(r);
37134     },
37135
37136     // private
37137     onRowUpdated : function(v, index, r){
37138         if(this.isSelected(r)){
37139             v.onRowSelect(index);
37140         }
37141     },
37142
37143     /**
37144      * Select records.
37145      * @param {Array} records The records to select
37146      * @param {Boolean} keepExisting (optional) True to keep existing selections
37147      */
37148     selectRecords : function(records, keepExisting){
37149         if(!keepExisting){
37150             this.clearSelections();
37151         }
37152         var ds = this.grid.dataSource;
37153         for(var i = 0, len = records.length; i < len; i++){
37154             this.selectRow(ds.indexOf(records[i]), true);
37155         }
37156     },
37157
37158     /**
37159      * Gets the number of selected rows.
37160      * @return {Number}
37161      */
37162     getCount : function(){
37163         return this.selections.length;
37164     },
37165
37166     /**
37167      * Selects the first row in the grid.
37168      */
37169     selectFirstRow : function(){
37170         this.selectRow(0);
37171     },
37172
37173     /**
37174      * Select the last row.
37175      * @param {Boolean} keepExisting (optional) True to keep existing selections
37176      */
37177     selectLastRow : function(keepExisting){
37178         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
37179     },
37180
37181     /**
37182      * Selects the row immediately following the last selected row.
37183      * @param {Boolean} keepExisting (optional) True to keep existing selections
37184      */
37185     selectNext : function(keepExisting){
37186         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
37187             this.selectRow(this.last+1, keepExisting);
37188             this.grid.getView().focusRow(this.last);
37189         }
37190     },
37191
37192     /**
37193      * Selects the row that precedes the last selected row.
37194      * @param {Boolean} keepExisting (optional) True to keep existing selections
37195      */
37196     selectPrevious : function(keepExisting){
37197         if(this.last){
37198             this.selectRow(this.last-1, keepExisting);
37199             this.grid.getView().focusRow(this.last);
37200         }
37201     },
37202
37203     /**
37204      * Returns the selected records
37205      * @return {Array} Array of selected records
37206      */
37207     getSelections : function(){
37208         return [].concat(this.selections.items);
37209     },
37210
37211     /**
37212      * Returns the first selected record.
37213      * @return {Record}
37214      */
37215     getSelected : function(){
37216         return this.selections.itemAt(0);
37217     },
37218
37219
37220     /**
37221      * Clears all selections.
37222      */
37223     clearSelections : function(fast){
37224         if(this.locked) return;
37225         if(fast !== true){
37226             var ds = this.grid.dataSource;
37227             var s = this.selections;
37228             s.each(function(r){
37229                 this.deselectRow(ds.indexOfId(r.id));
37230             }, this);
37231             s.clear();
37232         }else{
37233             this.selections.clear();
37234         }
37235         this.last = false;
37236     },
37237
37238
37239     /**
37240      * Selects all rows.
37241      */
37242     selectAll : function(){
37243         if(this.locked) return;
37244         this.selections.clear();
37245         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
37246             this.selectRow(i, true);
37247         }
37248     },
37249
37250     /**
37251      * Returns True if there is a selection.
37252      * @return {Boolean}
37253      */
37254     hasSelection : function(){
37255         return this.selections.length > 0;
37256     },
37257
37258     /**
37259      * Returns True if the specified row is selected.
37260      * @param {Number/Record} record The record or index of the record to check
37261      * @return {Boolean}
37262      */
37263     isSelected : function(index){
37264         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
37265         return (r && this.selections.key(r.id) ? true : false);
37266     },
37267
37268     /**
37269      * Returns True if the specified record id is selected.
37270      * @param {String} id The id of record to check
37271      * @return {Boolean}
37272      */
37273     isIdSelected : function(id){
37274         return (this.selections.key(id) ? true : false);
37275     },
37276
37277     // private
37278     handleMouseDown : function(e, t){
37279         var view = this.grid.getView(), rowIndex;
37280         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37281             return;
37282         };
37283         if(e.shiftKey && this.last !== false){
37284             var last = this.last;
37285             this.selectRange(last, rowIndex, e.ctrlKey);
37286             this.last = last; // reset the last
37287             view.focusRow(rowIndex);
37288         }else{
37289             var isSelected = this.isSelected(rowIndex);
37290             if(e.button !== 0 && isSelected){
37291                 view.focusRow(rowIndex);
37292             }else if(e.ctrlKey && isSelected){
37293                 this.deselectRow(rowIndex);
37294             }else if(!isSelected){
37295                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37296                 view.focusRow(rowIndex);
37297             }
37298         }
37299         this.fireEvent("afterselectionchange", this);
37300     },
37301     // private
37302     handleDragableRowClick :  function(grid, rowIndex, e) 
37303     {
37304         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37305             this.selectRow(rowIndex, false);
37306             grid.view.focusRow(rowIndex);
37307              this.fireEvent("afterselectionchange", this);
37308         }
37309     },
37310     
37311     /**
37312      * Selects multiple rows.
37313      * @param {Array} rows Array of the indexes of the row to select
37314      * @param {Boolean} keepExisting (optional) True to keep existing selections
37315      */
37316     selectRows : function(rows, keepExisting){
37317         if(!keepExisting){
37318             this.clearSelections();
37319         }
37320         for(var i = 0, len = rows.length; i < len; i++){
37321             this.selectRow(rows[i], true);
37322         }
37323     },
37324
37325     /**
37326      * Selects a range of rows. All rows in between startRow and endRow are also selected.
37327      * @param {Number} startRow The index of the first row in the range
37328      * @param {Number} endRow The index of the last row in the range
37329      * @param {Boolean} keepExisting (optional) True to retain existing selections
37330      */
37331     selectRange : function(startRow, endRow, keepExisting){
37332         if(this.locked) return;
37333         if(!keepExisting){
37334             this.clearSelections();
37335         }
37336         if(startRow <= endRow){
37337             for(var i = startRow; i <= endRow; i++){
37338                 this.selectRow(i, true);
37339             }
37340         }else{
37341             for(var i = startRow; i >= endRow; i--){
37342                 this.selectRow(i, true);
37343             }
37344         }
37345     },
37346
37347     /**
37348      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37349      * @param {Number} startRow The index of the first row in the range
37350      * @param {Number} endRow The index of the last row in the range
37351      */
37352     deselectRange : function(startRow, endRow, preventViewNotify){
37353         if(this.locked) return;
37354         for(var i = startRow; i <= endRow; i++){
37355             this.deselectRow(i, preventViewNotify);
37356         }
37357     },
37358
37359     /**
37360      * Selects a row.
37361      * @param {Number} row The index of the row to select
37362      * @param {Boolean} keepExisting (optional) True to keep existing selections
37363      */
37364     selectRow : function(index, keepExisting, preventViewNotify){
37365         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
37366         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37367             if(!keepExisting || this.singleSelect){
37368                 this.clearSelections();
37369             }
37370             var r = this.grid.dataSource.getAt(index);
37371             this.selections.add(r);
37372             this.last = this.lastActive = index;
37373             if(!preventViewNotify){
37374                 this.grid.getView().onRowSelect(index);
37375             }
37376             this.fireEvent("rowselect", this, index, r);
37377             this.fireEvent("selectionchange", this);
37378         }
37379     },
37380
37381     /**
37382      * Deselects a row.
37383      * @param {Number} row The index of the row to deselect
37384      */
37385     deselectRow : function(index, preventViewNotify){
37386         if(this.locked) return;
37387         if(this.last == index){
37388             this.last = false;
37389         }
37390         if(this.lastActive == index){
37391             this.lastActive = false;
37392         }
37393         var r = this.grid.dataSource.getAt(index);
37394         this.selections.remove(r);
37395         if(!preventViewNotify){
37396             this.grid.getView().onRowDeselect(index);
37397         }
37398         this.fireEvent("rowdeselect", this, index);
37399         this.fireEvent("selectionchange", this);
37400     },
37401
37402     // private
37403     restoreLast : function(){
37404         if(this._last){
37405             this.last = this._last;
37406         }
37407     },
37408
37409     // private
37410     acceptsNav : function(row, col, cm){
37411         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37412     },
37413
37414     // private
37415     onEditorKey : function(field, e){
37416         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
37417         if(k == e.TAB){
37418             e.stopEvent();
37419             ed.completeEdit();
37420             if(e.shiftKey){
37421                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37422             }else{
37423                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37424             }
37425         }else if(k == e.ENTER && !e.ctrlKey){
37426             e.stopEvent();
37427             ed.completeEdit();
37428             if(e.shiftKey){
37429                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
37430             }else{
37431                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
37432             }
37433         }else if(k == e.ESC){
37434             ed.cancelEdit();
37435         }
37436         if(newCell){
37437             g.startEditing(newCell[0], newCell[1]);
37438         }
37439     }
37440 });/*
37441  * Based on:
37442  * Ext JS Library 1.1.1
37443  * Copyright(c) 2006-2007, Ext JS, LLC.
37444  *
37445  * Originally Released Under LGPL - original licence link has changed is not relivant.
37446  *
37447  * Fork - LGPL
37448  * <script type="text/javascript">
37449  */
37450 /**
37451  * @class Roo.grid.CellSelectionModel
37452  * @extends Roo.grid.AbstractSelectionModel
37453  * This class provides the basic implementation for cell selection in a grid.
37454  * @constructor
37455  * @param {Object} config The object containing the configuration of this model.
37456  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
37457  */
37458 Roo.grid.CellSelectionModel = function(config){
37459     Roo.apply(this, config);
37460
37461     this.selection = null;
37462
37463     this.addEvents({
37464         /**
37465              * @event beforerowselect
37466              * Fires before a cell is selected.
37467              * @param {SelectionModel} this
37468              * @param {Number} rowIndex The selected row index
37469              * @param {Number} colIndex The selected cell index
37470              */
37471             "beforecellselect" : true,
37472         /**
37473              * @event cellselect
37474              * Fires when a cell is selected.
37475              * @param {SelectionModel} this
37476              * @param {Number} rowIndex The selected row index
37477              * @param {Number} colIndex The selected cell index
37478              */
37479             "cellselect" : true,
37480         /**
37481              * @event selectionchange
37482              * Fires when the active selection changes.
37483              * @param {SelectionModel} this
37484              * @param {Object} selection null for no selection or an object (o) with two properties
37485                 <ul>
37486                 <li>o.record: the record object for the row the selection is in</li>
37487                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
37488                 </ul>
37489              */
37490             "selectionchange" : true,
37491         /**
37492              * @event tabend
37493              * Fires when the tab (or enter) was pressed on the last editable cell
37494              * You can use this to trigger add new row.
37495              * @param {SelectionModel} this
37496              */
37497             "tabend" : true,
37498          /**
37499              * @event beforeeditnext
37500              * Fires before the next editable sell is made active
37501              * You can use this to skip to another cell or fire the tabend
37502              *    if you set cell to false
37503              * @param {Object} eventdata object : { cell : [ row, col ] } 
37504              */
37505             "beforeeditnext" : true
37506     });
37507     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
37508 };
37509
37510 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
37511     
37512     enter_is_tab: false,
37513
37514     /** @ignore */
37515     initEvents : function(){
37516         this.grid.on("mousedown", this.handleMouseDown, this);
37517         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
37518         var view = this.grid.view;
37519         view.on("refresh", this.onViewChange, this);
37520         view.on("rowupdated", this.onRowUpdated, this);
37521         view.on("beforerowremoved", this.clearSelections, this);
37522         view.on("beforerowsinserted", this.clearSelections, this);
37523         if(this.grid.isEditor){
37524             this.grid.on("beforeedit", this.beforeEdit,  this);
37525         }
37526     },
37527
37528         //private
37529     beforeEdit : function(e){
37530         this.select(e.row, e.column, false, true, e.record);
37531     },
37532
37533         //private
37534     onRowUpdated : function(v, index, r){
37535         if(this.selection && this.selection.record == r){
37536             v.onCellSelect(index, this.selection.cell[1]);
37537         }
37538     },
37539
37540         //private
37541     onViewChange : function(){
37542         this.clearSelections(true);
37543     },
37544
37545         /**
37546          * Returns the currently selected cell,.
37547          * @return {Array} The selected cell (row, column) or null if none selected.
37548          */
37549     getSelectedCell : function(){
37550         return this.selection ? this.selection.cell : null;
37551     },
37552
37553     /**
37554      * Clears all selections.
37555      * @param {Boolean} true to prevent the gridview from being notified about the change.
37556      */
37557     clearSelections : function(preventNotify){
37558         var s = this.selection;
37559         if(s){
37560             if(preventNotify !== true){
37561                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
37562             }
37563             this.selection = null;
37564             this.fireEvent("selectionchange", this, null);
37565         }
37566     },
37567
37568     /**
37569      * Returns true if there is a selection.
37570      * @return {Boolean}
37571      */
37572     hasSelection : function(){
37573         return this.selection ? true : false;
37574     },
37575
37576     /** @ignore */
37577     handleMouseDown : function(e, t){
37578         var v = this.grid.getView();
37579         if(this.isLocked()){
37580             return;
37581         };
37582         var row = v.findRowIndex(t);
37583         var cell = v.findCellIndex(t);
37584         if(row !== false && cell !== false){
37585             this.select(row, cell);
37586         }
37587     },
37588
37589     /**
37590      * Selects a cell.
37591      * @param {Number} rowIndex
37592      * @param {Number} collIndex
37593      */
37594     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
37595         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
37596             this.clearSelections();
37597             r = r || this.grid.dataSource.getAt(rowIndex);
37598             this.selection = {
37599                 record : r,
37600                 cell : [rowIndex, colIndex]
37601             };
37602             if(!preventViewNotify){
37603                 var v = this.grid.getView();
37604                 v.onCellSelect(rowIndex, colIndex);
37605                 if(preventFocus !== true){
37606                     v.focusCell(rowIndex, colIndex);
37607                 }
37608             }
37609             this.fireEvent("cellselect", this, rowIndex, colIndex);
37610             this.fireEvent("selectionchange", this, this.selection);
37611         }
37612     },
37613
37614         //private
37615     isSelectable : function(rowIndex, colIndex, cm){
37616         return !cm.isHidden(colIndex);
37617     },
37618
37619     /** @ignore */
37620     handleKeyDown : function(e){
37621         //Roo.log('Cell Sel Model handleKeyDown');
37622         if(!e.isNavKeyPress()){
37623             return;
37624         }
37625         var g = this.grid, s = this.selection;
37626         if(!s){
37627             e.stopEvent();
37628             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
37629             if(cell){
37630                 this.select(cell[0], cell[1]);
37631             }
37632             return;
37633         }
37634         var sm = this;
37635         var walk = function(row, col, step){
37636             return g.walkCells(row, col, step, sm.isSelectable,  sm);
37637         };
37638         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
37639         var newCell;
37640
37641       
37642
37643         switch(k){
37644             case e.TAB:
37645                 // handled by onEditorKey
37646                 if (g.isEditor && g.editing) {
37647                     return;
37648                 }
37649                 if(e.shiftKey) {
37650                     newCell = walk(r, c-1, -1);
37651                 } else {
37652                     newCell = walk(r, c+1, 1);
37653                 }
37654                 break;
37655             
37656             case e.DOWN:
37657                newCell = walk(r+1, c, 1);
37658                 break;
37659             
37660             case e.UP:
37661                 newCell = walk(r-1, c, -1);
37662                 break;
37663             
37664             case e.RIGHT:
37665                 newCell = walk(r, c+1, 1);
37666                 break;
37667             
37668             case e.LEFT:
37669                 newCell = walk(r, c-1, -1);
37670                 break;
37671             
37672             case e.ENTER:
37673                 
37674                 if(g.isEditor && !g.editing){
37675                    g.startEditing(r, c);
37676                    e.stopEvent();
37677                    return;
37678                 }
37679                 
37680                 
37681              break;
37682         };
37683         if(newCell){
37684             this.select(newCell[0], newCell[1]);
37685             e.stopEvent();
37686             
37687         }
37688     },
37689
37690     acceptsNav : function(row, col, cm){
37691         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37692     },
37693     /**
37694      * Selects a cell.
37695      * @param {Number} field (not used) - as it's normally used as a listener
37696      * @param {Number} e - event - fake it by using
37697      *
37698      * var e = Roo.EventObjectImpl.prototype;
37699      * e.keyCode = e.TAB
37700      *
37701      * 
37702      */
37703     onEditorKey : function(field, e){
37704         
37705         var k = e.getKey(),
37706             newCell,
37707             g = this.grid,
37708             ed = g.activeEditor,
37709             forward = false;
37710         ///Roo.log('onEditorKey' + k);
37711         
37712         
37713         if (this.enter_is_tab && k == e.ENTER) {
37714             k = e.TAB;
37715         }
37716         
37717         if(k == e.TAB){
37718             if(e.shiftKey){
37719                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37720             }else{
37721                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37722                 forward = true;
37723             }
37724             
37725             e.stopEvent();
37726             
37727         } else if(k == e.ENTER &&  !e.ctrlKey){
37728             ed.completeEdit();
37729             e.stopEvent();
37730             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37731         
37732                 } else if(k == e.ESC){
37733             ed.cancelEdit();
37734         }
37735                 
37736         if (newCell) {
37737             var ecall = { cell : newCell, forward : forward };
37738             this.fireEvent('beforeeditnext', ecall );
37739             newCell = ecall.cell;
37740                         forward = ecall.forward;
37741         }
37742                 
37743         if(newCell){
37744             //Roo.log('next cell after edit');
37745             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
37746         } else if (forward) {
37747             // tabbed past last
37748             this.fireEvent.defer(100, this, ['tabend',this]);
37749         }
37750     }
37751 });/*
37752  * Based on:
37753  * Ext JS Library 1.1.1
37754  * Copyright(c) 2006-2007, Ext JS, LLC.
37755  *
37756  * Originally Released Under LGPL - original licence link has changed is not relivant.
37757  *
37758  * Fork - LGPL
37759  * <script type="text/javascript">
37760  */
37761  
37762 /**
37763  * @class Roo.grid.EditorGrid
37764  * @extends Roo.grid.Grid
37765  * Class for creating and editable grid.
37766  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
37767  * The container MUST have some type of size defined for the grid to fill. The container will be 
37768  * automatically set to position relative if it isn't already.
37769  * @param {Object} dataSource The data model to bind to
37770  * @param {Object} colModel The column model with info about this grid's columns
37771  */
37772 Roo.grid.EditorGrid = function(container, config){
37773     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
37774     this.getGridEl().addClass("xedit-grid");
37775
37776     if(!this.selModel){
37777         this.selModel = new Roo.grid.CellSelectionModel();
37778     }
37779
37780     this.activeEditor = null;
37781
37782         this.addEvents({
37783             /**
37784              * @event beforeedit
37785              * Fires before cell editing is triggered. The edit event object has the following properties <br />
37786              * <ul style="padding:5px;padding-left:16px;">
37787              * <li>grid - This grid</li>
37788              * <li>record - The record being edited</li>
37789              * <li>field - The field name being edited</li>
37790              * <li>value - The value for the field being edited.</li>
37791              * <li>row - The grid row index</li>
37792              * <li>column - The grid column index</li>
37793              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37794              * </ul>
37795              * @param {Object} e An edit event (see above for description)
37796              */
37797             "beforeedit" : true,
37798             /**
37799              * @event afteredit
37800              * Fires after a cell is edited. <br />
37801              * <ul style="padding:5px;padding-left:16px;">
37802              * <li>grid - This grid</li>
37803              * <li>record - The record being edited</li>
37804              * <li>field - The field name being edited</li>
37805              * <li>value - The value being set</li>
37806              * <li>originalValue - The original value for the field, before the edit.</li>
37807              * <li>row - The grid row index</li>
37808              * <li>column - The grid column index</li>
37809              * </ul>
37810              * @param {Object} e An edit event (see above for description)
37811              */
37812             "afteredit" : true,
37813             /**
37814              * @event validateedit
37815              * Fires after a cell is edited, but before the value is set in the record. 
37816          * You can use this to modify the value being set in the field, Return false
37817              * to cancel the change. The edit event object has the following properties <br />
37818              * <ul style="padding:5px;padding-left:16px;">
37819          * <li>editor - This editor</li>
37820              * <li>grid - This grid</li>
37821              * <li>record - The record being edited</li>
37822              * <li>field - The field name being edited</li>
37823              * <li>value - The value being set</li>
37824              * <li>originalValue - The original value for the field, before the edit.</li>
37825              * <li>row - The grid row index</li>
37826              * <li>column - The grid column index</li>
37827              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
37828              * </ul>
37829              * @param {Object} e An edit event (see above for description)
37830              */
37831             "validateedit" : true
37832         });
37833     this.on("bodyscroll", this.stopEditing,  this);
37834     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
37835 };
37836
37837 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
37838     /**
37839      * @cfg {Number} clicksToEdit
37840      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
37841      */
37842     clicksToEdit: 2,
37843
37844     // private
37845     isEditor : true,
37846     // private
37847     trackMouseOver: false, // causes very odd FF errors
37848
37849     onCellDblClick : function(g, row, col){
37850         this.startEditing(row, col);
37851     },
37852
37853     onEditComplete : function(ed, value, startValue){
37854         this.editing = false;
37855         this.activeEditor = null;
37856         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
37857         var r = ed.record;
37858         var field = this.colModel.getDataIndex(ed.col);
37859         var e = {
37860             grid: this,
37861             record: r,
37862             field: field,
37863             originalValue: startValue,
37864             value: value,
37865             row: ed.row,
37866             column: ed.col,
37867             cancel:false,
37868             editor: ed
37869         };
37870         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
37871         cell.show();
37872           
37873         if(String(value) !== String(startValue)){
37874             
37875             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
37876                 r.set(field, e.value);
37877                 // if we are dealing with a combo box..
37878                 // then we also set the 'name' colum to be the displayField
37879                 if (ed.field.displayField && ed.field.name) {
37880                     r.set(ed.field.name, ed.field.el.dom.value);
37881                 }
37882                 
37883                 delete e.cancel; //?? why!!!
37884                 this.fireEvent("afteredit", e);
37885             }
37886         } else {
37887             this.fireEvent("afteredit", e); // always fire it!
37888         }
37889         this.view.focusCell(ed.row, ed.col);
37890     },
37891
37892     /**
37893      * Starts editing the specified for the specified row/column
37894      * @param {Number} rowIndex
37895      * @param {Number} colIndex
37896      */
37897     startEditing : function(row, col){
37898         this.stopEditing();
37899         if(this.colModel.isCellEditable(col, row)){
37900             this.view.ensureVisible(row, col, true);
37901           
37902             var r = this.dataSource.getAt(row);
37903             var field = this.colModel.getDataIndex(col);
37904             var cell = Roo.get(this.view.getCell(row,col));
37905             var e = {
37906                 grid: this,
37907                 record: r,
37908                 field: field,
37909                 value: r.data[field],
37910                 row: row,
37911                 column: col,
37912                 cancel:false 
37913             };
37914             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
37915                 this.editing = true;
37916                 var ed = this.colModel.getCellEditor(col, row);
37917                 
37918                 if (!ed) {
37919                     return;
37920                 }
37921                 if(!ed.rendered){
37922                     ed.render(ed.parentEl || document.body);
37923                 }
37924                 ed.field.reset();
37925                
37926                 cell.hide();
37927                 
37928                 (function(){ // complex but required for focus issues in safari, ie and opera
37929                     ed.row = row;
37930                     ed.col = col;
37931                     ed.record = r;
37932                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
37933                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
37934                     this.activeEditor = ed;
37935                     var v = r.data[field];
37936                     ed.startEdit(this.view.getCell(row, col), v);
37937                     // combo's with 'displayField and name set
37938                     if (ed.field.displayField && ed.field.name) {
37939                         ed.field.el.dom.value = r.data[ed.field.name];
37940                     }
37941                     
37942                     
37943                 }).defer(50, this);
37944             }
37945         }
37946     },
37947         
37948     /**
37949      * Stops any active editing
37950      */
37951     stopEditing : function(){
37952         if(this.activeEditor){
37953             this.activeEditor.completeEdit();
37954         }
37955         this.activeEditor = null;
37956     }
37957 });/*
37958  * Based on:
37959  * Ext JS Library 1.1.1
37960  * Copyright(c) 2006-2007, Ext JS, LLC.
37961  *
37962  * Originally Released Under LGPL - original licence link has changed is not relivant.
37963  *
37964  * Fork - LGPL
37965  * <script type="text/javascript">
37966  */
37967
37968 // private - not really -- you end up using it !
37969 // This is a support class used internally by the Grid components
37970
37971 /**
37972  * @class Roo.grid.GridEditor
37973  * @extends Roo.Editor
37974  * Class for creating and editable grid elements.
37975  * @param {Object} config any settings (must include field)
37976  */
37977 Roo.grid.GridEditor = function(field, config){
37978     if (!config && field.field) {
37979         config = field;
37980         field = Roo.factory(config.field, Roo.form);
37981     }
37982     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
37983     field.monitorTab = false;
37984 };
37985
37986 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
37987     
37988     /**
37989      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
37990      */
37991     
37992     alignment: "tl-tl",
37993     autoSize: "width",
37994     hideEl : false,
37995     cls: "x-small-editor x-grid-editor",
37996     shim:false,
37997     shadow:"frame"
37998 });/*
37999  * Based on:
38000  * Ext JS Library 1.1.1
38001  * Copyright(c) 2006-2007, Ext JS, LLC.
38002  *
38003  * Originally Released Under LGPL - original licence link has changed is not relivant.
38004  *
38005  * Fork - LGPL
38006  * <script type="text/javascript">
38007  */
38008   
38009
38010   
38011 Roo.grid.PropertyRecord = Roo.data.Record.create([
38012     {name:'name',type:'string'},  'value'
38013 ]);
38014
38015
38016 Roo.grid.PropertyStore = function(grid, source){
38017     this.grid = grid;
38018     this.store = new Roo.data.Store({
38019         recordType : Roo.grid.PropertyRecord
38020     });
38021     this.store.on('update', this.onUpdate,  this);
38022     if(source){
38023         this.setSource(source);
38024     }
38025     Roo.grid.PropertyStore.superclass.constructor.call(this);
38026 };
38027
38028
38029
38030 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
38031     setSource : function(o){
38032         this.source = o;
38033         this.store.removeAll();
38034         var data = [];
38035         for(var k in o){
38036             if(this.isEditableValue(o[k])){
38037                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
38038             }
38039         }
38040         this.store.loadRecords({records: data}, {}, true);
38041     },
38042
38043     onUpdate : function(ds, record, type){
38044         if(type == Roo.data.Record.EDIT){
38045             var v = record.data['value'];
38046             var oldValue = record.modified['value'];
38047             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
38048                 this.source[record.id] = v;
38049                 record.commit();
38050                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
38051             }else{
38052                 record.reject();
38053             }
38054         }
38055     },
38056
38057     getProperty : function(row){
38058        return this.store.getAt(row);
38059     },
38060
38061     isEditableValue: function(val){
38062         if(val && val instanceof Date){
38063             return true;
38064         }else if(typeof val == 'object' || typeof val == 'function'){
38065             return false;
38066         }
38067         return true;
38068     },
38069
38070     setValue : function(prop, value){
38071         this.source[prop] = value;
38072         this.store.getById(prop).set('value', value);
38073     },
38074
38075     getSource : function(){
38076         return this.source;
38077     }
38078 });
38079
38080 Roo.grid.PropertyColumnModel = function(grid, store){
38081     this.grid = grid;
38082     var g = Roo.grid;
38083     g.PropertyColumnModel.superclass.constructor.call(this, [
38084         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
38085         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
38086     ]);
38087     this.store = store;
38088     this.bselect = Roo.DomHelper.append(document.body, {
38089         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38090             {tag: 'option', value: 'true', html: 'true'},
38091             {tag: 'option', value: 'false', html: 'false'}
38092         ]
38093     });
38094     Roo.id(this.bselect);
38095     var f = Roo.form;
38096     this.editors = {
38097         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38098         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38099         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38100         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38101         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38102     };
38103     this.renderCellDelegate = this.renderCell.createDelegate(this);
38104     this.renderPropDelegate = this.renderProp.createDelegate(this);
38105 };
38106
38107 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38108     
38109     
38110     nameText : 'Name',
38111     valueText : 'Value',
38112     
38113     dateFormat : 'm/j/Y',
38114     
38115     
38116     renderDate : function(dateVal){
38117         return dateVal.dateFormat(this.dateFormat);
38118     },
38119
38120     renderBool : function(bVal){
38121         return bVal ? 'true' : 'false';
38122     },
38123
38124     isCellEditable : function(colIndex, rowIndex){
38125         return colIndex == 1;
38126     },
38127
38128     getRenderer : function(col){
38129         return col == 1 ?
38130             this.renderCellDelegate : this.renderPropDelegate;
38131     },
38132
38133     renderProp : function(v){
38134         return this.getPropertyName(v);
38135     },
38136
38137     renderCell : function(val){
38138         var rv = val;
38139         if(val instanceof Date){
38140             rv = this.renderDate(val);
38141         }else if(typeof val == 'boolean'){
38142             rv = this.renderBool(val);
38143         }
38144         return Roo.util.Format.htmlEncode(rv);
38145     },
38146
38147     getPropertyName : function(name){
38148         var pn = this.grid.propertyNames;
38149         return pn && pn[name] ? pn[name] : name;
38150     },
38151
38152     getCellEditor : function(colIndex, rowIndex){
38153         var p = this.store.getProperty(rowIndex);
38154         var n = p.data['name'], val = p.data['value'];
38155         
38156         if(typeof(this.grid.customEditors[n]) == 'string'){
38157             return this.editors[this.grid.customEditors[n]];
38158         }
38159         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38160             return this.grid.customEditors[n];
38161         }
38162         if(val instanceof Date){
38163             return this.editors['date'];
38164         }else if(typeof val == 'number'){
38165             return this.editors['number'];
38166         }else if(typeof val == 'boolean'){
38167             return this.editors['boolean'];
38168         }else{
38169             return this.editors['string'];
38170         }
38171     }
38172 });
38173
38174 /**
38175  * @class Roo.grid.PropertyGrid
38176  * @extends Roo.grid.EditorGrid
38177  * This class represents the  interface of a component based property grid control.
38178  * <br><br>Usage:<pre><code>
38179  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38180       
38181  });
38182  // set any options
38183  grid.render();
38184  * </code></pre>
38185   
38186  * @constructor
38187  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38188  * The container MUST have some type of size defined for the grid to fill. The container will be
38189  * automatically set to position relative if it isn't already.
38190  * @param {Object} config A config object that sets properties on this grid.
38191  */
38192 Roo.grid.PropertyGrid = function(container, config){
38193     config = config || {};
38194     var store = new Roo.grid.PropertyStore(this);
38195     this.store = store;
38196     var cm = new Roo.grid.PropertyColumnModel(this, store);
38197     store.store.sort('name', 'ASC');
38198     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38199         ds: store.store,
38200         cm: cm,
38201         enableColLock:false,
38202         enableColumnMove:false,
38203         stripeRows:false,
38204         trackMouseOver: false,
38205         clicksToEdit:1
38206     }, config));
38207     this.getGridEl().addClass('x-props-grid');
38208     this.lastEditRow = null;
38209     this.on('columnresize', this.onColumnResize, this);
38210     this.addEvents({
38211          /**
38212              * @event beforepropertychange
38213              * Fires before a property changes (return false to stop?)
38214              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38215              * @param {String} id Record Id
38216              * @param {String} newval New Value
38217          * @param {String} oldval Old Value
38218              */
38219         "beforepropertychange": true,
38220         /**
38221              * @event propertychange
38222              * Fires after a property changes
38223              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38224              * @param {String} id Record Id
38225              * @param {String} newval New Value
38226          * @param {String} oldval Old Value
38227              */
38228         "propertychange": true
38229     });
38230     this.customEditors = this.customEditors || {};
38231 };
38232 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38233     
38234      /**
38235      * @cfg {Object} customEditors map of colnames=> custom editors.
38236      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38237      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38238      * false disables editing of the field.
38239          */
38240     
38241       /**
38242      * @cfg {Object} propertyNames map of property Names to their displayed value
38243          */
38244     
38245     render : function(){
38246         Roo.grid.PropertyGrid.superclass.render.call(this);
38247         this.autoSize.defer(100, this);
38248     },
38249
38250     autoSize : function(){
38251         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38252         if(this.view){
38253             this.view.fitColumns();
38254         }
38255     },
38256
38257     onColumnResize : function(){
38258         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38259         this.autoSize();
38260     },
38261     /**
38262      * Sets the data for the Grid
38263      * accepts a Key => Value object of all the elements avaiable.
38264      * @param {Object} data  to appear in grid.
38265      */
38266     setSource : function(source){
38267         this.store.setSource(source);
38268         //this.autoSize();
38269     },
38270     /**
38271      * Gets all the data from the grid.
38272      * @return {Object} data  data stored in grid
38273      */
38274     getSource : function(){
38275         return this.store.getSource();
38276     }
38277 });/*
38278  * Based on:
38279  * Ext JS Library 1.1.1
38280  * Copyright(c) 2006-2007, Ext JS, LLC.
38281  *
38282  * Originally Released Under LGPL - original licence link has changed is not relivant.
38283  *
38284  * Fork - LGPL
38285  * <script type="text/javascript">
38286  */
38287  
38288 /**
38289  * @class Roo.LoadMask
38290  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38291  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38292  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38293  * element's UpdateManager load indicator and will be destroyed after the initial load.
38294  * @constructor
38295  * Create a new LoadMask
38296  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38297  * @param {Object} config The config object
38298  */
38299 Roo.LoadMask = function(el, config){
38300     this.el = Roo.get(el);
38301     Roo.apply(this, config);
38302     if(this.store){
38303         this.store.on('beforeload', this.onBeforeLoad, this);
38304         this.store.on('load', this.onLoad, this);
38305         this.store.on('loadexception', this.onLoadException, this);
38306         this.removeMask = false;
38307     }else{
38308         var um = this.el.getUpdateManager();
38309         um.showLoadIndicator = false; // disable the default indicator
38310         um.on('beforeupdate', this.onBeforeLoad, this);
38311         um.on('update', this.onLoad, this);
38312         um.on('failure', this.onLoad, this);
38313         this.removeMask = true;
38314     }
38315 };
38316
38317 Roo.LoadMask.prototype = {
38318     /**
38319      * @cfg {Boolean} removeMask
38320      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38321      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38322      */
38323     /**
38324      * @cfg {String} msg
38325      * The text to display in a centered loading message box (defaults to 'Loading...')
38326      */
38327     msg : 'Loading...',
38328     /**
38329      * @cfg {String} msgCls
38330      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38331      */
38332     msgCls : 'x-mask-loading',
38333
38334     /**
38335      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38336      * @type Boolean
38337      */
38338     disabled: false,
38339
38340     /**
38341      * Disables the mask to prevent it from being displayed
38342      */
38343     disable : function(){
38344        this.disabled = true;
38345     },
38346
38347     /**
38348      * Enables the mask so that it can be displayed
38349      */
38350     enable : function(){
38351         this.disabled = false;
38352     },
38353     
38354     onLoadException : function()
38355     {
38356         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38357             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38358         }
38359         this.el.unmask(this.removeMask);
38360     },
38361     // private
38362     onLoad : function()
38363     {
38364         this.el.unmask(this.removeMask);
38365     },
38366
38367     // private
38368     onBeforeLoad : function(){
38369         if(!this.disabled){
38370             this.el.mask(this.msg, this.msgCls);
38371         }
38372     },
38373
38374     // private
38375     destroy : function(){
38376         if(this.store){
38377             this.store.un('beforeload', this.onBeforeLoad, this);
38378             this.store.un('load', this.onLoad, this);
38379             this.store.un('loadexception', this.onLoadException, this);
38380         }else{
38381             var um = this.el.getUpdateManager();
38382             um.un('beforeupdate', this.onBeforeLoad, this);
38383             um.un('update', this.onLoad, this);
38384             um.un('failure', this.onLoad, this);
38385         }
38386     }
38387 };/*
38388  * Based on:
38389  * Ext JS Library 1.1.1
38390  * Copyright(c) 2006-2007, Ext JS, LLC.
38391  *
38392  * Originally Released Under LGPL - original licence link has changed is not relivant.
38393  *
38394  * Fork - LGPL
38395  * <script type="text/javascript">
38396  */
38397
38398
38399 /**
38400  * @class Roo.XTemplate
38401  * @extends Roo.Template
38402  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38403 <pre><code>
38404 var t = new Roo.XTemplate(
38405         '&lt;select name="{name}"&gt;',
38406                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38407         '&lt;/select&gt;'
38408 );
38409  
38410 // then append, applying the master template values
38411  </code></pre>
38412  *
38413  * Supported features:
38414  *
38415  *  Tags:
38416
38417 <pre><code>
38418       {a_variable} - output encoded.
38419       {a_variable.format:("Y-m-d")} - call a method on the variable
38420       {a_variable:raw} - unencoded output
38421       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38422       {a_variable:this.method_on_template(...)} - call a method on the template object.
38423  
38424 </code></pre>
38425  *  The tpl tag:
38426 <pre><code>
38427         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38428         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38429         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38430         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38431   
38432         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38433         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38434 </code></pre>
38435  *      
38436  */
38437 Roo.XTemplate = function()
38438 {
38439     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38440     if (this.html) {
38441         this.compile();
38442     }
38443 };
38444
38445
38446 Roo.extend(Roo.XTemplate, Roo.Template, {
38447
38448     /**
38449      * The various sub templates
38450      */
38451     tpls : false,
38452     /**
38453      *
38454      * basic tag replacing syntax
38455      * WORD:WORD()
38456      *
38457      * // you can fake an object call by doing this
38458      *  x.t:(test,tesT) 
38459      * 
38460      */
38461     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38462
38463     /**
38464      * compile the template
38465      *
38466      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38467      *
38468      */
38469     compile: function()
38470     {
38471         var s = this.html;
38472      
38473         s = ['<tpl>', s, '</tpl>'].join('');
38474     
38475         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38476             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38477             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38478             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38479             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38480             m,
38481             id     = 0,
38482             tpls   = [];
38483     
38484         while(true == !!(m = s.match(re))){
38485             var forMatch   = m[0].match(nameRe),
38486                 ifMatch   = m[0].match(ifRe),
38487                 execMatch   = m[0].match(execRe),
38488                 namedMatch   = m[0].match(namedRe),
38489                 
38490                 exp  = null, 
38491                 fn   = null,
38492                 exec = null,
38493                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38494                 
38495             if (ifMatch) {
38496                 // if - puts fn into test..
38497                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38498                 if(exp){
38499                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38500                 }
38501             }
38502             
38503             if (execMatch) {
38504                 // exec - calls a function... returns empty if true is  returned.
38505                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38506                 if(exp){
38507                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38508                 }
38509             }
38510             
38511             
38512             if (name) {
38513                 // for = 
38514                 switch(name){
38515                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38516                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38517                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38518                 }
38519             }
38520             var uid = namedMatch ? namedMatch[1] : id;
38521             
38522             
38523             tpls.push({
38524                 id:     namedMatch ? namedMatch[1] : id,
38525                 target: name,
38526                 exec:   exec,
38527                 test:   fn,
38528                 body:   m[1] || ''
38529             });
38530             if (namedMatch) {
38531                 s = s.replace(m[0], '');
38532             } else { 
38533                 s = s.replace(m[0], '{xtpl'+ id + '}');
38534             }
38535             ++id;
38536         }
38537         this.tpls = [];
38538         for(var i = tpls.length-1; i >= 0; --i){
38539             this.compileTpl(tpls[i]);
38540             this.tpls[tpls[i].id] = tpls[i];
38541         }
38542         this.master = tpls[tpls.length-1];
38543         return this;
38544     },
38545     /**
38546      * same as applyTemplate, except it's done to one of the subTemplates
38547      * when using named templates, you can do:
38548      *
38549      * var str = pl.applySubTemplate('your-name', values);
38550      *
38551      * 
38552      * @param {Number} id of the template
38553      * @param {Object} values to apply to template
38554      * @param {Object} parent (normaly the instance of this object)
38555      */
38556     applySubTemplate : function(id, values, parent)
38557     {
38558         
38559         
38560         var t = this.tpls[id];
38561         
38562         
38563         try { 
38564             if(t.test && !t.test.call(this, values, parent)){
38565                 return '';
38566             }
38567         } catch(e) {
38568             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38569             Roo.log(e.toString());
38570             Roo.log(t.test);
38571             return ''
38572         }
38573         try { 
38574             
38575             if(t.exec && t.exec.call(this, values, parent)){
38576                 return '';
38577             }
38578         } catch(e) {
38579             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38580             Roo.log(e.toString());
38581             Roo.log(t.exec);
38582             return ''
38583         }
38584         try {
38585             var vs = t.target ? t.target.call(this, values, parent) : values;
38586             parent = t.target ? values : parent;
38587             if(t.target && vs instanceof Array){
38588                 var buf = [];
38589                 for(var i = 0, len = vs.length; i < len; i++){
38590                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38591                 }
38592                 return buf.join('');
38593             }
38594             return t.compiled.call(this, vs, parent);
38595         } catch (e) {
38596             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38597             Roo.log(e.toString());
38598             Roo.log(t.compiled);
38599             return '';
38600         }
38601     },
38602
38603     compileTpl : function(tpl)
38604     {
38605         var fm = Roo.util.Format;
38606         var useF = this.disableFormats !== true;
38607         var sep = Roo.isGecko ? "+" : ",";
38608         var undef = function(str) {
38609             Roo.log("Property not found :"  + str);
38610             return '';
38611         };
38612         
38613         var fn = function(m, name, format, args)
38614         {
38615             //Roo.log(arguments);
38616             args = args ? args.replace(/\\'/g,"'") : args;
38617             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38618             if (typeof(format) == 'undefined') {
38619                 format= 'htmlEncode';
38620             }
38621             if (format == 'raw' ) {
38622                 format = false;
38623             }
38624             
38625             if(name.substr(0, 4) == 'xtpl'){
38626                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38627             }
38628             
38629             // build an array of options to determine if value is undefined..
38630             
38631             // basically get 'xxxx.yyyy' then do
38632             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38633             //    (function () { Roo.log("Property not found"); return ''; })() :
38634             //    ......
38635             
38636             var udef_ar = [];
38637             var lookfor = '';
38638             Roo.each(name.split('.'), function(st) {
38639                 lookfor += (lookfor.length ? '.': '') + st;
38640                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38641             });
38642             
38643             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38644             
38645             
38646             if(format && useF){
38647                 
38648                 args = args ? ',' + args : "";
38649                  
38650                 if(format.substr(0, 5) != "this."){
38651                     format = "fm." + format + '(';
38652                 }else{
38653                     format = 'this.call("'+ format.substr(5) + '", ';
38654                     args = ", values";
38655                 }
38656                 
38657                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38658             }
38659              
38660             if (args.length) {
38661                 // called with xxyx.yuu:(test,test)
38662                 // change to ()
38663                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38664             }
38665             // raw.. - :raw modifier..
38666             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38667             
38668         };
38669         var body;
38670         // branched to use + in gecko and [].join() in others
38671         if(Roo.isGecko){
38672             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38673                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38674                     "';};};";
38675         }else{
38676             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38677             body.push(tpl.body.replace(/(\r\n|\n)/g,
38678                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38679             body.push("'].join('');};};");
38680             body = body.join('');
38681         }
38682         
38683         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38684        
38685         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38686         eval(body);
38687         
38688         return this;
38689     },
38690
38691     applyTemplate : function(values){
38692         return this.master.compiled.call(this, values, {});
38693         //var s = this.subs;
38694     },
38695
38696     apply : function(){
38697         return this.applyTemplate.apply(this, arguments);
38698     }
38699
38700  });
38701
38702 Roo.XTemplate.from = function(el){
38703     el = Roo.getDom(el);
38704     return new Roo.XTemplate(el.value || el.innerHTML);
38705 };/*
38706  * Original code for Roojs - LGPL
38707  * <script type="text/javascript">
38708  */
38709  
38710 /**
38711  * @class Roo.XComponent
38712  * A delayed Element creator...
38713  * Or a way to group chunks of interface together.
38714  * 
38715  * Mypart.xyx = new Roo.XComponent({
38716
38717     parent : 'Mypart.xyz', // empty == document.element.!!
38718     order : '001',
38719     name : 'xxxx'
38720     region : 'xxxx'
38721     disabled : function() {} 
38722      
38723     tree : function() { // return an tree of xtype declared components
38724         var MODULE = this;
38725         return 
38726         {
38727             xtype : 'NestedLayoutPanel',
38728             // technicall
38729         }
38730      ]
38731  *})
38732  *
38733  *
38734  * It can be used to build a big heiracy, with parent etc.
38735  * or you can just use this to render a single compoent to a dom element
38736  * MYPART.render(Roo.Element | String(id) | dom_element )
38737  * 
38738  * @extends Roo.util.Observable
38739  * @constructor
38740  * @param cfg {Object} configuration of component
38741  * 
38742  */
38743 Roo.XComponent = function(cfg) {
38744     Roo.apply(this, cfg);
38745     this.addEvents({ 
38746         /**
38747              * @event built
38748              * Fires when this the componnt is built
38749              * @param {Roo.XComponent} c the component
38750              */
38751         'built' : true
38752         
38753     });
38754     this.region = this.region || 'center'; // default..
38755     Roo.XComponent.register(this);
38756     this.modules = false;
38757     this.el = false; // where the layout goes..
38758     
38759     
38760 }
38761 Roo.extend(Roo.XComponent, Roo.util.Observable, {
38762     /**
38763      * @property el
38764      * The created element (with Roo.factory())
38765      * @type {Roo.Layout}
38766      */
38767     el  : false,
38768     
38769     /**
38770      * @property el
38771      * for BC  - use el in new code
38772      * @type {Roo.Layout}
38773      */
38774     panel : false,
38775     
38776     /**
38777      * @property layout
38778      * for BC  - use el in new code
38779      * @type {Roo.Layout}
38780      */
38781     layout : false,
38782     
38783      /**
38784      * @cfg {Function|boolean} disabled
38785      * If this module is disabled by some rule, return true from the funtion
38786      */
38787     disabled : false,
38788     
38789     /**
38790      * @cfg {String} parent 
38791      * Name of parent element which it get xtype added to..
38792      */
38793     parent: false,
38794     
38795     /**
38796      * @cfg {String} order
38797      * Used to set the order in which elements are created (usefull for multiple tabs)
38798      */
38799     
38800     order : false,
38801     /**
38802      * @cfg {String} name
38803      * String to display while loading.
38804      */
38805     name : false,
38806     /**
38807      * @cfg {String} region
38808      * Region to render component to (defaults to center)
38809      */
38810     region : 'center',
38811     
38812     /**
38813      * @cfg {Array} items
38814      * A single item array - the first element is the root of the tree..
38815      * It's done this way to stay compatible with the Xtype system...
38816      */
38817     items : false,
38818     
38819     /**
38820      * @property _tree
38821      * The method that retuns the tree of parts that make up this compoennt 
38822      * @type {function}
38823      */
38824     _tree  : false,
38825     
38826      /**
38827      * render
38828      * render element to dom or tree
38829      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
38830      */
38831     
38832     render : function(el)
38833     {
38834         
38835         el = el || false;
38836         var hp = this.parent ? 1 : 0;
38837         
38838         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
38839             // if parent is a '#.....' string, then let's use that..
38840             var ename = this.parent.substr(1)
38841             this.parent = false;
38842             el = Roo.get(ename);
38843             if (!el) {
38844                 Roo.log("Warning - element can not be found :#" + ename );
38845                 return;
38846             }
38847         }
38848         
38849         
38850         if (!this.parent) {
38851             
38852             el = el ? Roo.get(el) : false;      
38853             
38854             // it's a top level one..
38855             this.parent =  {
38856                 el : new Roo.BorderLayout(el || document.body, {
38857                 
38858                      center: {
38859                          titlebar: false,
38860                          autoScroll:false,
38861                          closeOnTab: true,
38862                          tabPosition: 'top',
38863                           //resizeTabs: true,
38864                          alwaysShowTabs: el && hp? false :  true,
38865                          hideTabs: el || !hp ? true :  false,
38866                          minTabWidth: 140
38867                      }
38868                  })
38869             }
38870         }
38871         
38872                 if (!this.parent.el) {
38873                         // probably an old style ctor, which has been disabled.
38874                         return;
38875                         
38876                 }
38877                 // The 'tree' method is  '_tree now' 
38878             
38879         var tree = this._tree ? this._tree() : this.tree();
38880         tree.region = tree.region || this.region;
38881         this.el = this.parent.el.addxtype(tree);
38882         this.fireEvent('built', this);
38883         
38884         this.panel = this.el;
38885         this.layout = this.panel.layout;
38886                 this.parentLayout = this.parent.layout  || false;  
38887          
38888     }
38889     
38890 });
38891
38892 Roo.apply(Roo.XComponent, {
38893     /**
38894      * @property  hideProgress
38895      * true to disable the building progress bar.. usefull on single page renders.
38896      * @type Boolean
38897      */
38898     hideProgress : false,
38899     /**
38900      * @property  buildCompleted
38901      * True when the builder has completed building the interface.
38902      * @type Boolean
38903      */
38904     buildCompleted : false,
38905      
38906     /**
38907      * @property  topModule
38908      * the upper most module - uses document.element as it's constructor.
38909      * @type Object
38910      */
38911      
38912     topModule  : false,
38913       
38914     /**
38915      * @property  modules
38916      * array of modules to be created by registration system.
38917      * @type {Array} of Roo.XComponent
38918      */
38919     
38920     modules : [],
38921     /**
38922      * @property  elmodules
38923      * array of modules to be created by which use #ID 
38924      * @type {Array} of Roo.XComponent
38925      */
38926      
38927     elmodules : [],
38928
38929     
38930     /**
38931      * Register components to be built later.
38932      *
38933      * This solves the following issues
38934      * - Building is not done on page load, but after an authentication process has occured.
38935      * - Interface elements are registered on page load
38936      * - Parent Interface elements may not be loaded before child, so this handles that..
38937      * 
38938      *
38939      * example:
38940      * 
38941      * MyApp.register({
38942           order : '000001',
38943           module : 'Pman.Tab.projectMgr',
38944           region : 'center',
38945           parent : 'Pman.layout',
38946           disabled : false,  // or use a function..
38947         })
38948      
38949      * * @param {Object} details about module
38950      */
38951     register : function(obj) {
38952                 
38953         Roo.XComponent.event.fireEvent('register', obj);
38954         switch(typeof(obj.disabled) ) {
38955                 
38956             case 'undefined':
38957                 break;
38958             
38959             case 'function':
38960                 if ( obj.disabled() ) {
38961                         return;
38962                 }
38963                 break;
38964             
38965             default:
38966                 if (obj.disabled) {
38967                         return;
38968                 }
38969                 break;
38970         }
38971                 
38972         this.modules.push(obj);
38973          
38974     },
38975     /**
38976      * convert a string to an object..
38977      * eg. 'AAA.BBB' -> finds AAA.BBB
38978
38979      */
38980     
38981     toObject : function(str)
38982     {
38983         if (!str || typeof(str) == 'object') {
38984             return str;
38985         }
38986         if (str.substring(0,1) == '#') {
38987             return str;
38988         }
38989
38990         var ar = str.split('.');
38991         var rt, o;
38992         rt = ar.shift();
38993             /** eval:var:o */
38994         try {
38995             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
38996         } catch (e) {
38997             throw "Module not found : " + str;
38998         }
38999         
39000         if (o === false) {
39001             throw "Module not found : " + str;
39002         }
39003         Roo.each(ar, function(e) {
39004             if (typeof(o[e]) == 'undefined') {
39005                 throw "Module not found : " + str;
39006             }
39007             o = o[e];
39008         });
39009         
39010         return o;
39011         
39012     },
39013     
39014     
39015     /**
39016      * move modules into their correct place in the tree..
39017      * 
39018      */
39019     preBuild : function ()
39020     {
39021         var _t = this;
39022         Roo.each(this.modules , function (obj)
39023         {
39024             Roo.XComponent.event.fireEvent('beforebuild', obj);
39025             
39026             var opar = obj.parent;
39027             try { 
39028                 obj.parent = this.toObject(opar);
39029             } catch(e) {
39030                 Roo.log("parent:toObject failed: " + e.toString());
39031                 return;
39032             }
39033             
39034             if (!obj.parent) {
39035                 Roo.debug && Roo.log("GOT top level module");
39036                 Roo.debug && Roo.log(obj);
39037                 obj.modules = new Roo.util.MixedCollection(false, 
39038                     function(o) { return o.order + '' }
39039                 );
39040                 this.topModule = obj;
39041                 return;
39042             }
39043                         // parent is a string (usually a dom element name..)
39044             if (typeof(obj.parent) == 'string') {
39045                 this.elmodules.push(obj);
39046                 return;
39047             }
39048             if (obj.parent.constructor != Roo.XComponent) {
39049                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
39050             }
39051             if (!obj.parent.modules) {
39052                 obj.parent.modules = new Roo.util.MixedCollection(false, 
39053                     function(o) { return o.order + '' }
39054                 );
39055             }
39056             if (obj.parent.disabled) {
39057                 obj.disabled = true;
39058             }
39059             obj.parent.modules.add(obj);
39060         }, this);
39061     },
39062     
39063      /**
39064      * make a list of modules to build.
39065      * @return {Array} list of modules. 
39066      */ 
39067     
39068     buildOrder : function()
39069     {
39070         var _this = this;
39071         var cmp = function(a,b) {   
39072             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
39073         };
39074         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
39075             throw "No top level modules to build";
39076         }
39077         
39078         // make a flat list in order of modules to build.
39079         var mods = this.topModule ? [ this.topModule ] : [];
39080                 
39081         // elmodules (is a list of DOM based modules )
39082         Roo.each(this.elmodules, function(e) {
39083             mods.push(e)
39084         });
39085
39086         
39087         // add modules to their parents..
39088         var addMod = function(m) {
39089             Roo.debug && Roo.log("build Order: add: " + m.name);
39090             
39091         mods.push(m);
39092         if (m.modules && !m.disabled) {
39093             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
39094             m.modules.keySort('ASC',  cmp );
39095             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
39096
39097             m.modules.each(addMod);
39098         } else {
39099             Roo.debug && Roo.log("build Order: no child modules");
39100             }
39101             // not sure if this is used any more..
39102             if (m.finalize) {
39103                 m.finalize.name = m.name + " (clean up) ";
39104                 mods.push(m.finalize);
39105             }
39106             
39107         }
39108         if (this.topModule) { 
39109             this.topModule.modules.keySort('ASC',  cmp );
39110             this.topModule.modules.each(addMod);
39111         }
39112         return mods;
39113     },
39114     
39115      /**
39116      * Build the registered modules.
39117      * @param {Object} parent element.
39118      * @param {Function} optional method to call after module has been added.
39119      * 
39120      */ 
39121    
39122     build : function() 
39123     {
39124         
39125         this.preBuild();
39126         var mods = this.buildOrder();
39127       
39128         //this.allmods = mods;
39129         //Roo.debug && Roo.log(mods);
39130         //return;
39131         if (!mods.length) { // should not happen
39132             throw "NO modules!!!";
39133         }
39134         
39135         
39136         var msg = "Building Interface...";
39137         // flash it up as modal - so we store the mask!?
39138         if (!this.hideProgress) {
39139             Roo.MessageBox.show({ title: 'loading' });
39140             Roo.MessageBox.show({
39141                title: "Please wait...",
39142                msg: msg,
39143                width:450,
39144                progress:true,
39145                closable:false,
39146                modal: false
39147               
39148             });
39149         }
39150         var total = mods.length;
39151         
39152         var _this = this;
39153         var progressRun = function() {
39154             if (!mods.length) {
39155                 Roo.debug && Roo.log('hide?');
39156                 if (!this.hideProgress) {
39157                     Roo.MessageBox.hide();
39158                 }
39159                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
39160                 
39161                 // THE END...
39162                 return false;   
39163             }
39164             
39165             var m = mods.shift();
39166             
39167             
39168             Roo.debug && Roo.log(m);
39169             // not sure if this is supported any more.. - modules that are are just function
39170             if (typeof(m) == 'function') { 
39171                 m.call(this);
39172                 return progressRun.defer(10, _this);
39173             } 
39174             
39175             
39176             msg = "Building Interface " + (total  - mods.length) + 
39177                     " of " + total + 
39178                     (m.name ? (' - ' + m.name) : '');
39179                         Roo.debug && Roo.log(msg);
39180             if (!this.hideProgress) { 
39181                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
39182             }
39183             
39184          
39185             // is the module disabled?
39186             var disabled = (typeof(m.disabled) == 'function') ?
39187                 m.disabled.call(m.module.disabled) : m.disabled;    
39188             
39189             
39190             if (disabled) {
39191                 return progressRun(); // we do not update the display!
39192             }
39193             
39194             // now build 
39195             
39196                         
39197                         
39198             m.render();
39199             // it's 10 on top level, and 1 on others??? why...
39200             return progressRun.defer(10, _this);
39201              
39202         }
39203         progressRun.defer(1, _this);
39204      
39205         
39206         
39207     },
39208         
39209         
39210         /**
39211          * Event Object.
39212          *
39213          *
39214          */
39215         event: false, 
39216     /**
39217          * wrapper for event.on - aliased later..  
39218          * Typically use to register a event handler for register:
39219          *
39220          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
39221          *
39222          */
39223     on : false
39224    
39225     
39226     
39227 });
39228
39229 Roo.XComponent.event = new Roo.util.Observable({
39230                 events : { 
39231                         /**
39232                          * @event register
39233                          * Fires when an Component is registered,
39234                          * set the disable property on the Component to stop registration.
39235                          * @param {Roo.XComponent} c the component being registerd.
39236                          * 
39237                          */
39238                         'register' : true,
39239             /**
39240                          * @event beforebuild
39241                          * Fires before each Component is built
39242                          * can be used to apply permissions.
39243                          * @param {Roo.XComponent} c the component being registerd.
39244                          * 
39245                          */
39246                         'beforebuild' : true,
39247                         /**
39248                          * @event buildcomplete
39249                          * Fires on the top level element when all elements have been built
39250                          * @param {Roo.XComponent} the top level component.
39251                          */
39252                         'buildcomplete' : true
39253                         
39254                 }
39255 });
39256
39257 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
39258  //<script type="text/javascript">
39259
39260
39261 /**
39262  * @class Roo.Login
39263  * @extends Roo.LayoutDialog
39264  * A generic Login Dialog..... - only one needed in theory!?!?
39265  *
39266  * Fires XComponent builder on success...
39267  * 
39268  * Sends 
39269  *    username,password, lang = for login actions.
39270  *    check = 1 for periodic checking that sesion is valid.
39271  *    passwordRequest = email request password
39272  *    logout = 1 = to logout
39273  * 
39274  * Affects: (this id="????" elements)
39275  *   loading  (removed) (used to indicate application is loading)
39276  *   loading-mask (hides) (used to hide application when it's building loading)
39277  *   
39278  * 
39279  * Usage: 
39280  *    
39281  * 
39282  * Myapp.login = Roo.Login({
39283      url: xxxx,
39284    
39285      realm : 'Myapp', 
39286      
39287      
39288      method : 'POST',
39289      
39290      
39291      * 
39292  })
39293  * 
39294  * 
39295  * 
39296  **/
39297  
39298 Roo.Login = function(cfg)
39299 {
39300     this.addEvents({
39301         'refreshed' : true
39302     });
39303     
39304     Roo.apply(this,cfg);
39305     
39306     Roo.onReady(function() {
39307         this.onLoad();
39308     }, this);
39309     // call parent..
39310     
39311    
39312     Roo.Login.superclass.constructor.call(this, this);
39313     //this.addxtype(this.items[0]);
39314     
39315     
39316 }
39317
39318
39319 Roo.extend(Roo.Login, Roo.LayoutDialog, {
39320     
39321     /**
39322      * @cfg {String} method
39323      * Method used to query for login details.
39324      */
39325     
39326     method : 'POST',
39327     /**
39328      * @cfg {String} url
39329      * URL to query login data. - eg. baseURL + '/Login.php'
39330      */
39331     url : '',
39332     
39333     /**
39334      * @property user
39335      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
39336      * @type {Object} 
39337      */
39338     user : false,
39339     /**
39340      * @property checkFails
39341      * Number of times we have attempted to get authentication check, and failed.
39342      * @type {Number} 
39343      */
39344     checkFails : 0,
39345       /**
39346      * @property intervalID
39347      * The window interval that does the constant login checking.
39348      * @type {Number} 
39349      */
39350     intervalID : 0,
39351     
39352     
39353     onLoad : function() // called on page load...
39354     {
39355         // load 
39356          
39357         if (Roo.get('loading')) { // clear any loading indicator..
39358             Roo.get('loading').remove();
39359         }
39360         
39361         //this.switchLang('en'); // set the language to english..
39362        
39363         this.check({
39364             success:  function(response, opts)  {  // check successfull...
39365             
39366                 var res = this.processResponse(response);
39367                 this.checkFails =0;
39368                 if (!res.success) { // error!
39369                     this.checkFails = 5;
39370                     //console.log('call failure');
39371                     return this.failure(response,opts);
39372                 }
39373                 
39374                 if (!res.data.id) { // id=0 == login failure.
39375                     return this.show();
39376                 }
39377                 
39378                               
39379                         //console.log(success);
39380                 this.fillAuth(res.data);   
39381                 this.checkFails =0;
39382                 Roo.XComponent.build();
39383             },
39384             failure : this.show
39385         });
39386         
39387     }, 
39388     
39389     
39390     check: function(cfg) // called every so often to refresh cookie etc..
39391     {
39392         if (cfg.again) { // could be undefined..
39393             this.checkFails++;
39394         } else {
39395             this.checkFails = 0;
39396         }
39397         var _this = this;
39398         if (this.sending) {
39399             if ( this.checkFails > 4) {
39400                 Roo.MessageBox.alert("Error",  
39401                     "Error getting authentication status. - try reloading, or wait a while", function() {
39402                         _this.sending = false;
39403                     }); 
39404                 return;
39405             }
39406             cfg.again = true;
39407             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
39408             return;
39409         }
39410         this.sending = true;
39411         
39412         Roo.Ajax.request({  
39413             url: this.url,
39414             params: {
39415                 getAuthUser: true
39416             },  
39417             method: this.method,
39418             success:  cfg.success || this.success,
39419             failure : cfg.failure || this.failure,
39420             scope : this,
39421             callCfg : cfg
39422               
39423         });  
39424     }, 
39425     
39426     
39427     logout: function()
39428     {
39429         window.onbeforeunload = function() { }; // false does not work for IE..
39430         this.user = false;
39431         var _this = this;
39432         
39433         Roo.Ajax.request({  
39434             url: this.url,
39435             params: {
39436                 logout: 1
39437             },  
39438             method: 'GET',
39439             failure : function() {
39440                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
39441                     document.location = document.location.toString() + '?ts=' + Math.random();
39442                 });
39443                 
39444             },
39445             success : function() {
39446                 _this.user = false;
39447                 this.checkFails =0;
39448                 // fixme..
39449                 document.location = document.location.toString() + '?ts=' + Math.random();
39450             }
39451               
39452               
39453         }); 
39454     },
39455     
39456     processResponse : function (response)
39457     {
39458         var res = '';
39459         try {
39460             res = Roo.decode(response.responseText);
39461             // oops...
39462             if (typeof(res) != 'object') {
39463                 res = { success : false, errorMsg : res, errors : true };
39464             }
39465             if (typeof(res.success) == 'undefined') {
39466                 res.success = false;
39467             }
39468             
39469         } catch(e) {
39470             res = { success : false,  errorMsg : response.responseText, errors : true };
39471         }
39472         return res;
39473     },
39474     
39475     success : function(response, opts)  // check successfull...
39476     {  
39477         this.sending = false;
39478         var res = this.processResponse(response);
39479         if (!res.success) {
39480             return this.failure(response, opts);
39481         }
39482         if (!res.data || !res.data.id) {
39483             return this.failure(response,opts);
39484         }
39485         //console.log(res);
39486         this.fillAuth(res.data);
39487         
39488         this.checkFails =0;
39489         
39490     },
39491     
39492     
39493     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
39494     {
39495         this.authUser = -1;
39496         this.sending = false;
39497         var res = this.processResponse(response);
39498         //console.log(res);
39499         if ( this.checkFails > 2) {
39500         
39501             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
39502                 "Error getting authentication status. - try reloading"); 
39503             return;
39504         }
39505         opts.callCfg.again = true;
39506         this.check.defer(1000, this, [ opts.callCfg ]);
39507         return;  
39508     },
39509     
39510     
39511     
39512     fillAuth: function(au) {
39513         this.startAuthCheck();
39514         this.authUserId = au.id;
39515         this.authUser = au;
39516         this.lastChecked = new Date();
39517         this.fireEvent('refreshed', au);
39518         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
39519         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
39520         au.lang = au.lang || 'en';
39521         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
39522         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
39523         this.switchLang(au.lang );
39524         
39525      
39526         // open system... - -on setyp..
39527         if (this.authUserId  < 0) {
39528             Roo.MessageBox.alert("Warning", 
39529                 "This is an open system - please set up a admin user with a password.");  
39530         }
39531          
39532         //Pman.onload(); // which should do nothing if it's a re-auth result...
39533         
39534              
39535     },
39536     
39537     startAuthCheck : function() // starter for timeout checking..
39538     {
39539         if (this.intervalID) { // timer already in place...
39540             return false;
39541         }
39542         var _this = this;
39543         this.intervalID =  window.setInterval(function() {
39544               _this.check(false);
39545             }, 120000); // every 120 secs = 2mins..
39546         
39547         
39548     },
39549          
39550     
39551     switchLang : function (lang) 
39552     {
39553         _T = typeof(_T) == 'undefined' ? false : _T;
39554           if (!_T || !lang.length) {
39555             return;
39556         }
39557         
39558         if (!_T && lang != 'en') {
39559             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39560             return;
39561         }
39562         
39563         if (typeof(_T.en) == 'undefined') {
39564             _T.en = {};
39565             Roo.apply(_T.en, _T);
39566         }
39567         
39568         if (typeof(_T[lang]) == 'undefined') {
39569             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
39570             return;
39571         }
39572         
39573         
39574         Roo.apply(_T, _T[lang]);
39575         // just need to set the text values for everything...
39576         var _this = this;
39577         /* this will not work ...
39578         if (this.form) { 
39579             
39580                
39581             function formLabel(name, val) {
39582                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
39583             }
39584             
39585             formLabel('password', "Password"+':');
39586             formLabel('username', "Email Address"+':');
39587             formLabel('lang', "Language"+':');
39588             this.dialog.setTitle("Login");
39589             this.dialog.buttons[0].setText("Forgot Password");
39590             this.dialog.buttons[1].setText("Login");
39591         }
39592         */
39593         
39594         
39595     },
39596     
39597     
39598     title: "Login",
39599     modal: true,
39600     width:  350,
39601     //height: 230,
39602     height: 180,
39603     shadow: true,
39604     minWidth:200,
39605     minHeight:180,
39606     //proxyDrag: true,
39607     closable: false,
39608     draggable: false,
39609     collapsible: false,
39610     resizable: false,
39611     center: {  // needed??
39612         autoScroll:false,
39613         titlebar: false,
39614        // tabPosition: 'top',
39615         hideTabs: true,
39616         closeOnTab: true,
39617         alwaysShowTabs: false
39618     } ,
39619     listeners : {
39620         
39621         show  : function(dlg)
39622         {
39623             //console.log(this);
39624             this.form = this.layout.getRegion('center').activePanel.form;
39625             this.form.dialog = dlg;
39626             this.buttons[0].form = this.form;
39627             this.buttons[0].dialog = dlg;
39628             this.buttons[1].form = this.form;
39629             this.buttons[1].dialog = dlg;
39630            
39631            //this.resizeToLogo.defer(1000,this);
39632             // this is all related to resizing for logos..
39633             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
39634            //// if (!sz) {
39635              //   this.resizeToLogo.defer(1000,this);
39636              //   return;
39637            // }
39638             //var w = Ext.lib.Dom.getViewWidth() - 100;
39639             //var h = Ext.lib.Dom.getViewHeight() - 100;
39640             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
39641             //this.center();
39642             if (this.disabled) {
39643                 this.hide();
39644                 return;
39645             }
39646             
39647             if (this.user.id < 0) { // used for inital setup situations.
39648                 return;
39649             }
39650             
39651             if (this.intervalID) {
39652                 // remove the timer
39653                 window.clearInterval(this.intervalID);
39654                 this.intervalID = false;
39655             }
39656             
39657             
39658             if (Roo.get('loading')) {
39659                 Roo.get('loading').remove();
39660             }
39661             if (Roo.get('loading-mask')) {
39662                 Roo.get('loading-mask').hide();
39663             }
39664             
39665             //incomming._node = tnode;
39666             this.form.reset();
39667             //this.dialog.modal = !modal;
39668             //this.dialog.show();
39669             this.el.unmask(); 
39670             
39671             
39672             this.form.setValues({
39673                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
39674                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
39675             });
39676             
39677             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
39678             if (this.form.findField('username').getValue().length > 0 ){
39679                 this.form.findField('password').focus();
39680             } else {
39681                this.form.findField('username').focus();
39682             }
39683     
39684         }
39685     },
39686     items : [
39687          {
39688        
39689             xtype : 'ContentPanel',
39690             xns : Roo,
39691             region: 'center',
39692             fitToFrame : true,
39693             
39694             items : [
39695     
39696                 {
39697                
39698                     xtype : 'Form',
39699                     xns : Roo.form,
39700                     labelWidth: 100,
39701                     style : 'margin: 10px;',
39702                     
39703                     listeners : {
39704                         actionfailed : function(f, act) {
39705                             // form can return { errors: .... }
39706                                 
39707                             //act.result.errors // invalid form element list...
39708                             //act.result.errorMsg// invalid form element list...
39709                             
39710                             this.dialog.el.unmask();
39711                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
39712                                         "Login failed - communication error - try again.");
39713                                       
39714                         },
39715                         actioncomplete: function(re, act) {
39716                              
39717                             Roo.state.Manager.set(
39718                                 this.dialog.realm + '.username',  
39719                                     this.findField('username').getValue()
39720                             );
39721                             Roo.state.Manager.set(
39722                                 this.dialog.realm + '.lang',  
39723                                 this.findField('lang').getValue() 
39724                             );
39725                             
39726                             this.dialog.fillAuth(act.result.data);
39727                               
39728                             this.dialog.hide();
39729                             
39730                             if (Roo.get('loading-mask')) {
39731                                 Roo.get('loading-mask').show();
39732                             }
39733                             Roo.XComponent.build();
39734                             
39735                              
39736                             
39737                         }
39738                     },
39739                     items : [
39740                         {
39741                             xtype : 'TextField',
39742                             xns : Roo.form,
39743                             fieldLabel: "Email Address",
39744                             name: 'username',
39745                             width:200,
39746                             autoCreate : {tag: "input", type: "text", size: "20"}
39747                         },
39748                         {
39749                             xtype : 'TextField',
39750                             xns : Roo.form,
39751                             fieldLabel: "Password",
39752                             inputType: 'password',
39753                             name: 'password',
39754                             width:200,
39755                             autoCreate : {tag: "input", type: "text", size: "20"},
39756                             listeners : {
39757                                 specialkey : function(e,ev) {
39758                                     if (ev.keyCode == 13) {
39759                                         this.form.dialog.el.mask("Logging in");
39760                                         this.form.doAction('submit', {
39761                                             url: this.form.dialog.url,
39762                                             method: this.form.dialog.method
39763                                         });
39764                                     }
39765                                 }
39766                             }  
39767                         },
39768                         {
39769                             xtype : 'ComboBox',
39770                             xns : Roo.form,
39771                             fieldLabel: "Language",
39772                             name : 'langdisp',
39773                             store: {
39774                                 xtype : 'SimpleStore',
39775                                 fields: ['lang', 'ldisp'],
39776                                 data : [
39777                                     [ 'en', 'English' ],
39778                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
39779                                     [ 'zh_CN', '\u7C21\u4E2D' ]
39780                                 ]
39781                             },
39782                             
39783                             valueField : 'lang',
39784                             hiddenName:  'lang',
39785                             width: 200,
39786                             displayField:'ldisp',
39787                             typeAhead: false,
39788                             editable: false,
39789                             mode: 'local',
39790                             triggerAction: 'all',
39791                             emptyText:'Select a Language...',
39792                             selectOnFocus:true,
39793                             listeners : {
39794                                 select :  function(cb, rec, ix) {
39795                                     this.form.switchLang(rec.data.lang);
39796                                 }
39797                             }
39798                         
39799                         }
39800                     ]
39801                 }
39802                   
39803                 
39804             ]
39805         }
39806     ],
39807     buttons : [
39808         {
39809             xtype : 'Button',
39810             xns : 'Roo',
39811             text : "Forgot Password",
39812             listeners : {
39813                 click : function() {
39814                     //console.log(this);
39815                     var n = this.form.findField('username').getValue();
39816                     if (!n.length) {
39817                         Roo.MessageBox.alert("Error", "Fill in your email address");
39818                         return;
39819                     }
39820                     Roo.Ajax.request({
39821                         url: this.dialog.url,
39822                         params: {
39823                             passwordRequest: n
39824                         },
39825                         method: this.dialog.method,
39826                         success:  function(response, opts)  {  // check successfull...
39827                         
39828                             var res = this.dialog.processResponse(response);
39829                             if (!res.success) { // error!
39830                                Roo.MessageBox.alert("Error" ,
39831                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
39832                                return;
39833                             }
39834                             Roo.MessageBox.alert("Notice" ,
39835                                 "Please check you email for the Password Reset message");
39836                         },
39837                         failure : function() {
39838                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
39839                         }
39840                         
39841                     });
39842                 }
39843             }
39844         },
39845         {
39846             xtype : 'Button',
39847             xns : 'Roo',
39848             text : "Login",
39849             listeners : {
39850                 
39851                 click : function () {
39852                         
39853                     this.dialog.el.mask("Logging in");
39854                     this.form.doAction('submit', {
39855                             url: this.dialog.url,
39856                             method: this.dialog.method
39857                     });
39858                 }
39859             }
39860         }
39861     ]
39862   
39863   
39864 })
39865  
39866
39867
39868