roojs-core.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     
3044     
3045     var onStop = function(e){
3046         dragEl = null;
3047         clearProc();
3048     };
3049     
3050     var triggerRefresh = function(){
3051         if(ddm.dragCurrent){
3052              ddm.refreshCache(ddm.dragCurrent.groups);
3053         }
3054     };
3055     
3056     var doScroll = function(){
3057         if(ddm.dragCurrent){
3058             var dds = Roo.dd.ScrollManager;
3059             if(!dds.animate){
3060                 if(proc.el.scroll(proc.dir, dds.increment)){
3061                     triggerRefresh();
3062                 }
3063             }else{
3064                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3065             }
3066         }
3067     };
3068     
3069     var clearProc = function(){
3070         if(proc.id){
3071             clearInterval(proc.id);
3072         }
3073         proc.id = 0;
3074         proc.el = null;
3075         proc.dir = "";
3076     };
3077     
3078     var startProc = function(el, dir){
3079          Roo.log('scroll startproc');
3080         clearProc();
3081         proc.el = el;
3082         proc.dir = dir;
3083         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3084     };
3085     
3086     var onFire = function(e, isDrop){
3087        
3088         if(isDrop || !ddm.dragCurrent){ return; }
3089         var dds = Roo.dd.ScrollManager;
3090         if(!dragEl || dragEl != ddm.dragCurrent){
3091             dragEl = ddm.dragCurrent;
3092             // refresh regions on drag start
3093             dds.refreshCache();
3094         }
3095         
3096         var xy = Roo.lib.Event.getXY(e);
3097         var pt = new Roo.lib.Point(xy[0], xy[1]);
3098         for(var id in els){
3099             var el = els[id], r = el._region;
3100             if(r && r.contains(pt) && el.isScrollable()){
3101                 if(r.bottom - pt.y <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "down");
3104                     }
3105                     return;
3106                 }else if(r.right - pt.x <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "left");
3109                     }
3110                     return;
3111                 }else if(pt.y - r.top <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "up");
3114                     }
3115                     return;
3116                 }else if(pt.x - r.left <= dds.thresh){
3117                     if(proc.el != el){
3118                         startProc(el, "right");
3119                     }
3120                     return;
3121                 }
3122             }
3123         }
3124         clearProc();
3125     };
3126     
3127     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3128     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3129     
3130     return {
3131         /**
3132          * Registers new overflow element(s) to auto scroll
3133          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3134          */
3135         register : function(el){
3136             if(el instanceof Array){
3137                 for(var i = 0, len = el.length; i < len; i++) {
3138                         this.register(el[i]);
3139                 }
3140             }else{
3141                 el = Roo.get(el);
3142                 els[el.id] = el;
3143             }
3144             Roo.dd.ScrollManager.els = els;
3145         },
3146         
3147         /**
3148          * Unregisters overflow element(s) so they are no longer scrolled
3149          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3150          */
3151         unregister : function(el){
3152             if(el instanceof Array){
3153                 for(var i = 0, len = el.length; i < len; i++) {
3154                         this.unregister(el[i]);
3155                 }
3156             }else{
3157                 el = Roo.get(el);
3158                 delete els[el.id];
3159             }
3160         },
3161         
3162         /**
3163          * The number of pixels from the edge of a container the pointer needs to be to 
3164          * trigger scrolling (defaults to 25)
3165          * @type Number
3166          */
3167         thresh : 25,
3168         
3169         /**
3170          * The number of pixels to scroll in each scroll increment (defaults to 50)
3171          * @type Number
3172          */
3173         increment : 100,
3174         
3175         /**
3176          * The frequency of scrolls in milliseconds (defaults to 500)
3177          * @type Number
3178          */
3179         frequency : 500,
3180         
3181         /**
3182          * True to animate the scroll (defaults to true)
3183          * @type Boolean
3184          */
3185         animate: true,
3186         
3187         /**
3188          * The animation duration in seconds - 
3189          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3190          * @type Number
3191          */
3192         animDuration: .4,
3193         
3194         /**
3195          * Manually trigger a cache refresh.
3196          */
3197         refreshCache : function(){
3198             for(var id in els){
3199                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3200                     els[id]._region = els[id].getRegion();
3201                 }
3202             }
3203         }
3204     };
3205 }();/*
3206  * Based on:
3207  * Ext JS Library 1.1.1
3208  * Copyright(c) 2006-2007, Ext JS, LLC.
3209  *
3210  * Originally Released Under LGPL - original licence link has changed is not relivant.
3211  *
3212  * Fork - LGPL
3213  * <script type="text/javascript">
3214  */
3215  
3216
3217 /**
3218  * @class Roo.dd.Registry
3219  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3220  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3221  * @singleton
3222  */
3223 Roo.dd.Registry = function(){
3224     var elements = {}; 
3225     var handles = {}; 
3226     var autoIdSeed = 0;
3227
3228     var getId = function(el, autogen){
3229         if(typeof el == "string"){
3230             return el;
3231         }
3232         var id = el.id;
3233         if(!id && autogen !== false){
3234             id = "roodd-" + (++autoIdSeed);
3235             el.id = id;
3236         }
3237         return id;
3238     };
3239     
3240     return {
3241     /**
3242      * Register a drag drop element
3243      * @param {String|HTMLElement} element The id or DOM node to register
3244      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3245      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3246      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3247      * populated in the data object (if applicable):
3248      * <pre>
3249 Value      Description<br />
3250 ---------  ------------------------------------------<br />
3251 handles    Array of DOM nodes that trigger dragging<br />
3252            for the element being registered<br />
3253 isHandle   True if the element passed in triggers<br />
3254            dragging itself, else false
3255 </pre>
3256      */
3257         register : function(el, data){
3258             data = data || {};
3259             if(typeof el == "string"){
3260                 el = document.getElementById(el);
3261             }
3262             data.ddel = el;
3263             elements[getId(el)] = data;
3264             if(data.isHandle !== false){
3265                 handles[data.ddel.id] = data;
3266             }
3267             if(data.handles){
3268                 var hs = data.handles;
3269                 for(var i = 0, len = hs.length; i < len; i++){
3270                         handles[getId(hs[i])] = data;
3271                 }
3272             }
3273         },
3274
3275     /**
3276      * Unregister a drag drop element
3277      * @param {String|HTMLElement}  element The id or DOM node to unregister
3278      */
3279         unregister : function(el){
3280             var id = getId(el, false);
3281             var data = elements[id];
3282             if(data){
3283                 delete elements[id];
3284                 if(data.handles){
3285                     var hs = data.handles;
3286                     for(var i = 0, len = hs.length; i < len; i++){
3287                         delete handles[getId(hs[i], false)];
3288                     }
3289                 }
3290             }
3291         },
3292
3293     /**
3294      * Returns the handle registered for a DOM Node by id
3295      * @param {String|HTMLElement} id The DOM node or id to look up
3296      * @return {Object} handle The custom handle data
3297      */
3298         getHandle : function(id){
3299             if(typeof id != "string"){ // must be element?
3300                 id = id.id;
3301             }
3302             return handles[id];
3303         },
3304
3305     /**
3306      * Returns the handle that is registered for the DOM node that is the target of the event
3307      * @param {Event} e The event
3308      * @return {Object} handle The custom handle data
3309      */
3310         getHandleFromEvent : function(e){
3311             var t = Roo.lib.Event.getTarget(e);
3312             return t ? handles[t.id] : null;
3313         },
3314
3315     /**
3316      * Returns a custom data object that is registered for a DOM node by id
3317      * @param {String|HTMLElement} id The DOM node or id to look up
3318      * @return {Object} data The custom data
3319      */
3320         getTarget : function(id){
3321             if(typeof id != "string"){ // must be element?
3322                 id = id.id;
3323             }
3324             return elements[id];
3325         },
3326
3327     /**
3328      * Returns a custom data object that is registered for the DOM node that is the target of the event
3329      * @param {Event} e The event
3330      * @return {Object} data The custom data
3331      */
3332         getTargetFromEvent : function(e){
3333             var t = Roo.lib.Event.getTarget(e);
3334             return t ? elements[t.id] || handles[t.id] : null;
3335         }
3336     };
3337 }();/*
3338  * Based on:
3339  * Ext JS Library 1.1.1
3340  * Copyright(c) 2006-2007, Ext JS, LLC.
3341  *
3342  * Originally Released Under LGPL - original licence link has changed is not relivant.
3343  *
3344  * Fork - LGPL
3345  * <script type="text/javascript">
3346  */
3347  
3348
3349 /**
3350  * @class Roo.dd.StatusProxy
3351  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3352  * default drag proxy used by all Roo.dd components.
3353  * @constructor
3354  * @param {Object} config
3355  */
3356 Roo.dd.StatusProxy = function(config){
3357     Roo.apply(this, config);
3358     this.id = this.id || Roo.id();
3359     this.el = new Roo.Layer({
3360         dh: {
3361             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3362                 {tag: "div", cls: "x-dd-drop-icon"},
3363                 {tag: "div", cls: "x-dd-drag-ghost"}
3364             ]
3365         }, 
3366         shadow: !config || config.shadow !== false
3367     });
3368     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3369     this.dropStatus = this.dropNotAllowed;
3370 };
3371
3372 Roo.dd.StatusProxy.prototype = {
3373     /**
3374      * @cfg {String} dropAllowed
3375      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3376      */
3377     dropAllowed : "x-dd-drop-ok",
3378     /**
3379      * @cfg {String} dropNotAllowed
3380      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3381      */
3382     dropNotAllowed : "x-dd-drop-nodrop",
3383
3384     /**
3385      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3386      * over the current target element.
3387      * @param {String} cssClass The css class for the new drop status indicator image
3388      */
3389     setStatus : function(cssClass){
3390         cssClass = cssClass || this.dropNotAllowed;
3391         if(this.dropStatus != cssClass){
3392             this.el.replaceClass(this.dropStatus, cssClass);
3393             this.dropStatus = cssClass;
3394         }
3395     },
3396
3397     /**
3398      * Resets the status indicator to the default dropNotAllowed value
3399      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3400      */
3401     reset : function(clearGhost){
3402         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3403         this.dropStatus = this.dropNotAllowed;
3404         if(clearGhost){
3405             this.ghost.update("");
3406         }
3407     },
3408
3409     /**
3410      * Updates the contents of the ghost element
3411      * @param {String} html The html that will replace the current innerHTML of the ghost element
3412      */
3413     update : function(html){
3414         if(typeof html == "string"){
3415             this.ghost.update(html);
3416         }else{
3417             this.ghost.update("");
3418             html.style.margin = "0";
3419             this.ghost.dom.appendChild(html);
3420         }
3421         // ensure float = none set?? cant remember why though.
3422         var el = this.ghost.dom.firstChild;
3423                 if(el){
3424                         Roo.fly(el).setStyle('float', 'none');
3425                 }
3426     },
3427     
3428     /**
3429      * Returns the underlying proxy {@link Roo.Layer}
3430      * @return {Roo.Layer} el
3431     */
3432     getEl : function(){
3433         return this.el;
3434     },
3435
3436     /**
3437      * Returns the ghost element
3438      * @return {Roo.Element} el
3439      */
3440     getGhost : function(){
3441         return this.ghost;
3442     },
3443
3444     /**
3445      * Hides the proxy
3446      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3447      */
3448     hide : function(clear){
3449         this.el.hide();
3450         if(clear){
3451             this.reset(true);
3452         }
3453     },
3454
3455     /**
3456      * Stops the repair animation if it's currently running
3457      */
3458     stop : function(){
3459         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3460             this.anim.stop();
3461         }
3462     },
3463
3464     /**
3465      * Displays this proxy
3466      */
3467     show : function(){
3468         this.el.show();
3469     },
3470
3471     /**
3472      * Force the Layer to sync its shadow and shim positions to the element
3473      */
3474     sync : function(){
3475         this.el.sync();
3476     },
3477
3478     /**
3479      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3480      * invalid drop operation by the item being dragged.
3481      * @param {Array} xy The XY position of the element ([x, y])
3482      * @param {Function} callback The function to call after the repair is complete
3483      * @param {Object} scope The scope in which to execute the callback
3484      */
3485     repair : function(xy, callback, scope){
3486         this.callback = callback;
3487         this.scope = scope;
3488         if(xy && this.animRepair !== false){
3489             this.el.addClass("x-dd-drag-repair");
3490             this.el.hideUnders(true);
3491             this.anim = this.el.shift({
3492                 duration: this.repairDuration || .5,
3493                 easing: 'easeOut',
3494                 xy: xy,
3495                 stopFx: true,
3496                 callback: this.afterRepair,
3497                 scope: this
3498             });
3499         }else{
3500             this.afterRepair();
3501         }
3502     },
3503
3504     // private
3505     afterRepair : function(){
3506         this.hide(true);
3507         if(typeof this.callback == "function"){
3508             this.callback.call(this.scope || this);
3509         }
3510         this.callback = null;
3511         this.scope = null;
3512     }
3513 };/*
3514  * Based on:
3515  * Ext JS Library 1.1.1
3516  * Copyright(c) 2006-2007, Ext JS, LLC.
3517  *
3518  * Originally Released Under LGPL - original licence link has changed is not relivant.
3519  *
3520  * Fork - LGPL
3521  * <script type="text/javascript">
3522  */
3523
3524 /**
3525  * @class Roo.dd.DragSource
3526  * @extends Roo.dd.DDProxy
3527  * A simple class that provides the basic implementation needed to make any element draggable.
3528  * @constructor
3529  * @param {String/HTMLElement/Element} el The container element
3530  * @param {Object} config
3531  */
3532 Roo.dd.DragSource = function(el, config){
3533     this.el = Roo.get(el);
3534     this.dragData = {};
3535     
3536     Roo.apply(this, config);
3537     
3538     if(!this.proxy){
3539         this.proxy = new Roo.dd.StatusProxy();
3540     }
3541
3542     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3543           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3544     
3545     this.dragging = false;
3546 };
3547
3548 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3549     /**
3550      * @cfg {String} dropAllowed
3551      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3552      */
3553     dropAllowed : "x-dd-drop-ok",
3554     /**
3555      * @cfg {String} dropNotAllowed
3556      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3557      */
3558     dropNotAllowed : "x-dd-drop-nodrop",
3559
3560     /**
3561      * Returns the data object associated with this drag source
3562      * @return {Object} data An object containing arbitrary data
3563      */
3564     getDragData : function(e){
3565         return this.dragData;
3566     },
3567
3568     // private
3569     onDragEnter : function(e, id){
3570         var target = Roo.dd.DragDropMgr.getDDById(id);
3571         this.cachedTarget = target;
3572         if(this.beforeDragEnter(target, e, id) !== false){
3573             if(target.isNotifyTarget){
3574                 var status = target.notifyEnter(this, e, this.dragData);
3575                 this.proxy.setStatus(status);
3576             }else{
3577                 this.proxy.setStatus(this.dropAllowed);
3578             }
3579             
3580             if(this.afterDragEnter){
3581                 /**
3582                  * An empty function by default, but provided so that you can perform a custom action
3583                  * when the dragged item enters the drop target by providing an implementation.
3584                  * @param {Roo.dd.DragDrop} target The drop target
3585                  * @param {Event} e The event object
3586                  * @param {String} id The id of the dragged element
3587                  * @method afterDragEnter
3588                  */
3589                 this.afterDragEnter(target, e, id);
3590             }
3591         }
3592     },
3593
3594     /**
3595      * An empty function by default, but provided so that you can perform a custom action
3596      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3597      * @param {Roo.dd.DragDrop} target The drop target
3598      * @param {Event} e The event object
3599      * @param {String} id The id of the dragged element
3600      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3601      */
3602     beforeDragEnter : function(target, e, id){
3603         return true;
3604     },
3605
3606     // private
3607     alignElWithMouse: function() {
3608         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3609         this.proxy.sync();
3610     },
3611
3612     // private
3613     onDragOver : function(e, id){
3614         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3615         if(this.beforeDragOver(target, e, id) !== false){
3616             if(target.isNotifyTarget){
3617                 var status = target.notifyOver(this, e, this.dragData);
3618                 this.proxy.setStatus(status);
3619             }
3620
3621             if(this.afterDragOver){
3622                 /**
3623                  * An empty function by default, but provided so that you can perform a custom action
3624                  * while the dragged item is over the drop target by providing an implementation.
3625                  * @param {Roo.dd.DragDrop} target The drop target
3626                  * @param {Event} e The event object
3627                  * @param {String} id The id of the dragged element
3628                  * @method afterDragOver
3629                  */
3630                 this.afterDragOver(target, e, id);
3631             }
3632         }
3633     },
3634
3635     /**
3636      * An empty function by default, but provided so that you can perform a custom action
3637      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3638      * @param {Roo.dd.DragDrop} target The drop target
3639      * @param {Event} e The event object
3640      * @param {String} id The id of the dragged element
3641      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3642      */
3643     beforeDragOver : function(target, e, id){
3644         return true;
3645     },
3646
3647     // private
3648     onDragOut : function(e, id){
3649         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3650         if(this.beforeDragOut(target, e, id) !== false){
3651             if(target.isNotifyTarget){
3652                 target.notifyOut(this, e, this.dragData);
3653             }
3654             this.proxy.reset();
3655             if(this.afterDragOut){
3656                 /**
3657                  * An empty function by default, but provided so that you can perform a custom action
3658                  * after the dragged item is dragged out of the target without dropping.
3659                  * @param {Roo.dd.DragDrop} target The drop target
3660                  * @param {Event} e The event object
3661                  * @param {String} id The id of the dragged element
3662                  * @method afterDragOut
3663                  */
3664                 this.afterDragOut(target, e, id);
3665             }
3666         }
3667         this.cachedTarget = null;
3668     },
3669
3670     /**
3671      * An empty function by default, but provided so that you can perform a custom action before the dragged
3672      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3673      * @param {Roo.dd.DragDrop} target The drop target
3674      * @param {Event} e The event object
3675      * @param {String} id The id of the dragged element
3676      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3677      */
3678     beforeDragOut : function(target, e, id){
3679         return true;
3680     },
3681     
3682     // private
3683     onDragDrop : function(e, id){
3684         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3685         if(this.beforeDragDrop(target, e, id) !== false){
3686             if(target.isNotifyTarget){
3687                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3688                     this.onValidDrop(target, e, id);
3689                 }else{
3690                     this.onInvalidDrop(target, e, id);
3691                 }
3692             }else{
3693                 this.onValidDrop(target, e, id);
3694             }
3695             
3696             if(this.afterDragDrop){
3697                 /**
3698                  * An empty function by default, but provided so that you can perform a custom action
3699                  * after a valid drag drop has occurred by providing an implementation.
3700                  * @param {Roo.dd.DragDrop} target The drop target
3701                  * @param {Event} e The event object
3702                  * @param {String} id The id of the dropped element
3703                  * @method afterDragDrop
3704                  */
3705                 this.afterDragDrop(target, e, id);
3706             }
3707         }
3708         delete this.cachedTarget;
3709     },
3710
3711     /**
3712      * An empty function by default, but provided so that you can perform a custom action before the dragged
3713      * item is dropped onto the target and optionally cancel the onDragDrop.
3714      * @param {Roo.dd.DragDrop} target The drop target
3715      * @param {Event} e The event object
3716      * @param {String} id The id of the dragged element
3717      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3718      */
3719     beforeDragDrop : function(target, e, id){
3720         return true;
3721     },
3722
3723     // private
3724     onValidDrop : function(target, e, id){
3725         this.hideProxy();
3726         if(this.afterValidDrop){
3727             /**
3728              * An empty function by default, but provided so that you can perform a custom action
3729              * after a valid drop has occurred by providing an implementation.
3730              * @param {Object} target The target DD 
3731              * @param {Event} e The event object
3732              * @param {String} id The id of the dropped element
3733              * @method afterInvalidDrop
3734              */
3735             this.afterValidDrop(target, e, id);
3736         }
3737     },
3738
3739     // private
3740     getRepairXY : function(e, data){
3741         return this.el.getXY();  
3742     },
3743
3744     // private
3745     onInvalidDrop : function(target, e, id){
3746         this.beforeInvalidDrop(target, e, id);
3747         if(this.cachedTarget){
3748             if(this.cachedTarget.isNotifyTarget){
3749                 this.cachedTarget.notifyOut(this, e, this.dragData);
3750             }
3751             this.cacheTarget = null;
3752         }
3753         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3754
3755         if(this.afterInvalidDrop){
3756             /**
3757              * An empty function by default, but provided so that you can perform a custom action
3758              * after an invalid drop has occurred by providing an implementation.
3759              * @param {Event} e The event object
3760              * @param {String} id The id of the dropped element
3761              * @method afterInvalidDrop
3762              */
3763             this.afterInvalidDrop(e, id);
3764         }
3765     },
3766
3767     // private
3768     afterRepair : function(){
3769         if(Roo.enableFx){
3770             this.el.highlight(this.hlColor || "c3daf9");
3771         }
3772         this.dragging = false;
3773     },
3774
3775     /**
3776      * An empty function by default, but provided so that you can perform a custom action after an invalid
3777      * drop has occurred.
3778      * @param {Roo.dd.DragDrop} target The drop target
3779      * @param {Event} e The event object
3780      * @param {String} id The id of the dragged element
3781      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3782      */
3783     beforeInvalidDrop : function(target, e, id){
3784         return true;
3785     },
3786
3787     // private
3788     handleMouseDown : function(e){
3789         if(this.dragging) {
3790             return;
3791         }
3792         var data = this.getDragData(e);
3793         if(data && this.onBeforeDrag(data, e) !== false){
3794             this.dragData = data;
3795             this.proxy.stop();
3796             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3797         } 
3798     },
3799
3800     /**
3801      * An empty function by default, but provided so that you can perform a custom action before the initial
3802      * drag event begins and optionally cancel it.
3803      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3804      * @param {Event} e The event object
3805      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3806      */
3807     onBeforeDrag : function(data, e){
3808         return true;
3809     },
3810
3811     /**
3812      * An empty function by default, but provided so that you can perform a custom action once the initial
3813      * drag event has begun.  The drag cannot be canceled from this function.
3814      * @param {Number} x The x position of the click on the dragged object
3815      * @param {Number} y The y position of the click on the dragged object
3816      */
3817     onStartDrag : Roo.emptyFn,
3818
3819     // private - YUI override
3820     startDrag : function(x, y){
3821         this.proxy.reset();
3822         this.dragging = true;
3823         this.proxy.update("");
3824         this.onInitDrag(x, y);
3825         this.proxy.show();
3826     },
3827
3828     // private
3829     onInitDrag : function(x, y){
3830         var clone = this.el.dom.cloneNode(true);
3831         clone.id = Roo.id(); // prevent duplicate ids
3832         this.proxy.update(clone);
3833         this.onStartDrag(x, y);
3834         return true;
3835     },
3836
3837     /**
3838      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3839      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3840      */
3841     getProxy : function(){
3842         return this.proxy;  
3843     },
3844
3845     /**
3846      * Hides the drag source's {@link Roo.dd.StatusProxy}
3847      */
3848     hideProxy : function(){
3849         this.proxy.hide();  
3850         this.proxy.reset(true);
3851         this.dragging = false;
3852     },
3853
3854     // private
3855     triggerCacheRefresh : function(){
3856         Roo.dd.DDM.refreshCache(this.groups);
3857     },
3858
3859     // private - override to prevent hiding
3860     b4EndDrag: function(e) {
3861     },
3862
3863     // private - override to prevent moving
3864     endDrag : function(e){
3865         this.onEndDrag(this.dragData, e);
3866     },
3867
3868     // private
3869     onEndDrag : function(data, e){
3870     },
3871     
3872     // private - pin to cursor
3873     autoOffset : function(x, y) {
3874         this.setDelta(-12, -20);
3875     }    
3876 });/*
3877  * Based on:
3878  * Ext JS Library 1.1.1
3879  * Copyright(c) 2006-2007, Ext JS, LLC.
3880  *
3881  * Originally Released Under LGPL - original licence link has changed is not relivant.
3882  *
3883  * Fork - LGPL
3884  * <script type="text/javascript">
3885  */
3886
3887
3888 /**
3889  * @class Roo.dd.DropTarget
3890  * @extends Roo.dd.DDTarget
3891  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3892  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3893  * @constructor
3894  * @param {String/HTMLElement/Element} el The container element
3895  * @param {Object} config
3896  */
3897 Roo.dd.DropTarget = function(el, config){
3898     this.el = Roo.get(el);
3899     
3900     var listeners = false; ;
3901     if (config && config.listeners) {
3902         listeners= config.listeners;
3903         delete config.listeners;
3904     }
3905     Roo.apply(this, config);
3906     
3907     if(this.containerScroll){
3908         Roo.dd.ScrollManager.register(this.el);
3909     }
3910     this.addEvents( {
3911          /**
3912          * @scope Roo.dd.DropTarget
3913          */
3914          
3915          /**
3916          * @event enter
3917          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3918          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3919          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3920          * 
3921          * IMPORTANT : it should set this.overClass and this.dropAllowed
3922          * 
3923          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3924          * @param {Event} e The event
3925          * @param {Object} data An object containing arbitrary data supplied by the drag source
3926          */
3927         "enter" : true,
3928         
3929          /**
3930          * @event over
3931          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3932          * This method will be called on every mouse movement while the drag source is over the drop target.
3933          * This default implementation simply returns the dropAllowed config value.
3934          * 
3935          * IMPORTANT : it should set this.dropAllowed
3936          * 
3937          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3938          * @param {Event} e The event
3939          * @param {Object} data An object containing arbitrary data supplied by the drag source
3940          
3941          */
3942         "over" : true,
3943         /**
3944          * @event out
3945          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3946          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3947          * overClass (if any) from the drop element.
3948          * 
3949          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3950          * @param {Event} e The event
3951          * @param {Object} data An object containing arbitrary data supplied by the drag source
3952          */
3953          "out" : true,
3954          
3955         /**
3956          * @event drop
3957          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3958          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3959          * implementation that does something to process the drop event and returns true so that the drag source's
3960          * repair action does not run.
3961          * 
3962          * IMPORTANT : it should set this.success
3963          * 
3964          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3965          * @param {Event} e The event
3966          * @param {Object} data An object containing arbitrary data supplied by the drag source
3967         */
3968          "drop" : true
3969     });
3970             
3971      
3972     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3973         this.el.dom, 
3974         this.ddGroup || this.group,
3975         {
3976             isTarget: true,
3977             listeners : listeners || {} 
3978            
3979         
3980         }
3981     );
3982
3983 };
3984
3985 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3986     /**
3987      * @cfg {String} overClass
3988      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3989      */
3990      /**
3991      * @cfg {String} ddGroup
3992      * The drag drop group to handle drop events for
3993      */
3994      
3995     /**
3996      * @cfg {String} dropAllowed
3997      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3998      */
3999     dropAllowed : "x-dd-drop-ok",
4000     /**
4001      * @cfg {String} dropNotAllowed
4002      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4003      */
4004     dropNotAllowed : "x-dd-drop-nodrop",
4005     /**
4006      * @cfg {boolean} success
4007      * set this after drop listener.. 
4008      */
4009     success : false,
4010     /**
4011      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4012      * if the drop point is valid for over/enter..
4013      */
4014     valid : false,
4015     // private
4016     isTarget : true,
4017
4018     // private
4019     isNotifyTarget : true,
4020     
4021     /**
4022      * @hide
4023      */
4024     notifyEnter : function(dd, e, data)
4025     {
4026         this.valid = true;
4027         this.fireEvent('enter', dd, e, data);
4028         if(this.overClass){
4029             this.el.addClass(this.overClass);
4030         }
4031         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4032             this.valid ? this.dropAllowed : this.dropNotAllowed
4033         );
4034     },
4035
4036     /**
4037      * @hide
4038      */
4039     notifyOver : function(dd, e, data)
4040     {
4041         this.valid = true;
4042         this.fireEvent('over', dd, e, data);
4043         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4044             this.valid ? this.dropAllowed : this.dropNotAllowed
4045         );
4046     },
4047
4048     /**
4049      * @hide
4050      */
4051     notifyOut : function(dd, e, data)
4052     {
4053         this.fireEvent('out', dd, e, data);
4054         if(this.overClass){
4055             this.el.removeClass(this.overClass);
4056         }
4057     },
4058
4059     /**
4060      * @hide
4061      */
4062     notifyDrop : function(dd, e, data)
4063     {
4064         this.success = false;
4065         this.fireEvent('drop', dd, e, data);
4066         return this.success;
4067     }
4068 });/*
4069  * Based on:
4070  * Ext JS Library 1.1.1
4071  * Copyright(c) 2006-2007, Ext JS, LLC.
4072  *
4073  * Originally Released Under LGPL - original licence link has changed is not relivant.
4074  *
4075  * Fork - LGPL
4076  * <script type="text/javascript">
4077  */
4078
4079
4080 /**
4081  * @class Roo.dd.DragZone
4082  * @extends Roo.dd.DragSource
4083  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4084  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4085  * @constructor
4086  * @param {String/HTMLElement/Element} el The container element
4087  * @param {Object} config
4088  */
4089 Roo.dd.DragZone = function(el, config){
4090     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4091     if(this.containerScroll){
4092         Roo.dd.ScrollManager.register(this.el);
4093     }
4094 };
4095
4096 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4097     /**
4098      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4099      * for auto scrolling during drag operations.
4100      */
4101     /**
4102      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4103      * method after a failed drop (defaults to "c3daf9" - light blue)
4104      */
4105
4106     /**
4107      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4108      * for a valid target to drag based on the mouse down. Override this method
4109      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4110      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4111      * @param {EventObject} e The mouse down event
4112      * @return {Object} The dragData
4113      */
4114     getDragData : function(e){
4115         return Roo.dd.Registry.getHandleFromEvent(e);
4116     },
4117     
4118     /**
4119      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4120      * this.dragData.ddel
4121      * @param {Number} x The x position of the click on the dragged object
4122      * @param {Number} y The y position of the click on the dragged object
4123      * @return {Boolean} true to continue the drag, false to cancel
4124      */
4125     onInitDrag : function(x, y){
4126         this.proxy.update(this.dragData.ddel.cloneNode(true));
4127         this.onStartDrag(x, y);
4128         return true;
4129     },
4130     
4131     /**
4132      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4133      */
4134     afterRepair : function(){
4135         if(Roo.enableFx){
4136             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4137         }
4138         this.dragging = false;
4139     },
4140
4141     /**
4142      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4143      * the XY of this.dragData.ddel
4144      * @param {EventObject} e The mouse up event
4145      * @return {Array} The xy location (e.g. [100, 200])
4146      */
4147     getRepairXY : function(e){
4148         return Roo.Element.fly(this.dragData.ddel).getXY();  
4149     }
4150 });/*
4151  * Based on:
4152  * Ext JS Library 1.1.1
4153  * Copyright(c) 2006-2007, Ext JS, LLC.
4154  *
4155  * Originally Released Under LGPL - original licence link has changed is not relivant.
4156  *
4157  * Fork - LGPL
4158  * <script type="text/javascript">
4159  */
4160 /**
4161  * @class Roo.dd.DropZone
4162  * @extends Roo.dd.DropTarget
4163  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4164  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4165  * @constructor
4166  * @param {String/HTMLElement/Element} el The container element
4167  * @param {Object} config
4168  */
4169 Roo.dd.DropZone = function(el, config){
4170     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4171 };
4172
4173 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4174     /**
4175      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4176      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4177      * provide your own custom lookup.
4178      * @param {Event} e The event
4179      * @return {Object} data The custom data
4180      */
4181     getTargetFromEvent : function(e){
4182         return Roo.dd.Registry.getTargetFromEvent(e);
4183     },
4184
4185     /**
4186      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4187      * that it has registered.  This method has no default implementation and should be overridden to provide
4188      * node-specific processing if necessary.
4189      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4190      * {@link #getTargetFromEvent} for this node)
4191      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4192      * @param {Event} e The event
4193      * @param {Object} data An object containing arbitrary data supplied by the drag source
4194      */
4195     onNodeEnter : function(n, dd, e, data){
4196         
4197     },
4198
4199     /**
4200      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4201      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4202      * overridden to provide the proper feedback.
4203      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4204      * {@link #getTargetFromEvent} for this node)
4205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4206      * @param {Event} e The event
4207      * @param {Object} data An object containing arbitrary data supplied by the drag source
4208      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4209      * underlying {@link Roo.dd.StatusProxy} can be updated
4210      */
4211     onNodeOver : function(n, dd, e, data){
4212         return this.dropAllowed;
4213     },
4214
4215     /**
4216      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4217      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4218      * node-specific processing if necessary.
4219      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4220      * {@link #getTargetFromEvent} for this node)
4221      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4222      * @param {Event} e The event
4223      * @param {Object} data An object containing arbitrary data supplied by the drag source
4224      */
4225     onNodeOut : function(n, dd, e, data){
4226         
4227     },
4228
4229     /**
4230      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4231      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4232      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4233      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4234      * {@link #getTargetFromEvent} for this node)
4235      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4236      * @param {Event} e The event
4237      * @param {Object} data An object containing arbitrary data supplied by the drag source
4238      * @return {Boolean} True if the drop was valid, else false
4239      */
4240     onNodeDrop : function(n, dd, e, data){
4241         return false;
4242     },
4243
4244     /**
4245      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4246      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4247      * it should be overridden to provide the proper feedback if necessary.
4248      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4249      * @param {Event} e The event
4250      * @param {Object} data An object containing arbitrary data supplied by the drag source
4251      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4252      * underlying {@link Roo.dd.StatusProxy} can be updated
4253      */
4254     onContainerOver : function(dd, e, data){
4255         return this.dropNotAllowed;
4256     },
4257
4258     /**
4259      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4260      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4261      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4262      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4263      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4264      * @param {Event} e The event
4265      * @param {Object} data An object containing arbitrary data supplied by the drag source
4266      * @return {Boolean} True if the drop was valid, else false
4267      */
4268     onContainerDrop : function(dd, e, data){
4269         return false;
4270     },
4271
4272     /**
4273      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4274      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4275      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4276      * you should override this method and provide a custom implementation.
4277      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4278      * @param {Event} e The event
4279      * @param {Object} data An object containing arbitrary data supplied by the drag source
4280      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4281      * underlying {@link Roo.dd.StatusProxy} can be updated
4282      */
4283     notifyEnter : function(dd, e, data){
4284         return this.dropNotAllowed;
4285     },
4286
4287     /**
4288      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4289      * This method will be called on every mouse movement while the drag source is over the drop zone.
4290      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4291      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4292      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4293      * registered node, it will call {@link #onContainerOver}.
4294      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4295      * @param {Event} e The event
4296      * @param {Object} data An object containing arbitrary data supplied by the drag source
4297      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4298      * underlying {@link Roo.dd.StatusProxy} can be updated
4299      */
4300     notifyOver : function(dd, e, data){
4301         var n = this.getTargetFromEvent(e);
4302         if(!n){ // not over valid drop target
4303             if(this.lastOverNode){
4304                 this.onNodeOut(this.lastOverNode, dd, e, data);
4305                 this.lastOverNode = null;
4306             }
4307             return this.onContainerOver(dd, e, data);
4308         }
4309         if(this.lastOverNode != n){
4310             if(this.lastOverNode){
4311                 this.onNodeOut(this.lastOverNode, dd, e, data);
4312             }
4313             this.onNodeEnter(n, dd, e, data);
4314             this.lastOverNode = n;
4315         }
4316         return this.onNodeOver(n, dd, e, data);
4317     },
4318
4319     /**
4320      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4321      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4322      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4323      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4324      * @param {Event} e The event
4325      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4326      */
4327     notifyOut : function(dd, e, data){
4328         if(this.lastOverNode){
4329             this.onNodeOut(this.lastOverNode, dd, e, data);
4330             this.lastOverNode = null;
4331         }
4332     },
4333
4334     /**
4335      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4336      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4337      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4338      * otherwise it will call {@link #onContainerDrop}.
4339      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4340      * @param {Event} e The event
4341      * @param {Object} data An object containing arbitrary data supplied by the drag source
4342      * @return {Boolean} True if the drop was valid, else false
4343      */
4344     notifyDrop : function(dd, e, data){
4345         if(this.lastOverNode){
4346             this.onNodeOut(this.lastOverNode, dd, e, data);
4347             this.lastOverNode = null;
4348         }
4349         var n = this.getTargetFromEvent(e);
4350         return n ?
4351             this.onNodeDrop(n, dd, e, data) :
4352             this.onContainerDrop(dd, e, data);
4353     },
4354
4355     // private
4356     triggerCacheRefresh : function(){
4357         Roo.dd.DDM.refreshCache(this.groups);
4358     }  
4359 });/*
4360  * Based on:
4361  * Ext JS Library 1.1.1
4362  * Copyright(c) 2006-2007, Ext JS, LLC.
4363  *
4364  * Originally Released Under LGPL - original licence link has changed is not relivant.
4365  *
4366  * Fork - LGPL
4367  * <script type="text/javascript">
4368  */
4369
4370
4371 /**
4372  * @class Roo.data.SortTypes
4373  * @singleton
4374  * Defines the default sorting (casting?) comparison functions used when sorting data.
4375  */
4376 Roo.data.SortTypes = {
4377     /**
4378      * Default sort that does nothing
4379      * @param {Mixed} s The value being converted
4380      * @return {Mixed} The comparison value
4381      */
4382     none : function(s){
4383         return s;
4384     },
4385     
4386     /**
4387      * The regular expression used to strip tags
4388      * @type {RegExp}
4389      * @property
4390      */
4391     stripTagsRE : /<\/?[^>]+>/gi,
4392     
4393     /**
4394      * Strips all HTML tags to sort on text only
4395      * @param {Mixed} s The value being converted
4396      * @return {String} The comparison value
4397      */
4398     asText : function(s){
4399         return String(s).replace(this.stripTagsRE, "");
4400     },
4401     
4402     /**
4403      * Strips all HTML tags to sort on text only - Case insensitive
4404      * @param {Mixed} s The value being converted
4405      * @return {String} The comparison value
4406      */
4407     asUCText : function(s){
4408         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4409     },
4410     
4411     /**
4412      * Case insensitive string
4413      * @param {Mixed} s The value being converted
4414      * @return {String} The comparison value
4415      */
4416     asUCString : function(s) {
4417         return String(s).toUpperCase();
4418     },
4419     
4420     /**
4421      * Date sorting
4422      * @param {Mixed} s The value being converted
4423      * @return {Number} The comparison value
4424      */
4425     asDate : function(s) {
4426         if(!s){
4427             return 0;
4428         }
4429         if(s instanceof Date){
4430             return s.getTime();
4431         }
4432         return Date.parse(String(s));
4433     },
4434     
4435     /**
4436      * Float sorting
4437      * @param {Mixed} s The value being converted
4438      * @return {Float} The comparison value
4439      */
4440     asFloat : function(s) {
4441         var val = parseFloat(String(s).replace(/,/g, ""));
4442         if(isNaN(val)) val = 0;
4443         return val;
4444     },
4445     
4446     /**
4447      * Integer sorting
4448      * @param {Mixed} s The value being converted
4449      * @return {Number} The comparison value
4450      */
4451     asInt : function(s) {
4452         var val = parseInt(String(s).replace(/,/g, ""));
4453         if(isNaN(val)) val = 0;
4454         return val;
4455     }
4456 };/*
4457  * Based on:
4458  * Ext JS Library 1.1.1
4459  * Copyright(c) 2006-2007, Ext JS, LLC.
4460  *
4461  * Originally Released Under LGPL - original licence link has changed is not relivant.
4462  *
4463  * Fork - LGPL
4464  * <script type="text/javascript">
4465  */
4466
4467 /**
4468 * @class Roo.data.Record
4469  * Instances of this class encapsulate both record <em>definition</em> information, and record
4470  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4471  * to access Records cached in an {@link Roo.data.Store} object.<br>
4472  * <p>
4473  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4474  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4475  * objects.<br>
4476  * <p>
4477  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4478  * @constructor
4479  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4480  * {@link #create}. The parameters are the same.
4481  * @param {Array} data An associative Array of data values keyed by the field name.
4482  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4483  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4484  * not specified an integer id is generated.
4485  */
4486 Roo.data.Record = function(data, id){
4487     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4488     this.data = data;
4489 };
4490
4491 /**
4492  * Generate a constructor for a specific record layout.
4493  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4494  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4495  * Each field definition object may contain the following properties: <ul>
4496  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4497  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4498  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4499  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4500  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4501  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4502  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4503  * this may be omitted.</p></li>
4504  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4505  * <ul><li>auto (Default, implies no conversion)</li>
4506  * <li>string</li>
4507  * <li>int</li>
4508  * <li>float</li>
4509  * <li>boolean</li>
4510  * <li>date</li></ul></p></li>
4511  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4512  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4513  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4514  * by the Reader into an object that will be stored in the Record. It is passed the
4515  * following parameters:<ul>
4516  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4517  * </ul></p></li>
4518  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4519  * </ul>
4520  * <br>usage:<br><pre><code>
4521 var TopicRecord = Roo.data.Record.create(
4522     {name: 'title', mapping: 'topic_title'},
4523     {name: 'author', mapping: 'username'},
4524     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4525     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4526     {name: 'lastPoster', mapping: 'user2'},
4527     {name: 'excerpt', mapping: 'post_text'}
4528 );
4529
4530 var myNewRecord = new TopicRecord({
4531     title: 'Do my job please',
4532     author: 'noobie',
4533     totalPosts: 1,
4534     lastPost: new Date(),
4535     lastPoster: 'Animal',
4536     excerpt: 'No way dude!'
4537 });
4538 myStore.add(myNewRecord);
4539 </code></pre>
4540  * @method create
4541  * @static
4542  */
4543 Roo.data.Record.create = function(o){
4544     var f = function(){
4545         f.superclass.constructor.apply(this, arguments);
4546     };
4547     Roo.extend(f, Roo.data.Record);
4548     var p = f.prototype;
4549     p.fields = new Roo.util.MixedCollection(false, function(field){
4550         return field.name;
4551     });
4552     for(var i = 0, len = o.length; i < len; i++){
4553         p.fields.add(new Roo.data.Field(o[i]));
4554     }
4555     f.getField = function(name){
4556         return p.fields.get(name);  
4557     };
4558     return f;
4559 };
4560
4561 Roo.data.Record.AUTO_ID = 1000;
4562 Roo.data.Record.EDIT = 'edit';
4563 Roo.data.Record.REJECT = 'reject';
4564 Roo.data.Record.COMMIT = 'commit';
4565
4566 Roo.data.Record.prototype = {
4567     /**
4568      * Readonly flag - true if this record has been modified.
4569      * @type Boolean
4570      */
4571     dirty : false,
4572     editing : false,
4573     error: null,
4574     modified: null,
4575
4576     // private
4577     join : function(store){
4578         this.store = store;
4579     },
4580
4581     /**
4582      * Set the named field to the specified value.
4583      * @param {String} name The name of the field to set.
4584      * @param {Object} value The value to set the field to.
4585      */
4586     set : function(name, value){
4587         if(this.data[name] == value){
4588             return;
4589         }
4590         this.dirty = true;
4591         if(!this.modified){
4592             this.modified = {};
4593         }
4594         if(typeof this.modified[name] == 'undefined'){
4595             this.modified[name] = this.data[name];
4596         }
4597         this.data[name] = value;
4598         if(!this.editing && this.store){
4599             this.store.afterEdit(this);
4600         }       
4601     },
4602
4603     /**
4604      * Get the value of the named field.
4605      * @param {String} name The name of the field to get the value of.
4606      * @return {Object} The value of the field.
4607      */
4608     get : function(name){
4609         return this.data[name]; 
4610     },
4611
4612     // private
4613     beginEdit : function(){
4614         this.editing = true;
4615         this.modified = {}; 
4616     },
4617
4618     // private
4619     cancelEdit : function(){
4620         this.editing = false;
4621         delete this.modified;
4622     },
4623
4624     // private
4625     endEdit : function(){
4626         this.editing = false;
4627         if(this.dirty && this.store){
4628             this.store.afterEdit(this);
4629         }
4630     },
4631
4632     /**
4633      * Usually called by the {@link Roo.data.Store} which owns the Record.
4634      * Rejects all changes made to the Record since either creation, or the last commit operation.
4635      * Modified fields are reverted to their original values.
4636      * <p>
4637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4638      * of reject operations.
4639      */
4640     reject : function(){
4641         var m = this.modified;
4642         for(var n in m){
4643             if(typeof m[n] != "function"){
4644                 this.data[n] = m[n];
4645             }
4646         }
4647         this.dirty = false;
4648         delete this.modified;
4649         this.editing = false;
4650         if(this.store){
4651             this.store.afterReject(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Commits all changes made to the Record since either creation, or the last commit operation.
4658      * <p>
4659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4660      * of commit operations.
4661      */
4662     commit : function(){
4663         this.dirty = false;
4664         delete this.modified;
4665         this.editing = false;
4666         if(this.store){
4667             this.store.afterCommit(this);
4668         }
4669     },
4670
4671     // private
4672     hasError : function(){
4673         return this.error != null;
4674     },
4675
4676     // private
4677     clearError : function(){
4678         this.error = null;
4679     },
4680
4681     /**
4682      * Creates a copy of this record.
4683      * @param {String} id (optional) A new record id if you don't want to use this record's id
4684      * @return {Record}
4685      */
4686     copy : function(newId) {
4687         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4688     }
4689 };/*
4690  * Based on:
4691  * Ext JS Library 1.1.1
4692  * Copyright(c) 2006-2007, Ext JS, LLC.
4693  *
4694  * Originally Released Under LGPL - original licence link has changed is not relivant.
4695  *
4696  * Fork - LGPL
4697  * <script type="text/javascript">
4698  */
4699
4700
4701
4702 /**
4703  * @class Roo.data.Store
4704  * @extends Roo.util.Observable
4705  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4706  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4707  * <p>
4708  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4709  * has no knowledge of the format of the data returned by the Proxy.<br>
4710  * <p>
4711  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4712  * instances from the data object. These records are cached and made available through accessor functions.
4713  * @constructor
4714  * Creates a new Store.
4715  * @param {Object} config A config object containing the objects needed for the Store to access data,
4716  * and read the data into Records.
4717  */
4718 Roo.data.Store = function(config){
4719     this.data = new Roo.util.MixedCollection(false);
4720     this.data.getKey = function(o){
4721         return o.id;
4722     };
4723     this.baseParams = {};
4724     // private
4725     this.paramNames = {
4726         "start" : "start",
4727         "limit" : "limit",
4728         "sort" : "sort",
4729         "dir" : "dir",
4730         "multisort" : "_multisort"
4731     };
4732
4733     if(config && config.data){
4734         this.inlineData = config.data;
4735         delete config.data;
4736     }
4737
4738     Roo.apply(this, config);
4739     
4740     if(this.reader){ // reader passed
4741         this.reader = Roo.factory(this.reader, Roo.data);
4742         this.reader.xmodule = this.xmodule || false;
4743         if(!this.recordType){
4744             this.recordType = this.reader.recordType;
4745         }
4746         if(this.reader.onMetaChange){
4747             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4748         }
4749     }
4750
4751     if(this.recordType){
4752         this.fields = this.recordType.prototype.fields;
4753     }
4754     this.modified = [];
4755
4756     this.addEvents({
4757         /**
4758          * @event datachanged
4759          * Fires when the data cache has changed, and a widget which is using this Store
4760          * as a Record cache should refresh its view.
4761          * @param {Store} this
4762          */
4763         datachanged : true,
4764         /**
4765          * @event metachange
4766          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4767          * @param {Store} this
4768          * @param {Object} meta The JSON metadata
4769          */
4770         metachange : true,
4771         /**
4772          * @event add
4773          * Fires when Records have been added to the Store
4774          * @param {Store} this
4775          * @param {Roo.data.Record[]} records The array of Records added
4776          * @param {Number} index The index at which the record(s) were added
4777          */
4778         add : true,
4779         /**
4780          * @event remove
4781          * Fires when a Record has been removed from the Store
4782          * @param {Store} this
4783          * @param {Roo.data.Record} record The Record that was removed
4784          * @param {Number} index The index at which the record was removed
4785          */
4786         remove : true,
4787         /**
4788          * @event update
4789          * Fires when a Record has been updated
4790          * @param {Store} this
4791          * @param {Roo.data.Record} record The Record that was updated
4792          * @param {String} operation The update operation being performed.  Value may be one of:
4793          * <pre><code>
4794  Roo.data.Record.EDIT
4795  Roo.data.Record.REJECT
4796  Roo.data.Record.COMMIT
4797          * </code></pre>
4798          */
4799         update : true,
4800         /**
4801          * @event clear
4802          * Fires when the data cache has been cleared.
4803          * @param {Store} this
4804          */
4805         clear : true,
4806         /**
4807          * @event beforeload
4808          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4809          * the load action will be canceled.
4810          * @param {Store} this
4811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4812          */
4813         beforeload : true,
4814         /**
4815          * @event beforeloadadd
4816          * Fires after a new set of Records has been loaded.
4817          * @param {Store} this
4818          * @param {Roo.data.Record[]} records The Records that were loaded
4819          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4820          */
4821         beforeloadadd : true,
4822         /**
4823          * @event load
4824          * Fires after a new set of Records has been loaded, before they are added to the store.
4825          * @param {Store} this
4826          * @param {Roo.data.Record[]} records The Records that were loaded
4827          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4828          * @params {Object} return from reader
4829          */
4830         load : true,
4831         /**
4832          * @event loadexception
4833          * Fires if an exception occurs in the Proxy during loading.
4834          * Called with the signature of the Proxy's "loadexception" event.
4835          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4836          * 
4837          * @param {Proxy} 
4838          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4839          * @param {Object} load options 
4840          * @param {Object} jsonData from your request (normally this contains the Exception)
4841          */
4842         loadexception : true
4843     });
4844     
4845     if(this.proxy){
4846         this.proxy = Roo.factory(this.proxy, Roo.data);
4847         this.proxy.xmodule = this.xmodule || false;
4848         this.relayEvents(this.proxy,  ["loadexception"]);
4849     }
4850     this.sortToggle = {};
4851     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4852
4853     Roo.data.Store.superclass.constructor.call(this);
4854
4855     if(this.inlineData){
4856         this.loadData(this.inlineData);
4857         delete this.inlineData;
4858     }
4859 };
4860
4861 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4862      /**
4863     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4864     * without a remote query - used by combo/forms at present.
4865     */
4866     
4867     /**
4868     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4869     */
4870     /**
4871     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4872     */
4873     /**
4874     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4875     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4876     */
4877     /**
4878     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4879     * on any HTTP request
4880     */
4881     /**
4882     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4883     */
4884     /**
4885     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4886     */
4887     multiSort: false,
4888     /**
4889     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4890     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4891     */
4892     remoteSort : false,
4893
4894     /**
4895     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4896      * loaded or when a record is removed. (defaults to false).
4897     */
4898     pruneModifiedRecords : false,
4899
4900     // private
4901     lastOptions : null,
4902
4903     /**
4904      * Add Records to the Store and fires the add event.
4905      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4906      */
4907     add : function(records){
4908         records = [].concat(records);
4909         for(var i = 0, len = records.length; i < len; i++){
4910             records[i].join(this);
4911         }
4912         var index = this.data.length;
4913         this.data.addAll(records);
4914         this.fireEvent("add", this, records, index);
4915     },
4916
4917     /**
4918      * Remove a Record from the Store and fires the remove event.
4919      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4920      */
4921     remove : function(record){
4922         var index = this.data.indexOf(record);
4923         this.data.removeAt(index);
4924         if(this.pruneModifiedRecords){
4925             this.modified.remove(record);
4926         }
4927         this.fireEvent("remove", this, record, index);
4928     },
4929
4930     /**
4931      * Remove all Records from the Store and fires the clear event.
4932      */
4933     removeAll : function(){
4934         this.data.clear();
4935         if(this.pruneModifiedRecords){
4936             this.modified = [];
4937         }
4938         this.fireEvent("clear", this);
4939     },
4940
4941     /**
4942      * Inserts Records to the Store at the given index and fires the add event.
4943      * @param {Number} index The start index at which to insert the passed Records.
4944      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4945      */
4946     insert : function(index, records){
4947         records = [].concat(records);
4948         for(var i = 0, len = records.length; i < len; i++){
4949             this.data.insert(index, records[i]);
4950             records[i].join(this);
4951         }
4952         this.fireEvent("add", this, records, index);
4953     },
4954
4955     /**
4956      * Get the index within the cache of the passed Record.
4957      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4958      * @return {Number} The index of the passed Record. Returns -1 if not found.
4959      */
4960     indexOf : function(record){
4961         return this.data.indexOf(record);
4962     },
4963
4964     /**
4965      * Get the index within the cache of the Record with the passed id.
4966      * @param {String} id The id of the Record to find.
4967      * @return {Number} The index of the Record. Returns -1 if not found.
4968      */
4969     indexOfId : function(id){
4970         return this.data.indexOfKey(id);
4971     },
4972
4973     /**
4974      * Get the Record with the specified id.
4975      * @param {String} id The id of the Record to find.
4976      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4977      */
4978     getById : function(id){
4979         return this.data.key(id);
4980     },
4981
4982     /**
4983      * Get the Record at the specified index.
4984      * @param {Number} index The index of the Record to find.
4985      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4986      */
4987     getAt : function(index){
4988         return this.data.itemAt(index);
4989     },
4990
4991     /**
4992      * Returns a range of Records between specified indices.
4993      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4994      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4995      * @return {Roo.data.Record[]} An array of Records
4996      */
4997     getRange : function(start, end){
4998         return this.data.getRange(start, end);
4999     },
5000
5001     // private
5002     storeOptions : function(o){
5003         o = Roo.apply({}, o);
5004         delete o.callback;
5005         delete o.scope;
5006         this.lastOptions = o;
5007     },
5008
5009     /**
5010      * Loads the Record cache from the configured Proxy using the configured Reader.
5011      * <p>
5012      * If using remote paging, then the first load call must specify the <em>start</em>
5013      * and <em>limit</em> properties in the options.params property to establish the initial
5014      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5015      * <p>
5016      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5017      * and this call will return before the new data has been loaded. Perform any post-processing
5018      * in a callback function, or in a "load" event handler.</strong>
5019      * <p>
5020      * @param {Object} options An object containing properties which control loading options:<ul>
5021      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5022      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5023      * passed the following arguments:<ul>
5024      * <li>r : Roo.data.Record[]</li>
5025      * <li>options: Options object from the load call</li>
5026      * <li>success: Boolean success indicator</li></ul></li>
5027      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5028      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5029      * </ul>
5030      */
5031     load : function(options){
5032         options = options || {};
5033         if(this.fireEvent("beforeload", this, options) !== false){
5034             this.storeOptions(options);
5035             var p = Roo.apply(options.params || {}, this.baseParams);
5036             // if meta was not loaded from remote source.. try requesting it.
5037             if (!this.reader.metaFromRemote) {
5038                 p._requestMeta = 1;
5039             }
5040             if(this.sortInfo && this.remoteSort){
5041                 var pn = this.paramNames;
5042                 p[pn["sort"]] = this.sortInfo.field;
5043                 p[pn["dir"]] = this.sortInfo.direction;
5044             }
5045             if (this.multiSort) {
5046                 var pn = this.paramNames;
5047                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5048             }
5049             
5050             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5051         }
5052     },
5053
5054     /**
5055      * Reloads the Record cache from the configured Proxy using the configured Reader and
5056      * the options from the last load operation performed.
5057      * @param {Object} options (optional) An object containing properties which may override the options
5058      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5059      * the most recently used options are reused).
5060      */
5061     reload : function(options){
5062         this.load(Roo.applyIf(options||{}, this.lastOptions));
5063     },
5064
5065     // private
5066     // Called as a callback by the Reader during a load operation.
5067     loadRecords : function(o, options, success){
5068         if(!o || success === false){
5069             if(success !== false){
5070                 this.fireEvent("load", this, [], options, o);
5071             }
5072             if(options.callback){
5073                 options.callback.call(options.scope || this, [], options, false);
5074             }
5075             return;
5076         }
5077         // if data returned failure - throw an exception.
5078         if (o.success === false) {
5079             // show a message if no listener is registered.
5080             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5081                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5082             }
5083             // loadmask wil be hooked into this..
5084             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5085             return;
5086         }
5087         var r = o.records, t = o.totalRecords || r.length;
5088         
5089         this.fireEvent("beforeloadadd", this, r, options, o);
5090         
5091         if(!options || options.add !== true){
5092             if(this.pruneModifiedRecords){
5093                 this.modified = [];
5094             }
5095             for(var i = 0, len = r.length; i < len; i++){
5096                 r[i].join(this);
5097             }
5098             if(this.snapshot){
5099                 this.data = this.snapshot;
5100                 delete this.snapshot;
5101             }
5102             this.data.clear();
5103             this.data.addAll(r);
5104             this.totalLength = t;
5105             this.applySort();
5106             this.fireEvent("datachanged", this);
5107         }else{
5108             this.totalLength = Math.max(t, this.data.length+r.length);
5109             this.add(r);
5110         }
5111         this.fireEvent("load", this, r, options, o);
5112         if(options.callback){
5113             options.callback.call(options.scope || this, r, options, true);
5114         }
5115     },
5116
5117
5118     /**
5119      * Loads data from a passed data block. A Reader which understands the format of the data
5120      * must have been configured in the constructor.
5121      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5122      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5123      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5124      */
5125     loadData : function(o, append){
5126         var r = this.reader.readRecords(o);
5127         this.loadRecords(r, {add: append}, true);
5128     },
5129
5130     /**
5131      * Gets the number of cached records.
5132      * <p>
5133      * <em>If using paging, this may not be the total size of the dataset. If the data object
5134      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5135      * the data set size</em>
5136      */
5137     getCount : function(){
5138         return this.data.length || 0;
5139     },
5140
5141     /**
5142      * Gets the total number of records in the dataset as returned by the server.
5143      * <p>
5144      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5145      * the dataset size</em>
5146      */
5147     getTotalCount : function(){
5148         return this.totalLength || 0;
5149     },
5150
5151     /**
5152      * Returns the sort state of the Store as an object with two properties:
5153      * <pre><code>
5154  field {String} The name of the field by which the Records are sorted
5155  direction {String} The sort order, "ASC" or "DESC"
5156      * </code></pre>
5157      */
5158     getSortState : function(){
5159         return this.sortInfo;
5160     },
5161
5162     // private
5163     applySort : function(){
5164         if(this.sortInfo && !this.remoteSort){
5165             var s = this.sortInfo, f = s.field;
5166             var st = this.fields.get(f).sortType;
5167             var fn = function(r1, r2){
5168                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5169                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5170             };
5171             this.data.sort(s.direction, fn);
5172             if(this.snapshot && this.snapshot != this.data){
5173                 this.snapshot.sort(s.direction, fn);
5174             }
5175         }
5176     },
5177
5178     /**
5179      * Sets the default sort column and order to be used by the next load operation.
5180      * @param {String} fieldName The name of the field to sort by.
5181      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5182      */
5183     setDefaultSort : function(field, dir){
5184         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5185     },
5186
5187     /**
5188      * Sort the Records.
5189      * If remote sorting is used, the sort is performed on the server, and the cache is
5190      * reloaded. If local sorting is used, the cache is sorted internally.
5191      * @param {String} fieldName The name of the field to sort by.
5192      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5193      */
5194     sort : function(fieldName, dir){
5195         var f = this.fields.get(fieldName);
5196         if(!dir){
5197             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5198             
5199             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5200                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5201             }else{
5202                 dir = f.sortDir;
5203             }
5204         }
5205         this.sortToggle[f.name] = dir;
5206         this.sortInfo = {field: f.name, direction: dir};
5207         if(!this.remoteSort){
5208             this.applySort();
5209             this.fireEvent("datachanged", this);
5210         }else{
5211             this.load(this.lastOptions);
5212         }
5213     },
5214
5215     /**
5216      * Calls the specified function for each of the Records in the cache.
5217      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5218      * Returning <em>false</em> aborts and exits the iteration.
5219      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5220      */
5221     each : function(fn, scope){
5222         this.data.each(fn, scope);
5223     },
5224
5225     /**
5226      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5227      * (e.g., during paging).
5228      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5229      */
5230     getModifiedRecords : function(){
5231         return this.modified;
5232     },
5233
5234     // private
5235     createFilterFn : function(property, value, anyMatch){
5236         if(!value.exec){ // not a regex
5237             value = String(value);
5238             if(value.length == 0){
5239                 return false;
5240             }
5241             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5242         }
5243         return function(r){
5244             return value.test(r.data[property]);
5245         };
5246     },
5247
5248     /**
5249      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5250      * @param {String} property A field on your records
5251      * @param {Number} start The record index to start at (defaults to 0)
5252      * @param {Number} end The last record index to include (defaults to length - 1)
5253      * @return {Number} The sum
5254      */
5255     sum : function(property, start, end){
5256         var rs = this.data.items, v = 0;
5257         start = start || 0;
5258         end = (end || end === 0) ? end : rs.length-1;
5259
5260         for(var i = start; i <= end; i++){
5261             v += (rs[i].data[property] || 0);
5262         }
5263         return v;
5264     },
5265
5266     /**
5267      * Filter the records by a specified property.
5268      * @param {String} field A field on your records
5269      * @param {String/RegExp} value Either a string that the field
5270      * should start with or a RegExp to test against the field
5271      * @param {Boolean} anyMatch True to match any part not just the beginning
5272      */
5273     filter : function(property, value, anyMatch){
5274         var fn = this.createFilterFn(property, value, anyMatch);
5275         return fn ? this.filterBy(fn) : this.clearFilter();
5276     },
5277
5278     /**
5279      * Filter by a function. The specified function will be called with each
5280      * record in this data source. If the function returns true the record is included,
5281      * otherwise it is filtered.
5282      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5283      * @param {Object} scope (optional) The scope of the function (defaults to this)
5284      */
5285     filterBy : function(fn, scope){
5286         this.snapshot = this.snapshot || this.data;
5287         this.data = this.queryBy(fn, scope||this);
5288         this.fireEvent("datachanged", this);
5289     },
5290
5291     /**
5292      * Query the records by a specified property.
5293      * @param {String} field A field on your records
5294      * @param {String/RegExp} value Either a string that the field
5295      * should start with or a RegExp to test against the field
5296      * @param {Boolean} anyMatch True to match any part not just the beginning
5297      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5298      */
5299     query : function(property, value, anyMatch){
5300         var fn = this.createFilterFn(property, value, anyMatch);
5301         return fn ? this.queryBy(fn) : this.data.clone();
5302     },
5303
5304     /**
5305      * Query by a function. The specified function will be called with each
5306      * record in this data source. If the function returns true the record is included
5307      * in the results.
5308      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5309      * @param {Object} scope (optional) The scope of the function (defaults to this)
5310       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5311      **/
5312     queryBy : function(fn, scope){
5313         var data = this.snapshot || this.data;
5314         return data.filterBy(fn, scope||this);
5315     },
5316
5317     /**
5318      * Collects unique values for a particular dataIndex from this store.
5319      * @param {String} dataIndex The property to collect
5320      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5321      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5322      * @return {Array} An array of the unique values
5323      **/
5324     collect : function(dataIndex, allowNull, bypassFilter){
5325         var d = (bypassFilter === true && this.snapshot) ?
5326                 this.snapshot.items : this.data.items;
5327         var v, sv, r = [], l = {};
5328         for(var i = 0, len = d.length; i < len; i++){
5329             v = d[i].data[dataIndex];
5330             sv = String(v);
5331             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5332                 l[sv] = true;
5333                 r[r.length] = v;
5334             }
5335         }
5336         return r;
5337     },
5338
5339     /**
5340      * Revert to a view of the Record cache with no filtering applied.
5341      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5342      */
5343     clearFilter : function(suppressEvent){
5344         if(this.snapshot && this.snapshot != this.data){
5345             this.data = this.snapshot;
5346             delete this.snapshot;
5347             if(suppressEvent !== true){
5348                 this.fireEvent("datachanged", this);
5349             }
5350         }
5351     },
5352
5353     // private
5354     afterEdit : function(record){
5355         if(this.modified.indexOf(record) == -1){
5356             this.modified.push(record);
5357         }
5358         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5359     },
5360     
5361     // private
5362     afterReject : function(record){
5363         this.modified.remove(record);
5364         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5365     },
5366
5367     // private
5368     afterCommit : function(record){
5369         this.modified.remove(record);
5370         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5371     },
5372
5373     /**
5374      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5375      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5376      */
5377     commitChanges : function(){
5378         var m = this.modified.slice(0);
5379         this.modified = [];
5380         for(var i = 0, len = m.length; i < len; i++){
5381             m[i].commit();
5382         }
5383     },
5384
5385     /**
5386      * Cancel outstanding changes on all changed records.
5387      */
5388     rejectChanges : function(){
5389         var m = this.modified.slice(0);
5390         this.modified = [];
5391         for(var i = 0, len = m.length; i < len; i++){
5392             m[i].reject();
5393         }
5394     },
5395
5396     onMetaChange : function(meta, rtype, o){
5397         this.recordType = rtype;
5398         this.fields = rtype.prototype.fields;
5399         delete this.snapshot;
5400         this.sortInfo = meta.sortInfo || this.sortInfo;
5401         this.modified = [];
5402         this.fireEvent('metachange', this, this.reader.meta);
5403     }
5404 });/*
5405  * Based on:
5406  * Ext JS Library 1.1.1
5407  * Copyright(c) 2006-2007, Ext JS, LLC.
5408  *
5409  * Originally Released Under LGPL - original licence link has changed is not relivant.
5410  *
5411  * Fork - LGPL
5412  * <script type="text/javascript">
5413  */
5414
5415 /**
5416  * @class Roo.data.SimpleStore
5417  * @extends Roo.data.Store
5418  * Small helper class to make creating Stores from Array data easier.
5419  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5420  * @cfg {Array} fields An array of field definition objects, or field name strings.
5421  * @cfg {Array} data The multi-dimensional array of data
5422  * @constructor
5423  * @param {Object} config
5424  */
5425 Roo.data.SimpleStore = function(config){
5426     Roo.data.SimpleStore.superclass.constructor.call(this, {
5427         isLocal : true,
5428         reader: new Roo.data.ArrayReader({
5429                 id: config.id
5430             },
5431             Roo.data.Record.create(config.fields)
5432         ),
5433         proxy : new Roo.data.MemoryProxy(config.data)
5434     });
5435     this.load();
5436 };
5437 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5438  * Based on:
5439  * Ext JS Library 1.1.1
5440  * Copyright(c) 2006-2007, Ext JS, LLC.
5441  *
5442  * Originally Released Under LGPL - original licence link has changed is not relivant.
5443  *
5444  * Fork - LGPL
5445  * <script type="text/javascript">
5446  */
5447
5448 /**
5449 /**
5450  * @extends Roo.data.Store
5451  * @class Roo.data.JsonStore
5452  * Small helper class to make creating Stores for JSON data easier. <br/>
5453 <pre><code>
5454 var store = new Roo.data.JsonStore({
5455     url: 'get-images.php',
5456     root: 'images',
5457     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5458 });
5459 </code></pre>
5460  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5461  * JsonReader and HttpProxy (unless inline data is provided).</b>
5462  * @cfg {Array} fields An array of field definition objects, or field name strings.
5463  * @constructor
5464  * @param {Object} config
5465  */
5466 Roo.data.JsonStore = function(c){
5467     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5468         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5469         reader: new Roo.data.JsonReader(c, c.fields)
5470     }));
5471 };
5472 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5473  * Based on:
5474  * Ext JS Library 1.1.1
5475  * Copyright(c) 2006-2007, Ext JS, LLC.
5476  *
5477  * Originally Released Under LGPL - original licence link has changed is not relivant.
5478  *
5479  * Fork - LGPL
5480  * <script type="text/javascript">
5481  */
5482
5483  
5484 Roo.data.Field = function(config){
5485     if(typeof config == "string"){
5486         config = {name: config};
5487     }
5488     Roo.apply(this, config);
5489     
5490     if(!this.type){
5491         this.type = "auto";
5492     }
5493     
5494     var st = Roo.data.SortTypes;
5495     // named sortTypes are supported, here we look them up
5496     if(typeof this.sortType == "string"){
5497         this.sortType = st[this.sortType];
5498     }
5499     
5500     // set default sortType for strings and dates
5501     if(!this.sortType){
5502         switch(this.type){
5503             case "string":
5504                 this.sortType = st.asUCString;
5505                 break;
5506             case "date":
5507                 this.sortType = st.asDate;
5508                 break;
5509             default:
5510                 this.sortType = st.none;
5511         }
5512     }
5513
5514     // define once
5515     var stripRe = /[\$,%]/g;
5516
5517     // prebuilt conversion function for this field, instead of
5518     // switching every time we're reading a value
5519     if(!this.convert){
5520         var cv, dateFormat = this.dateFormat;
5521         switch(this.type){
5522             case "":
5523             case "auto":
5524             case undefined:
5525                 cv = function(v){ return v; };
5526                 break;
5527             case "string":
5528                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5529                 break;
5530             case "int":
5531                 cv = function(v){
5532                     return v !== undefined && v !== null && v !== '' ?
5533                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5534                     };
5535                 break;
5536             case "float":
5537                 cv = function(v){
5538                     return v !== undefined && v !== null && v !== '' ?
5539                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5540                     };
5541                 break;
5542             case "bool":
5543             case "boolean":
5544                 cv = function(v){ return v === true || v === "true" || v == 1; };
5545                 break;
5546             case "date":
5547                 cv = function(v){
5548                     if(!v){
5549                         return '';
5550                     }
5551                     if(v instanceof Date){
5552                         return v;
5553                     }
5554                     if(dateFormat){
5555                         if(dateFormat == "timestamp"){
5556                             return new Date(v*1000);
5557                         }
5558                         return Date.parseDate(v, dateFormat);
5559                     }
5560                     var parsed = Date.parse(v);
5561                     return parsed ? new Date(parsed) : null;
5562                 };
5563              break;
5564             
5565         }
5566         this.convert = cv;
5567     }
5568 };
5569
5570 Roo.data.Field.prototype = {
5571     dateFormat: null,
5572     defaultValue: "",
5573     mapping: null,
5574     sortType : null,
5575     sortDir : "ASC"
5576 };/*
5577  * Based on:
5578  * Ext JS Library 1.1.1
5579  * Copyright(c) 2006-2007, Ext JS, LLC.
5580  *
5581  * Originally Released Under LGPL - original licence link has changed is not relivant.
5582  *
5583  * Fork - LGPL
5584  * <script type="text/javascript">
5585  */
5586  
5587 // Base class for reading structured data from a data source.  This class is intended to be
5588 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5589
5590 /**
5591  * @class Roo.data.DataReader
5592  * Base class for reading structured data from a data source.  This class is intended to be
5593  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5594  */
5595
5596 Roo.data.DataReader = function(meta, recordType){
5597     
5598     this.meta = meta;
5599     
5600     this.recordType = recordType instanceof Array ? 
5601         Roo.data.Record.create(recordType) : recordType;
5602 };
5603
5604 Roo.data.DataReader.prototype = {
5605      /**
5606      * Create an empty record
5607      * @param {Object} data (optional) - overlay some values
5608      * @return {Roo.data.Record} record created.
5609      */
5610     newRow :  function(d) {
5611         var da =  {};
5612         this.recordType.prototype.fields.each(function(c) {
5613             switch( c.type) {
5614                 case 'int' : da[c.name] = 0; break;
5615                 case 'date' : da[c.name] = new Date(); break;
5616                 case 'float' : da[c.name] = 0.0; break;
5617                 case 'boolean' : da[c.name] = false; break;
5618                 default : da[c.name] = ""; break;
5619             }
5620             
5621         });
5622         return new this.recordType(Roo.apply(da, d));
5623     }
5624     
5625 };/*
5626  * Based on:
5627  * Ext JS Library 1.1.1
5628  * Copyright(c) 2006-2007, Ext JS, LLC.
5629  *
5630  * Originally Released Under LGPL - original licence link has changed is not relivant.
5631  *
5632  * Fork - LGPL
5633  * <script type="text/javascript">
5634  */
5635
5636 /**
5637  * @class Roo.data.DataProxy
5638  * @extends Roo.data.Observable
5639  * This class is an abstract base class for implementations which provide retrieval of
5640  * unformatted data objects.<br>
5641  * <p>
5642  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5643  * (of the appropriate type which knows how to parse the data object) to provide a block of
5644  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5645  * <p>
5646  * Custom implementations must implement the load method as described in
5647  * {@link Roo.data.HttpProxy#load}.
5648  */
5649 Roo.data.DataProxy = function(){
5650     this.addEvents({
5651         /**
5652          * @event beforeload
5653          * Fires before a network request is made to retrieve a data object.
5654          * @param {Object} This DataProxy object.
5655          * @param {Object} params The params parameter to the load function.
5656          */
5657         beforeload : true,
5658         /**
5659          * @event load
5660          * Fires before the load method's callback is called.
5661          * @param {Object} This DataProxy object.
5662          * @param {Object} o The data object.
5663          * @param {Object} arg The callback argument object passed to the load function.
5664          */
5665         load : true,
5666         /**
5667          * @event loadexception
5668          * Fires if an Exception occurs during data retrieval.
5669          * @param {Object} This DataProxy object.
5670          * @param {Object} o The data object.
5671          * @param {Object} arg The callback argument object passed to the load function.
5672          * @param {Object} e The Exception.
5673          */
5674         loadexception : true
5675     });
5676     Roo.data.DataProxy.superclass.constructor.call(this);
5677 };
5678
5679 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5680
5681     /**
5682      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5683      */
5684 /*
5685  * Based on:
5686  * Ext JS Library 1.1.1
5687  * Copyright(c) 2006-2007, Ext JS, LLC.
5688  *
5689  * Originally Released Under LGPL - original licence link has changed is not relivant.
5690  *
5691  * Fork - LGPL
5692  * <script type="text/javascript">
5693  */
5694 /**
5695  * @class Roo.data.MemoryProxy
5696  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5697  * to the Reader when its load method is called.
5698  * @constructor
5699  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5700  */
5701 Roo.data.MemoryProxy = function(data){
5702     if (data.data) {
5703         data = data.data;
5704     }
5705     Roo.data.MemoryProxy.superclass.constructor.call(this);
5706     this.data = data;
5707 };
5708
5709 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5710     /**
5711      * Load data from the requested source (in this case an in-memory
5712      * data object passed to the constructor), read the data object into
5713      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5714      * process that block using the passed callback.
5715      * @param {Object} params This parameter is not used by the MemoryProxy class.
5716      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5717      * object into a block of Roo.data.Records.
5718      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5719      * The function must be passed <ul>
5720      * <li>The Record block object</li>
5721      * <li>The "arg" argument from the load function</li>
5722      * <li>A boolean success indicator</li>
5723      * </ul>
5724      * @param {Object} scope The scope in which to call the callback
5725      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5726      */
5727     load : function(params, reader, callback, scope, arg){
5728         params = params || {};
5729         var result;
5730         try {
5731             result = reader.readRecords(this.data);
5732         }catch(e){
5733             this.fireEvent("loadexception", this, arg, null, e);
5734             callback.call(scope, null, arg, false);
5735             return;
5736         }
5737         callback.call(scope, result, arg, true);
5738     },
5739     
5740     // private
5741     update : function(params, records){
5742         
5743     }
5744 });/*
5745  * Based on:
5746  * Ext JS Library 1.1.1
5747  * Copyright(c) 2006-2007, Ext JS, LLC.
5748  *
5749  * Originally Released Under LGPL - original licence link has changed is not relivant.
5750  *
5751  * Fork - LGPL
5752  * <script type="text/javascript">
5753  */
5754 /**
5755  * @class Roo.data.HttpProxy
5756  * @extends Roo.data.DataProxy
5757  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5758  * configured to reference a certain URL.<br><br>
5759  * <p>
5760  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5761  * from which the running page was served.<br><br>
5762  * <p>
5763  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5764  * <p>
5765  * Be aware that to enable the browser to parse an XML document, the server must set
5766  * the Content-Type header in the HTTP response to "text/xml".
5767  * @constructor
5768  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5769  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5770  * will be used to make the request.
5771  */
5772 Roo.data.HttpProxy = function(conn){
5773     Roo.data.HttpProxy.superclass.constructor.call(this);
5774     // is conn a conn config or a real conn?
5775     this.conn = conn;
5776     this.useAjax = !conn || !conn.events;
5777   
5778 };
5779
5780 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5781     // thse are take from connection...
5782     
5783     /**
5784      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5785      */
5786     /**
5787      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5788      * extra parameters to each request made by this object. (defaults to undefined)
5789      */
5790     /**
5791      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5792      *  to each request made by this object. (defaults to undefined)
5793      */
5794     /**
5795      * @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)
5796      */
5797     /**
5798      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5799      */
5800      /**
5801      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5802      * @type Boolean
5803      */
5804   
5805
5806     /**
5807      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5808      * @type Boolean
5809      */
5810     /**
5811      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5812      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5813      * a finer-grained basis than the DataProxy events.
5814      */
5815     getConnection : function(){
5816         return this.useAjax ? Roo.Ajax : this.conn;
5817     },
5818
5819     /**
5820      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5821      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5822      * process that block using the passed callback.
5823      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5824      * for the request to the remote server.
5825      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5826      * object into a block of Roo.data.Records.
5827      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5828      * The function must be passed <ul>
5829      * <li>The Record block object</li>
5830      * <li>The "arg" argument from the load function</li>
5831      * <li>A boolean success indicator</li>
5832      * </ul>
5833      * @param {Object} scope The scope in which to call the callback
5834      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5835      */
5836     load : function(params, reader, callback, scope, arg){
5837         if(this.fireEvent("beforeload", this, params) !== false){
5838             var  o = {
5839                 params : params || {},
5840                 request: {
5841                     callback : callback,
5842                     scope : scope,
5843                     arg : arg
5844                 },
5845                 reader: reader,
5846                 callback : this.loadResponse,
5847                 scope: this
5848             };
5849             if(this.useAjax){
5850                 Roo.applyIf(o, this.conn);
5851                 if(this.activeRequest){
5852                     Roo.Ajax.abort(this.activeRequest);
5853                 }
5854                 this.activeRequest = Roo.Ajax.request(o);
5855             }else{
5856                 this.conn.request(o);
5857             }
5858         }else{
5859             callback.call(scope||this, null, arg, false);
5860         }
5861     },
5862
5863     // private
5864     loadResponse : function(o, success, response){
5865         delete this.activeRequest;
5866         if(!success){
5867             this.fireEvent("loadexception", this, o, response);
5868             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5869             return;
5870         }
5871         var result;
5872         try {
5873             result = o.reader.read(response);
5874         }catch(e){
5875             this.fireEvent("loadexception", this, o, response, e);
5876             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5877             return;
5878         }
5879         
5880         this.fireEvent("load", this, o, o.request.arg);
5881         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5882     },
5883
5884     // private
5885     update : function(dataSet){
5886
5887     },
5888
5889     // private
5890     updateResponse : function(dataSet){
5891
5892     }
5893 });/*
5894  * Based on:
5895  * Ext JS Library 1.1.1
5896  * Copyright(c) 2006-2007, Ext JS, LLC.
5897  *
5898  * Originally Released Under LGPL - original licence link has changed is not relivant.
5899  *
5900  * Fork - LGPL
5901  * <script type="text/javascript">
5902  */
5903
5904 /**
5905  * @class Roo.data.ScriptTagProxy
5906  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5907  * other than the originating domain of the running page.<br><br>
5908  * <p>
5909  * <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
5910  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5911  * <p>
5912  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5913  * source code that is used as the source inside a &lt;script> tag.<br><br>
5914  * <p>
5915  * In order for the browser to process the returned data, the server must wrap the data object
5916  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5917  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5918  * depending on whether the callback name was passed:
5919  * <p>
5920  * <pre><code>
5921 boolean scriptTag = false;
5922 String cb = request.getParameter("callback");
5923 if (cb != null) {
5924     scriptTag = true;
5925     response.setContentType("text/javascript");
5926 } else {
5927     response.setContentType("application/x-json");
5928 }
5929 Writer out = response.getWriter();
5930 if (scriptTag) {
5931     out.write(cb + "(");
5932 }
5933 out.print(dataBlock.toJsonString());
5934 if (scriptTag) {
5935     out.write(");");
5936 }
5937 </pre></code>
5938  *
5939  * @constructor
5940  * @param {Object} config A configuration object.
5941  */
5942 Roo.data.ScriptTagProxy = function(config){
5943     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5944     Roo.apply(this, config);
5945     this.head = document.getElementsByTagName("head")[0];
5946 };
5947
5948 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5949
5950 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5951     /**
5952      * @cfg {String} url The URL from which to request the data object.
5953      */
5954     /**
5955      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5956      */
5957     timeout : 30000,
5958     /**
5959      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5960      * the server the name of the callback function set up by the load call to process the returned data object.
5961      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5962      * javascript output which calls this named function passing the data object as its only parameter.
5963      */
5964     callbackParam : "callback",
5965     /**
5966      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5967      * name to the request.
5968      */
5969     nocache : true,
5970
5971     /**
5972      * Load data from the configured URL, read the data object into
5973      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5974      * process that block using the passed callback.
5975      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5976      * for the request to the remote server.
5977      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5978      * object into a block of Roo.data.Records.
5979      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5980      * The function must be passed <ul>
5981      * <li>The Record block object</li>
5982      * <li>The "arg" argument from the load function</li>
5983      * <li>A boolean success indicator</li>
5984      * </ul>
5985      * @param {Object} scope The scope in which to call the callback
5986      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5987      */
5988     load : function(params, reader, callback, scope, arg){
5989         if(this.fireEvent("beforeload", this, params) !== false){
5990
5991             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5992
5993             var url = this.url;
5994             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5995             if(this.nocache){
5996                 url += "&_dc=" + (new Date().getTime());
5997             }
5998             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5999             var trans = {
6000                 id : transId,
6001                 cb : "stcCallback"+transId,
6002                 scriptId : "stcScript"+transId,
6003                 params : params,
6004                 arg : arg,
6005                 url : url,
6006                 callback : callback,
6007                 scope : scope,
6008                 reader : reader
6009             };
6010             var conn = this;
6011
6012             window[trans.cb] = function(o){
6013                 conn.handleResponse(o, trans);
6014             };
6015
6016             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6017
6018             if(this.autoAbort !== false){
6019                 this.abort();
6020             }
6021
6022             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6023
6024             var script = document.createElement("script");
6025             script.setAttribute("src", url);
6026             script.setAttribute("type", "text/javascript");
6027             script.setAttribute("id", trans.scriptId);
6028             this.head.appendChild(script);
6029
6030             this.trans = trans;
6031         }else{
6032             callback.call(scope||this, null, arg, false);
6033         }
6034     },
6035
6036     // private
6037     isLoading : function(){
6038         return this.trans ? true : false;
6039     },
6040
6041     /**
6042      * Abort the current server request.
6043      */
6044     abort : function(){
6045         if(this.isLoading()){
6046             this.destroyTrans(this.trans);
6047         }
6048     },
6049
6050     // private
6051     destroyTrans : function(trans, isLoaded){
6052         this.head.removeChild(document.getElementById(trans.scriptId));
6053         clearTimeout(trans.timeoutId);
6054         if(isLoaded){
6055             window[trans.cb] = undefined;
6056             try{
6057                 delete window[trans.cb];
6058             }catch(e){}
6059         }else{
6060             // if hasn't been loaded, wait for load to remove it to prevent script error
6061             window[trans.cb] = function(){
6062                 window[trans.cb] = undefined;
6063                 try{
6064                     delete window[trans.cb];
6065                 }catch(e){}
6066             };
6067         }
6068     },
6069
6070     // private
6071     handleResponse : function(o, trans){
6072         this.trans = false;
6073         this.destroyTrans(trans, true);
6074         var result;
6075         try {
6076             result = trans.reader.readRecords(o);
6077         }catch(e){
6078             this.fireEvent("loadexception", this, o, trans.arg, e);
6079             trans.callback.call(trans.scope||window, null, trans.arg, false);
6080             return;
6081         }
6082         this.fireEvent("load", this, o, trans.arg);
6083         trans.callback.call(trans.scope||window, result, trans.arg, true);
6084     },
6085
6086     // private
6087     handleFailure : function(trans){
6088         this.trans = false;
6089         this.destroyTrans(trans, false);
6090         this.fireEvent("loadexception", this, null, trans.arg);
6091         trans.callback.call(trans.scope||window, null, trans.arg, false);
6092     }
6093 });/*
6094  * Based on:
6095  * Ext JS Library 1.1.1
6096  * Copyright(c) 2006-2007, Ext JS, LLC.
6097  *
6098  * Originally Released Under LGPL - original licence link has changed is not relivant.
6099  *
6100  * Fork - LGPL
6101  * <script type="text/javascript">
6102  */
6103
6104 /**
6105  * @class Roo.data.JsonReader
6106  * @extends Roo.data.DataReader
6107  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6108  * based on mappings in a provided Roo.data.Record constructor.
6109  * 
6110  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6111  * in the reply previously. 
6112  * 
6113  * <p>
6114  * Example code:
6115  * <pre><code>
6116 var RecordDef = Roo.data.Record.create([
6117     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6118     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6119 ]);
6120 var myReader = new Roo.data.JsonReader({
6121     totalProperty: "results",    // The property which contains the total dataset size (optional)
6122     root: "rows",                // The property which contains an Array of row objects
6123     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6124 }, RecordDef);
6125 </code></pre>
6126  * <p>
6127  * This would consume a JSON file like this:
6128  * <pre><code>
6129 { 'results': 2, 'rows': [
6130     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6131     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6132 }
6133 </code></pre>
6134  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6135  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6136  * paged from the remote server.
6137  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6138  * @cfg {String} root name of the property which contains the Array of row objects.
6139  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6140  * @constructor
6141  * Create a new JsonReader
6142  * @param {Object} meta Metadata configuration options
6143  * @param {Object} recordType Either an Array of field definition objects,
6144  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6145  */
6146 Roo.data.JsonReader = function(meta, recordType){
6147     
6148     meta = meta || {};
6149     // set some defaults:
6150     Roo.applyIf(meta, {
6151         totalProperty: 'total',
6152         successProperty : 'success',
6153         root : 'data',
6154         id : 'id'
6155     });
6156     
6157     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6158 };
6159 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6160     
6161     /**
6162      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6163      * Used by Store query builder to append _requestMeta to params.
6164      * 
6165      */
6166     metaFromRemote : false,
6167     /**
6168      * This method is only used by a DataProxy which has retrieved data from a remote server.
6169      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6170      * @return {Object} data A data block which is used by an Roo.data.Store object as
6171      * a cache of Roo.data.Records.
6172      */
6173     read : function(response){
6174         var json = response.responseText;
6175        
6176         var o = /* eval:var:o */ eval("("+json+")");
6177         if(!o) {
6178             throw {message: "JsonReader.read: Json object not found"};
6179         }
6180         
6181         if(o.metaData){
6182             
6183             delete this.ef;
6184             this.metaFromRemote = true;
6185             this.meta = o.metaData;
6186             this.recordType = Roo.data.Record.create(o.metaData.fields);
6187             this.onMetaChange(this.meta, this.recordType, o);
6188         }
6189         return this.readRecords(o);
6190     },
6191
6192     // private function a store will implement
6193     onMetaChange : function(meta, recordType, o){
6194
6195     },
6196
6197     /**
6198          * @ignore
6199          */
6200     simpleAccess: function(obj, subsc) {
6201         return obj[subsc];
6202     },
6203
6204         /**
6205          * @ignore
6206          */
6207     getJsonAccessor: function(){
6208         var re = /[\[\.]/;
6209         return function(expr) {
6210             try {
6211                 return(re.test(expr))
6212                     ? new Function("obj", "return obj." + expr)
6213                     : function(obj){
6214                         return obj[expr];
6215                     };
6216             } catch(e){}
6217             return Roo.emptyFn;
6218         };
6219     }(),
6220
6221     /**
6222      * Create a data block containing Roo.data.Records from an XML document.
6223      * @param {Object} o An object which contains an Array of row objects in the property specified
6224      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6225      * which contains the total size of the dataset.
6226      * @return {Object} data A data block which is used by an Roo.data.Store object as
6227      * a cache of Roo.data.Records.
6228      */
6229     readRecords : function(o){
6230         /**
6231          * After any data loads, the raw JSON data is available for further custom processing.
6232          * @type Object
6233          */
6234         this.o = o;
6235         var s = this.meta, Record = this.recordType,
6236             f = Record.prototype.fields, fi = f.items, fl = f.length;
6237
6238 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6239         if (!this.ef) {
6240             if(s.totalProperty) {
6241                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6242                 }
6243                 if(s.successProperty) {
6244                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6245                 }
6246                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6247                 if (s.id) {
6248                         var g = this.getJsonAccessor(s.id);
6249                         this.getId = function(rec) {
6250                                 var r = g(rec);
6251                                 return (r === undefined || r === "") ? null : r;
6252                         };
6253                 } else {
6254                         this.getId = function(){return null;};
6255                 }
6256             this.ef = [];
6257             for(var jj = 0; jj < fl; jj++){
6258                 f = fi[jj];
6259                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6260                 this.ef[jj] = this.getJsonAccessor(map);
6261             }
6262         }
6263
6264         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6265         if(s.totalProperty){
6266             var vt = parseInt(this.getTotal(o), 10);
6267             if(!isNaN(vt)){
6268                 totalRecords = vt;
6269             }
6270         }
6271         if(s.successProperty){
6272             var vs = this.getSuccess(o);
6273             if(vs === false || vs === 'false'){
6274                 success = false;
6275             }
6276         }
6277         var records = [];
6278             for(var i = 0; i < c; i++){
6279                     var n = root[i];
6280                 var values = {};
6281                 var id = this.getId(n);
6282                 for(var j = 0; j < fl; j++){
6283                     f = fi[j];
6284                 var v = this.ef[j](n);
6285                 if (!f.convert) {
6286                     Roo.log('missing convert for ' + f.name);
6287                     Roo.log(f);
6288                     continue;
6289                 }
6290                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6291                 }
6292                 var record = new Record(values, id);
6293                 record.json = n;
6294                 records[i] = record;
6295             }
6296             return {
6297             raw : o,
6298                 success : success,
6299                 records : records,
6300                 totalRecords : totalRecords
6301             };
6302     }
6303 });/*
6304  * Based on:
6305  * Ext JS Library 1.1.1
6306  * Copyright(c) 2006-2007, Ext JS, LLC.
6307  *
6308  * Originally Released Under LGPL - original licence link has changed is not relivant.
6309  *
6310  * Fork - LGPL
6311  * <script type="text/javascript">
6312  */
6313
6314 /**
6315  * @class Roo.data.XmlReader
6316  * @extends Roo.data.DataReader
6317  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6318  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6319  * <p>
6320  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6321  * header in the HTTP response must be set to "text/xml".</em>
6322  * <p>
6323  * Example code:
6324  * <pre><code>
6325 var RecordDef = Roo.data.Record.create([
6326    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6327    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6328 ]);
6329 var myReader = new Roo.data.XmlReader({
6330    totalRecords: "results", // The element which contains the total dataset size (optional)
6331    record: "row",           // The repeated element which contains row information
6332    id: "id"                 // The element within the row that provides an ID for the record (optional)
6333 }, RecordDef);
6334 </code></pre>
6335  * <p>
6336  * This would consume an XML file like this:
6337  * <pre><code>
6338 &lt;?xml?>
6339 &lt;dataset>
6340  &lt;results>2&lt;/results>
6341  &lt;row>
6342    &lt;id>1&lt;/id>
6343    &lt;name>Bill&lt;/name>
6344    &lt;occupation>Gardener&lt;/occupation>
6345  &lt;/row>
6346  &lt;row>
6347    &lt;id>2&lt;/id>
6348    &lt;name>Ben&lt;/name>
6349    &lt;occupation>Horticulturalist&lt;/occupation>
6350  &lt;/row>
6351 &lt;/dataset>
6352 </code></pre>
6353  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6354  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6355  * paged from the remote server.
6356  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6357  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6358  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6359  * a record identifier value.
6360  * @constructor
6361  * Create a new XmlReader
6362  * @param {Object} meta Metadata configuration options
6363  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6364  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6365  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6366  */
6367 Roo.data.XmlReader = function(meta, recordType){
6368     meta = meta || {};
6369     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6370 };
6371 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6372     /**
6373      * This method is only used by a DataProxy which has retrieved data from a remote server.
6374          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6375          * to contain a method called 'responseXML' that returns an XML document object.
6376      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6377      * a cache of Roo.data.Records.
6378      */
6379     read : function(response){
6380         var doc = response.responseXML;
6381         if(!doc) {
6382             throw {message: "XmlReader.read: XML Document not available"};
6383         }
6384         return this.readRecords(doc);
6385     },
6386
6387     /**
6388      * Create a data block containing Roo.data.Records from an XML document.
6389          * @param {Object} doc A parsed XML document.
6390      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6391      * a cache of Roo.data.Records.
6392      */
6393     readRecords : function(doc){
6394         /**
6395          * After any data loads/reads, the raw XML Document is available for further custom processing.
6396          * @type XMLDocument
6397          */
6398         this.xmlData = doc;
6399         var root = doc.documentElement || doc;
6400         var q = Roo.DomQuery;
6401         var recordType = this.recordType, fields = recordType.prototype.fields;
6402         var sid = this.meta.id;
6403         var totalRecords = 0, success = true;
6404         if(this.meta.totalRecords){
6405             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6406         }
6407         
6408         if(this.meta.success){
6409             var sv = q.selectValue(this.meta.success, root, true);
6410             success = sv !== false && sv !== 'false';
6411         }
6412         var records = [];
6413         var ns = q.select(this.meta.record, root);
6414         for(var i = 0, len = ns.length; i < len; i++) {
6415                 var n = ns[i];
6416                 var values = {};
6417                 var id = sid ? q.selectValue(sid, n) : undefined;
6418                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6419                     var f = fields.items[j];
6420                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6421                     v = f.convert(v);
6422                     values[f.name] = v;
6423                 }
6424                 var record = new recordType(values, id);
6425                 record.node = n;
6426                 records[records.length] = record;
6427             }
6428
6429             return {
6430                 success : success,
6431                 records : records,
6432                 totalRecords : totalRecords || records.length
6433             };
6434     }
6435 });/*
6436  * Based on:
6437  * Ext JS Library 1.1.1
6438  * Copyright(c) 2006-2007, Ext JS, LLC.
6439  *
6440  * Originally Released Under LGPL - original licence link has changed is not relivant.
6441  *
6442  * Fork - LGPL
6443  * <script type="text/javascript">
6444  */
6445
6446 /**
6447  * @class Roo.data.ArrayReader
6448  * @extends Roo.data.DataReader
6449  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6450  * Each element of that Array represents a row of data fields. The
6451  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6452  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6453  * <p>
6454  * Example code:.
6455  * <pre><code>
6456 var RecordDef = Roo.data.Record.create([
6457     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6458     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6459 ]);
6460 var myReader = new Roo.data.ArrayReader({
6461     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6462 }, RecordDef);
6463 </code></pre>
6464  * <p>
6465  * This would consume an Array like this:
6466  * <pre><code>
6467 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6468   </code></pre>
6469  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6470  * @constructor
6471  * Create a new JsonReader
6472  * @param {Object} meta Metadata configuration options.
6473  * @param {Object} recordType Either an Array of field definition objects
6474  * as specified to {@link Roo.data.Record#create},
6475  * or an {@link Roo.data.Record} object
6476  * created using {@link Roo.data.Record#create}.
6477  */
6478 Roo.data.ArrayReader = function(meta, recordType){
6479     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6480 };
6481
6482 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6483     /**
6484      * Create a data block containing Roo.data.Records from an XML document.
6485      * @param {Object} o An Array of row objects which represents the dataset.
6486      * @return {Object} data A data block which is used by an Roo.data.Store object as
6487      * a cache of Roo.data.Records.
6488      */
6489     readRecords : function(o){
6490         var sid = this.meta ? this.meta.id : null;
6491         var recordType = this.recordType, fields = recordType.prototype.fields;
6492         var records = [];
6493         var root = o;
6494             for(var i = 0; i < root.length; i++){
6495                     var n = root[i];
6496                 var values = {};
6497                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6498                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6499                 var f = fields.items[j];
6500                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6501                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6502                 v = f.convert(v);
6503                 values[f.name] = v;
6504             }
6505                 var record = new recordType(values, id);
6506                 record.json = n;
6507                 records[records.length] = record;
6508             }
6509             return {
6510                 records : records,
6511                 totalRecords : records.length
6512             };
6513     }
6514 });/*
6515  * Based on:
6516  * Ext JS Library 1.1.1
6517  * Copyright(c) 2006-2007, Ext JS, LLC.
6518  *
6519  * Originally Released Under LGPL - original licence link has changed is not relivant.
6520  *
6521  * Fork - LGPL
6522  * <script type="text/javascript">
6523  */
6524
6525
6526 /**
6527  * @class Roo.data.Tree
6528  * @extends Roo.util.Observable
6529  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6530  * in the tree have most standard DOM functionality.
6531  * @constructor
6532  * @param {Node} root (optional) The root node
6533  */
6534 Roo.data.Tree = function(root){
6535    this.nodeHash = {};
6536    /**
6537     * The root node for this tree
6538     * @type Node
6539     */
6540    this.root = null;
6541    if(root){
6542        this.setRootNode(root);
6543    }
6544    this.addEvents({
6545        /**
6546         * @event append
6547         * Fires when a new child node is appended to a node in this tree.
6548         * @param {Tree} tree The owner tree
6549         * @param {Node} parent The parent node
6550         * @param {Node} node The newly appended node
6551         * @param {Number} index The index of the newly appended node
6552         */
6553        "append" : true,
6554        /**
6555         * @event remove
6556         * Fires when a child node is removed from a node in this tree.
6557         * @param {Tree} tree The owner tree
6558         * @param {Node} parent The parent node
6559         * @param {Node} node The child node removed
6560         */
6561        "remove" : true,
6562        /**
6563         * @event move
6564         * Fires when a node is moved to a new location in the tree
6565         * @param {Tree} tree The owner tree
6566         * @param {Node} node The node moved
6567         * @param {Node} oldParent The old parent of this node
6568         * @param {Node} newParent The new parent of this node
6569         * @param {Number} index The index it was moved to
6570         */
6571        "move" : true,
6572        /**
6573         * @event insert
6574         * Fires when a new child node is inserted in a node in this tree.
6575         * @param {Tree} tree The owner tree
6576         * @param {Node} parent The parent node
6577         * @param {Node} node The child node inserted
6578         * @param {Node} refNode The child node the node was inserted before
6579         */
6580        "insert" : true,
6581        /**
6582         * @event beforeappend
6583         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The child node to be appended
6587         */
6588        "beforeappend" : true,
6589        /**
6590         * @event beforeremove
6591         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6592         * @param {Tree} tree The owner tree
6593         * @param {Node} parent The parent node
6594         * @param {Node} node The child node to be removed
6595         */
6596        "beforeremove" : true,
6597        /**
6598         * @event beforemove
6599         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6600         * @param {Tree} tree The owner tree
6601         * @param {Node} node The node being moved
6602         * @param {Node} oldParent The parent of the node
6603         * @param {Node} newParent The new parent the node is moving to
6604         * @param {Number} index The index it is being moved to
6605         */
6606        "beforemove" : true,
6607        /**
6608         * @event beforeinsert
6609         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6610         * @param {Tree} tree The owner tree
6611         * @param {Node} parent The parent node
6612         * @param {Node} node The child node to be inserted
6613         * @param {Node} refNode The child node the node is being inserted before
6614         */
6615        "beforeinsert" : true
6616    });
6617
6618     Roo.data.Tree.superclass.constructor.call(this);
6619 };
6620
6621 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6622     pathSeparator: "/",
6623
6624     proxyNodeEvent : function(){
6625         return this.fireEvent.apply(this, arguments);
6626     },
6627
6628     /**
6629      * Returns the root node for this tree.
6630      * @return {Node}
6631      */
6632     getRootNode : function(){
6633         return this.root;
6634     },
6635
6636     /**
6637      * Sets the root node for this tree.
6638      * @param {Node} node
6639      * @return {Node}
6640      */
6641     setRootNode : function(node){
6642         this.root = node;
6643         node.ownerTree = this;
6644         node.isRoot = true;
6645         this.registerNode(node);
6646         return node;
6647     },
6648
6649     /**
6650      * Gets a node in this tree by its id.
6651      * @param {String} id
6652      * @return {Node}
6653      */
6654     getNodeById : function(id){
6655         return this.nodeHash[id];
6656     },
6657
6658     registerNode : function(node){
6659         this.nodeHash[node.id] = node;
6660     },
6661
6662     unregisterNode : function(node){
6663         delete this.nodeHash[node.id];
6664     },
6665
6666     toString : function(){
6667         return "[Tree"+(this.id?" "+this.id:"")+"]";
6668     }
6669 });
6670
6671 /**
6672  * @class Roo.data.Node
6673  * @extends Roo.util.Observable
6674  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6675  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6676  * @constructor
6677  * @param {Object} attributes The attributes/config for the node
6678  */
6679 Roo.data.Node = function(attributes){
6680     /**
6681      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6682      * @type {Object}
6683      */
6684     this.attributes = attributes || {};
6685     this.leaf = this.attributes.leaf;
6686     /**
6687      * The node id. @type String
6688      */
6689     this.id = this.attributes.id;
6690     if(!this.id){
6691         this.id = Roo.id(null, "ynode-");
6692         this.attributes.id = this.id;
6693     }
6694      
6695     
6696     /**
6697      * All child nodes of this node. @type Array
6698      */
6699     this.childNodes = [];
6700     if(!this.childNodes.indexOf){ // indexOf is a must
6701         this.childNodes.indexOf = function(o){
6702             for(var i = 0, len = this.length; i < len; i++){
6703                 if(this[i] == o) {
6704                     return i;
6705                 }
6706             }
6707             return -1;
6708         };
6709     }
6710     /**
6711      * The parent node for this node. @type Node
6712      */
6713     this.parentNode = null;
6714     /**
6715      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6716      */
6717     this.firstChild = null;
6718     /**
6719      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6720      */
6721     this.lastChild = null;
6722     /**
6723      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6724      */
6725     this.previousSibling = null;
6726     /**
6727      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6728      */
6729     this.nextSibling = null;
6730
6731     this.addEvents({
6732        /**
6733         * @event append
6734         * Fires when a new child node is appended
6735         * @param {Tree} tree The owner tree
6736         * @param {Node} this This node
6737         * @param {Node} node The newly appended node
6738         * @param {Number} index The index of the newly appended node
6739         */
6740        "append" : true,
6741        /**
6742         * @event remove
6743         * Fires when a child node is removed
6744         * @param {Tree} tree The owner tree
6745         * @param {Node} this This node
6746         * @param {Node} node The removed node
6747         */
6748        "remove" : true,
6749        /**
6750         * @event move
6751         * Fires when this node is moved to a new location in the tree
6752         * @param {Tree} tree The owner tree
6753         * @param {Node} this This node
6754         * @param {Node} oldParent The old parent of this node
6755         * @param {Node} newParent The new parent of this node
6756         * @param {Number} index The index it was moved to
6757         */
6758        "move" : true,
6759        /**
6760         * @event insert
6761         * Fires when a new child node is inserted.
6762         * @param {Tree} tree The owner tree
6763         * @param {Node} this This node
6764         * @param {Node} node The child node inserted
6765         * @param {Node} refNode The child node the node was inserted before
6766         */
6767        "insert" : true,
6768        /**
6769         * @event beforeappend
6770         * Fires before a new child is appended, return false to cancel the append.
6771         * @param {Tree} tree The owner tree
6772         * @param {Node} this This node
6773         * @param {Node} node The child node to be appended
6774         */
6775        "beforeappend" : true,
6776        /**
6777         * @event beforeremove
6778         * Fires before a child is removed, return false to cancel the remove.
6779         * @param {Tree} tree The owner tree
6780         * @param {Node} this This node
6781         * @param {Node} node The child node to be removed
6782         */
6783        "beforeremove" : true,
6784        /**
6785         * @event beforemove
6786         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6787         * @param {Tree} tree The owner tree
6788         * @param {Node} this This node
6789         * @param {Node} oldParent The parent of this node
6790         * @param {Node} newParent The new parent this node is moving to
6791         * @param {Number} index The index it is being moved to
6792         */
6793        "beforemove" : true,
6794        /**
6795         * @event beforeinsert
6796         * Fires before a new child is inserted, return false to cancel the insert.
6797         * @param {Tree} tree The owner tree
6798         * @param {Node} this This node
6799         * @param {Node} node The child node to be inserted
6800         * @param {Node} refNode The child node the node is being inserted before
6801         */
6802        "beforeinsert" : true
6803    });
6804     this.listeners = this.attributes.listeners;
6805     Roo.data.Node.superclass.constructor.call(this);
6806 };
6807
6808 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6809     fireEvent : function(evtName){
6810         // first do standard event for this node
6811         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6812             return false;
6813         }
6814         // then bubble it up to the tree if the event wasn't cancelled
6815         var ot = this.getOwnerTree();
6816         if(ot){
6817             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6818                 return false;
6819             }
6820         }
6821         return true;
6822     },
6823
6824     /**
6825      * Returns true if this node is a leaf
6826      * @return {Boolean}
6827      */
6828     isLeaf : function(){
6829         return this.leaf === true;
6830     },
6831
6832     // private
6833     setFirstChild : function(node){
6834         this.firstChild = node;
6835     },
6836
6837     //private
6838     setLastChild : function(node){
6839         this.lastChild = node;
6840     },
6841
6842
6843     /**
6844      * Returns true if this node is the last child of its parent
6845      * @return {Boolean}
6846      */
6847     isLast : function(){
6848        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6849     },
6850
6851     /**
6852      * Returns true if this node is the first child of its parent
6853      * @return {Boolean}
6854      */
6855     isFirst : function(){
6856        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6857     },
6858
6859     hasChildNodes : function(){
6860         return !this.isLeaf() && this.childNodes.length > 0;
6861     },
6862
6863     /**
6864      * Insert node(s) as the last child node of this node.
6865      * @param {Node/Array} node The node or Array of nodes to append
6866      * @return {Node} The appended node if single append, or null if an array was passed
6867      */
6868     appendChild : function(node){
6869         var multi = false;
6870         if(node instanceof Array){
6871             multi = node;
6872         }else if(arguments.length > 1){
6873             multi = arguments;
6874         }
6875         // if passed an array or multiple args do them one by one
6876         if(multi){
6877             for(var i = 0, len = multi.length; i < len; i++) {
6878                 this.appendChild(multi[i]);
6879             }
6880         }else{
6881             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6882                 return false;
6883             }
6884             var index = this.childNodes.length;
6885             var oldParent = node.parentNode;
6886             // it's a move, make sure we move it cleanly
6887             if(oldParent){
6888                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6889                     return false;
6890                 }
6891                 oldParent.removeChild(node);
6892             }
6893             index = this.childNodes.length;
6894             if(index == 0){
6895                 this.setFirstChild(node);
6896             }
6897             this.childNodes.push(node);
6898             node.parentNode = this;
6899             var ps = this.childNodes[index-1];
6900             if(ps){
6901                 node.previousSibling = ps;
6902                 ps.nextSibling = node;
6903             }else{
6904                 node.previousSibling = null;
6905             }
6906             node.nextSibling = null;
6907             this.setLastChild(node);
6908             node.setOwnerTree(this.getOwnerTree());
6909             this.fireEvent("append", this.ownerTree, this, node, index);
6910             if(oldParent){
6911                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6912             }
6913             return node;
6914         }
6915     },
6916
6917     /**
6918      * Removes a child node from this node.
6919      * @param {Node} node The node to remove
6920      * @return {Node} The removed node
6921      */
6922     removeChild : function(node){
6923         var index = this.childNodes.indexOf(node);
6924         if(index == -1){
6925             return false;
6926         }
6927         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6928             return false;
6929         }
6930
6931         // remove it from childNodes collection
6932         this.childNodes.splice(index, 1);
6933
6934         // update siblings
6935         if(node.previousSibling){
6936             node.previousSibling.nextSibling = node.nextSibling;
6937         }
6938         if(node.nextSibling){
6939             node.nextSibling.previousSibling = node.previousSibling;
6940         }
6941
6942         // update child refs
6943         if(this.firstChild == node){
6944             this.setFirstChild(node.nextSibling);
6945         }
6946         if(this.lastChild == node){
6947             this.setLastChild(node.previousSibling);
6948         }
6949
6950         node.setOwnerTree(null);
6951         // clear any references from the node
6952         node.parentNode = null;
6953         node.previousSibling = null;
6954         node.nextSibling = null;
6955         this.fireEvent("remove", this.ownerTree, this, node);
6956         return node;
6957     },
6958
6959     /**
6960      * Inserts the first node before the second node in this nodes childNodes collection.
6961      * @param {Node} node The node to insert
6962      * @param {Node} refNode The node to insert before (if null the node is appended)
6963      * @return {Node} The inserted node
6964      */
6965     insertBefore : function(node, refNode){
6966         if(!refNode){ // like standard Dom, refNode can be null for append
6967             return this.appendChild(node);
6968         }
6969         // nothing to do
6970         if(node == refNode){
6971             return false;
6972         }
6973
6974         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6975             return false;
6976         }
6977         var index = this.childNodes.indexOf(refNode);
6978         var oldParent = node.parentNode;
6979         var refIndex = index;
6980
6981         // when moving internally, indexes will change after remove
6982         if(oldParent == this && this.childNodes.indexOf(node) < index){
6983             refIndex--;
6984         }
6985
6986         // it's a move, make sure we move it cleanly
6987         if(oldParent){
6988             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6989                 return false;
6990             }
6991             oldParent.removeChild(node);
6992         }
6993         if(refIndex == 0){
6994             this.setFirstChild(node);
6995         }
6996         this.childNodes.splice(refIndex, 0, node);
6997         node.parentNode = this;
6998         var ps = this.childNodes[refIndex-1];
6999         if(ps){
7000             node.previousSibling = ps;
7001             ps.nextSibling = node;
7002         }else{
7003             node.previousSibling = null;
7004         }
7005         node.nextSibling = refNode;
7006         refNode.previousSibling = node;
7007         node.setOwnerTree(this.getOwnerTree());
7008         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7009         if(oldParent){
7010             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7011         }
7012         return node;
7013     },
7014
7015     /**
7016      * Returns the child node at the specified index.
7017      * @param {Number} index
7018      * @return {Node}
7019      */
7020     item : function(index){
7021         return this.childNodes[index];
7022     },
7023
7024     /**
7025      * Replaces one child node in this node with another.
7026      * @param {Node} newChild The replacement node
7027      * @param {Node} oldChild The node to replace
7028      * @return {Node} The replaced node
7029      */
7030     replaceChild : function(newChild, oldChild){
7031         this.insertBefore(newChild, oldChild);
7032         this.removeChild(oldChild);
7033         return oldChild;
7034     },
7035
7036     /**
7037      * Returns the index of a child node
7038      * @param {Node} node
7039      * @return {Number} The index of the node or -1 if it was not found
7040      */
7041     indexOf : function(child){
7042         return this.childNodes.indexOf(child);
7043     },
7044
7045     /**
7046      * Returns the tree this node is in.
7047      * @return {Tree}
7048      */
7049     getOwnerTree : function(){
7050         // if it doesn't have one, look for one
7051         if(!this.ownerTree){
7052             var p = this;
7053             while(p){
7054                 if(p.ownerTree){
7055                     this.ownerTree = p.ownerTree;
7056                     break;
7057                 }
7058                 p = p.parentNode;
7059             }
7060         }
7061         return this.ownerTree;
7062     },
7063
7064     /**
7065      * Returns depth of this node (the root node has a depth of 0)
7066      * @return {Number}
7067      */
7068     getDepth : function(){
7069         var depth = 0;
7070         var p = this;
7071         while(p.parentNode){
7072             ++depth;
7073             p = p.parentNode;
7074         }
7075         return depth;
7076     },
7077
7078     // private
7079     setOwnerTree : function(tree){
7080         // if it's move, we need to update everyone
7081         if(tree != this.ownerTree){
7082             if(this.ownerTree){
7083                 this.ownerTree.unregisterNode(this);
7084             }
7085             this.ownerTree = tree;
7086             var cs = this.childNodes;
7087             for(var i = 0, len = cs.length; i < len; i++) {
7088                 cs[i].setOwnerTree(tree);
7089             }
7090             if(tree){
7091                 tree.registerNode(this);
7092             }
7093         }
7094     },
7095
7096     /**
7097      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7098      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7099      * @return {String} The path
7100      */
7101     getPath : function(attr){
7102         attr = attr || "id";
7103         var p = this.parentNode;
7104         var b = [this.attributes[attr]];
7105         while(p){
7106             b.unshift(p.attributes[attr]);
7107             p = p.parentNode;
7108         }
7109         var sep = this.getOwnerTree().pathSeparator;
7110         return sep + b.join(sep);
7111     },
7112
7113     /**
7114      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7115      * function call will be the scope provided or the current node. The arguments to the function
7116      * will be the args provided or the current node. If the function returns false at any point,
7117      * the bubble is stopped.
7118      * @param {Function} fn The function to call
7119      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7120      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7121      */
7122     bubble : function(fn, scope, args){
7123         var p = this;
7124         while(p){
7125             if(fn.call(scope || p, args || p) === false){
7126                 break;
7127             }
7128             p = p.parentNode;
7129         }
7130     },
7131
7132     /**
7133      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7134      * function call will be the scope provided or the current node. The arguments to the function
7135      * will be the args provided or the current node. If the function returns false at any point,
7136      * the cascade is stopped on that branch.
7137      * @param {Function} fn The function to call
7138      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7139      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7140      */
7141     cascade : function(fn, scope, args){
7142         if(fn.call(scope || this, args || this) !== false){
7143             var cs = this.childNodes;
7144             for(var i = 0, len = cs.length; i < len; i++) {
7145                 cs[i].cascade(fn, scope, args);
7146             }
7147         }
7148     },
7149
7150     /**
7151      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7152      * function call will be the scope provided or the current node. The arguments to the function
7153      * will be the args provided or the current node. If the function returns false at any point,
7154      * the iteration stops.
7155      * @param {Function} fn The function to call
7156      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7157      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7158      */
7159     eachChild : function(fn, scope, args){
7160         var cs = this.childNodes;
7161         for(var i = 0, len = cs.length; i < len; i++) {
7162                 if(fn.call(scope || this, args || cs[i]) === false){
7163                     break;
7164                 }
7165         }
7166     },
7167
7168     /**
7169      * Finds the first child that has the attribute with the specified value.
7170      * @param {String} attribute The attribute name
7171      * @param {Mixed} value The value to search for
7172      * @return {Node} The found child or null if none was found
7173      */
7174     findChild : function(attribute, value){
7175         var cs = this.childNodes;
7176         for(var i = 0, len = cs.length; i < len; i++) {
7177                 if(cs[i].attributes[attribute] == value){
7178                     return cs[i];
7179                 }
7180         }
7181         return null;
7182     },
7183
7184     /**
7185      * Finds the first child by a custom function. The child matches if the function passed
7186      * returns true.
7187      * @param {Function} fn
7188      * @param {Object} scope (optional)
7189      * @return {Node} The found child or null if none was found
7190      */
7191     findChildBy : function(fn, scope){
7192         var cs = this.childNodes;
7193         for(var i = 0, len = cs.length; i < len; i++) {
7194                 if(fn.call(scope||cs[i], cs[i]) === true){
7195                     return cs[i];
7196                 }
7197         }
7198         return null;
7199     },
7200
7201     /**
7202      * Sorts this nodes children using the supplied sort function
7203      * @param {Function} fn
7204      * @param {Object} scope (optional)
7205      */
7206     sort : function(fn, scope){
7207         var cs = this.childNodes;
7208         var len = cs.length;
7209         if(len > 0){
7210             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7211             cs.sort(sortFn);
7212             for(var i = 0; i < len; i++){
7213                 var n = cs[i];
7214                 n.previousSibling = cs[i-1];
7215                 n.nextSibling = cs[i+1];
7216                 if(i == 0){
7217                     this.setFirstChild(n);
7218                 }
7219                 if(i == len-1){
7220                     this.setLastChild(n);
7221                 }
7222             }
7223         }
7224     },
7225
7226     /**
7227      * Returns true if this node is an ancestor (at any point) of the passed node.
7228      * @param {Node} node
7229      * @return {Boolean}
7230      */
7231     contains : function(node){
7232         return node.isAncestor(this);
7233     },
7234
7235     /**
7236      * Returns true if the passed node is an ancestor (at any point) of this node.
7237      * @param {Node} node
7238      * @return {Boolean}
7239      */
7240     isAncestor : function(node){
7241         var p = this.parentNode;
7242         while(p){
7243             if(p == node){
7244                 return true;
7245             }
7246             p = p.parentNode;
7247         }
7248         return false;
7249     },
7250
7251     toString : function(){
7252         return "[Node"+(this.id?" "+this.id:"")+"]";
7253     }
7254 });/*
7255  * Based on:
7256  * Ext JS Library 1.1.1
7257  * Copyright(c) 2006-2007, Ext JS, LLC.
7258  *
7259  * Originally Released Under LGPL - original licence link has changed is not relivant.
7260  *
7261  * Fork - LGPL
7262  * <script type="text/javascript">
7263  */
7264  
7265
7266 /**
7267  * @class Roo.ComponentMgr
7268  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7269  * @singleton
7270  */
7271 Roo.ComponentMgr = function(){
7272     var all = new Roo.util.MixedCollection();
7273
7274     return {
7275         /**
7276          * Registers a component.
7277          * @param {Roo.Component} c The component
7278          */
7279         register : function(c){
7280             all.add(c);
7281         },
7282
7283         /**
7284          * Unregisters a component.
7285          * @param {Roo.Component} c The component
7286          */
7287         unregister : function(c){
7288             all.remove(c);
7289         },
7290
7291         /**
7292          * Returns a component by id
7293          * @param {String} id The component id
7294          */
7295         get : function(id){
7296             return all.get(id);
7297         },
7298
7299         /**
7300          * Registers a function that will be called when a specified component is added to ComponentMgr
7301          * @param {String} id The component id
7302          * @param {Funtction} fn The callback function
7303          * @param {Object} scope The scope of the callback
7304          */
7305         onAvailable : function(id, fn, scope){
7306             all.on("add", function(index, o){
7307                 if(o.id == id){
7308                     fn.call(scope || o, o);
7309                     all.un("add", fn, scope);
7310                 }
7311             });
7312         }
7313     };
7314 }();/*
7315  * Based on:
7316  * Ext JS Library 1.1.1
7317  * Copyright(c) 2006-2007, Ext JS, LLC.
7318  *
7319  * Originally Released Under LGPL - original licence link has changed is not relivant.
7320  *
7321  * Fork - LGPL
7322  * <script type="text/javascript">
7323  */
7324  
7325 /**
7326  * @class Roo.Component
7327  * @extends Roo.util.Observable
7328  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7329  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7330  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7331  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7332  * All visual components (widgets) that require rendering into a layout should subclass Component.
7333  * @constructor
7334  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7335  * 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
7336  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7337  */
7338 Roo.Component = function(config){
7339     config = config || {};
7340     if(config.tagName || config.dom || typeof config == "string"){ // element object
7341         config = {el: config, id: config.id || config};
7342     }
7343     this.initialConfig = config;
7344
7345     Roo.apply(this, config);
7346     this.addEvents({
7347         /**
7348          * @event disable
7349          * Fires after the component is disabled.
7350              * @param {Roo.Component} this
7351              */
7352         disable : true,
7353         /**
7354          * @event enable
7355          * Fires after the component is enabled.
7356              * @param {Roo.Component} this
7357              */
7358         enable : true,
7359         /**
7360          * @event beforeshow
7361          * Fires before the component is shown.  Return false to stop the show.
7362              * @param {Roo.Component} this
7363              */
7364         beforeshow : true,
7365         /**
7366          * @event show
7367          * Fires after the component is shown.
7368              * @param {Roo.Component} this
7369              */
7370         show : true,
7371         /**
7372          * @event beforehide
7373          * Fires before the component is hidden. Return false to stop the hide.
7374              * @param {Roo.Component} this
7375              */
7376         beforehide : true,
7377         /**
7378          * @event hide
7379          * Fires after the component is hidden.
7380              * @param {Roo.Component} this
7381              */
7382         hide : true,
7383         /**
7384          * @event beforerender
7385          * Fires before the component is rendered. Return false to stop the render.
7386              * @param {Roo.Component} this
7387              */
7388         beforerender : true,
7389         /**
7390          * @event render
7391          * Fires after the component is rendered.
7392              * @param {Roo.Component} this
7393              */
7394         render : true,
7395         /**
7396          * @event beforedestroy
7397          * Fires before the component is destroyed. Return false to stop the destroy.
7398              * @param {Roo.Component} this
7399              */
7400         beforedestroy : true,
7401         /**
7402          * @event destroy
7403          * Fires after the component is destroyed.
7404              * @param {Roo.Component} this
7405              */
7406         destroy : true
7407     });
7408     if(!this.id){
7409         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7410     }
7411     Roo.ComponentMgr.register(this);
7412     Roo.Component.superclass.constructor.call(this);
7413     this.initComponent();
7414     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7415         this.render(this.renderTo);
7416         delete this.renderTo;
7417     }
7418 };
7419
7420 /** @private */
7421 Roo.Component.AUTO_ID = 1000;
7422
7423 Roo.extend(Roo.Component, Roo.util.Observable, {
7424     /**
7425      * @scope Roo.Component.prototype
7426      * @type {Boolean}
7427      * true if this component is hidden. Read-only.
7428      */
7429     hidden : false,
7430     /**
7431      * @type {Boolean}
7432      * true if this component is disabled. Read-only.
7433      */
7434     disabled : false,
7435     /**
7436      * @type {Boolean}
7437      * true if this component has been rendered. Read-only.
7438      */
7439     rendered : false,
7440     
7441     /** @cfg {String} disableClass
7442      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7443      */
7444     disabledClass : "x-item-disabled",
7445         /** @cfg {Boolean} allowDomMove
7446          * Whether the component can move the Dom node when rendering (defaults to true).
7447          */
7448     allowDomMove : true,
7449     /** @cfg {String} hideMode
7450      * How this component should hidden. Supported values are
7451      * "visibility" (css visibility), "offsets" (negative offset position) and
7452      * "display" (css display) - defaults to "display".
7453      */
7454     hideMode: 'display',
7455
7456     /** @private */
7457     ctype : "Roo.Component",
7458
7459     /**
7460      * @cfg {String} actionMode 
7461      * which property holds the element that used for  hide() / show() / disable() / enable()
7462      * default is 'el' 
7463      */
7464     actionMode : "el",
7465
7466     /** @private */
7467     getActionEl : function(){
7468         return this[this.actionMode];
7469     },
7470
7471     initComponent : Roo.emptyFn,
7472     /**
7473      * If this is a lazy rendering component, render it to its container element.
7474      * @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.
7475      */
7476     render : function(container, position){
7477         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7478             if(!container && this.el){
7479                 this.el = Roo.get(this.el);
7480                 container = this.el.dom.parentNode;
7481                 this.allowDomMove = false;
7482             }
7483             this.container = Roo.get(container);
7484             this.rendered = true;
7485             if(position !== undefined){
7486                 if(typeof position == 'number'){
7487                     position = this.container.dom.childNodes[position];
7488                 }else{
7489                     position = Roo.getDom(position);
7490                 }
7491             }
7492             this.onRender(this.container, position || null);
7493             if(this.cls){
7494                 this.el.addClass(this.cls);
7495                 delete this.cls;
7496             }
7497             if(this.style){
7498                 this.el.applyStyles(this.style);
7499                 delete this.style;
7500             }
7501             this.fireEvent("render", this);
7502             this.afterRender(this.container);
7503             if(this.hidden){
7504                 this.hide();
7505             }
7506             if(this.disabled){
7507                 this.disable();
7508             }
7509         }
7510         return this;
7511     },
7512
7513     /** @private */
7514     // default function is not really useful
7515     onRender : function(ct, position){
7516         if(this.el){
7517             this.el = Roo.get(this.el);
7518             if(this.allowDomMove !== false){
7519                 ct.dom.insertBefore(this.el.dom, position);
7520             }
7521         }
7522     },
7523
7524     /** @private */
7525     getAutoCreate : function(){
7526         var cfg = typeof this.autoCreate == "object" ?
7527                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7528         if(this.id && !cfg.id){
7529             cfg.id = this.id;
7530         }
7531         return cfg;
7532     },
7533
7534     /** @private */
7535     afterRender : Roo.emptyFn,
7536
7537     /**
7538      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7539      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7540      */
7541     destroy : function(){
7542         if(this.fireEvent("beforedestroy", this) !== false){
7543             this.purgeListeners();
7544             this.beforeDestroy();
7545             if(this.rendered){
7546                 this.el.removeAllListeners();
7547                 this.el.remove();
7548                 if(this.actionMode == "container"){
7549                     this.container.remove();
7550                 }
7551             }
7552             this.onDestroy();
7553             Roo.ComponentMgr.unregister(this);
7554             this.fireEvent("destroy", this);
7555         }
7556     },
7557
7558         /** @private */
7559     beforeDestroy : function(){
7560
7561     },
7562
7563         /** @private */
7564         onDestroy : function(){
7565
7566     },
7567
7568     /**
7569      * Returns the underlying {@link Roo.Element}.
7570      * @return {Roo.Element} The element
7571      */
7572     getEl : function(){
7573         return this.el;
7574     },
7575
7576     /**
7577      * Returns the id of this component.
7578      * @return {String}
7579      */
7580     getId : function(){
7581         return this.id;
7582     },
7583
7584     /**
7585      * Try to focus this component.
7586      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7587      * @return {Roo.Component} this
7588      */
7589     focus : function(selectText){
7590         if(this.rendered){
7591             this.el.focus();
7592             if(selectText === true){
7593                 this.el.dom.select();
7594             }
7595         }
7596         return this;
7597     },
7598
7599     /** @private */
7600     blur : function(){
7601         if(this.rendered){
7602             this.el.blur();
7603         }
7604         return this;
7605     },
7606
7607     /**
7608      * Disable this component.
7609      * @return {Roo.Component} this
7610      */
7611     disable : function(){
7612         if(this.rendered){
7613             this.onDisable();
7614         }
7615         this.disabled = true;
7616         this.fireEvent("disable", this);
7617         return this;
7618     },
7619
7620         // private
7621     onDisable : function(){
7622         this.getActionEl().addClass(this.disabledClass);
7623         this.el.dom.disabled = true;
7624     },
7625
7626     /**
7627      * Enable this component.
7628      * @return {Roo.Component} this
7629      */
7630     enable : function(){
7631         if(this.rendered){
7632             this.onEnable();
7633         }
7634         this.disabled = false;
7635         this.fireEvent("enable", this);
7636         return this;
7637     },
7638
7639         // private
7640     onEnable : function(){
7641         this.getActionEl().removeClass(this.disabledClass);
7642         this.el.dom.disabled = false;
7643     },
7644
7645     /**
7646      * Convenience function for setting disabled/enabled by boolean.
7647      * @param {Boolean} disabled
7648      */
7649     setDisabled : function(disabled){
7650         this[disabled ? "disable" : "enable"]();
7651     },
7652
7653     /**
7654      * Show this component.
7655      * @return {Roo.Component} this
7656      */
7657     show: function(){
7658         if(this.fireEvent("beforeshow", this) !== false){
7659             this.hidden = false;
7660             if(this.rendered){
7661                 this.onShow();
7662             }
7663             this.fireEvent("show", this);
7664         }
7665         return this;
7666     },
7667
7668     // private
7669     onShow : function(){
7670         var ae = this.getActionEl();
7671         if(this.hideMode == 'visibility'){
7672             ae.dom.style.visibility = "visible";
7673         }else if(this.hideMode == 'offsets'){
7674             ae.removeClass('x-hidden');
7675         }else{
7676             ae.dom.style.display = "";
7677         }
7678     },
7679
7680     /**
7681      * Hide this component.
7682      * @return {Roo.Component} this
7683      */
7684     hide: function(){
7685         if(this.fireEvent("beforehide", this) !== false){
7686             this.hidden = true;
7687             if(this.rendered){
7688                 this.onHide();
7689             }
7690             this.fireEvent("hide", this);
7691         }
7692         return this;
7693     },
7694
7695     // private
7696     onHide : function(){
7697         var ae = this.getActionEl();
7698         if(this.hideMode == 'visibility'){
7699             ae.dom.style.visibility = "hidden";
7700         }else if(this.hideMode == 'offsets'){
7701             ae.addClass('x-hidden');
7702         }else{
7703             ae.dom.style.display = "none";
7704         }
7705     },
7706
7707     /**
7708      * Convenience function to hide or show this component by boolean.
7709      * @param {Boolean} visible True to show, false to hide
7710      * @return {Roo.Component} this
7711      */
7712     setVisible: function(visible){
7713         if(visible) {
7714             this.show();
7715         }else{
7716             this.hide();
7717         }
7718         return this;
7719     },
7720
7721     /**
7722      * Returns true if this component is visible.
7723      */
7724     isVisible : function(){
7725         return this.getActionEl().isVisible();
7726     },
7727
7728     cloneConfig : function(overrides){
7729         overrides = overrides || {};
7730         var id = overrides.id || Roo.id();
7731         var cfg = Roo.applyIf(overrides, this.initialConfig);
7732         cfg.id = id; // prevent dup id
7733         return new this.constructor(cfg);
7734     }
7735 });/*
7736  * Based on:
7737  * Ext JS Library 1.1.1
7738  * Copyright(c) 2006-2007, Ext JS, LLC.
7739  *
7740  * Originally Released Under LGPL - original licence link has changed is not relivant.
7741  *
7742  * Fork - LGPL
7743  * <script type="text/javascript">
7744  */
7745  (function(){ 
7746 /**
7747  * @class Roo.Layer
7748  * @extends Roo.Element
7749  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7750  * automatic maintaining of shadow/shim positions.
7751  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7752  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7753  * you can pass a string with a CSS class name. False turns off the shadow.
7754  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7755  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7756  * @cfg {String} cls CSS class to add to the element
7757  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7758  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7759  * @constructor
7760  * @param {Object} config An object with config options.
7761  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7762  */
7763
7764 Roo.Layer = function(config, existingEl){
7765     config = config || {};
7766     var dh = Roo.DomHelper;
7767     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7768     if(existingEl){
7769         this.dom = Roo.getDom(existingEl);
7770     }
7771     if(!this.dom){
7772         var o = config.dh || {tag: "div", cls: "x-layer"};
7773         this.dom = dh.append(pel, o);
7774     }
7775     if(config.cls){
7776         this.addClass(config.cls);
7777     }
7778     this.constrain = config.constrain !== false;
7779     this.visibilityMode = Roo.Element.VISIBILITY;
7780     if(config.id){
7781         this.id = this.dom.id = config.id;
7782     }else{
7783         this.id = Roo.id(this.dom);
7784     }
7785     this.zindex = config.zindex || this.getZIndex();
7786     this.position("absolute", this.zindex);
7787     if(config.shadow){
7788         this.shadowOffset = config.shadowOffset || 4;
7789         this.shadow = new Roo.Shadow({
7790             offset : this.shadowOffset,
7791             mode : config.shadow
7792         });
7793     }else{
7794         this.shadowOffset = 0;
7795     }
7796     this.useShim = config.shim !== false && Roo.useShims;
7797     this.useDisplay = config.useDisplay;
7798     this.hide();
7799 };
7800
7801 var supr = Roo.Element.prototype;
7802
7803 // shims are shared among layer to keep from having 100 iframes
7804 var shims = [];
7805
7806 Roo.extend(Roo.Layer, Roo.Element, {
7807
7808     getZIndex : function(){
7809         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7810     },
7811
7812     getShim : function(){
7813         if(!this.useShim){
7814             return null;
7815         }
7816         if(this.shim){
7817             return this.shim;
7818         }
7819         var shim = shims.shift();
7820         if(!shim){
7821             shim = this.createShim();
7822             shim.enableDisplayMode('block');
7823             shim.dom.style.display = 'none';
7824             shim.dom.style.visibility = 'visible';
7825         }
7826         var pn = this.dom.parentNode;
7827         if(shim.dom.parentNode != pn){
7828             pn.insertBefore(shim.dom, this.dom);
7829         }
7830         shim.setStyle('z-index', this.getZIndex()-2);
7831         this.shim = shim;
7832         return shim;
7833     },
7834
7835     hideShim : function(){
7836         if(this.shim){
7837             this.shim.setDisplayed(false);
7838             shims.push(this.shim);
7839             delete this.shim;
7840         }
7841     },
7842
7843     disableShadow : function(){
7844         if(this.shadow){
7845             this.shadowDisabled = true;
7846             this.shadow.hide();
7847             this.lastShadowOffset = this.shadowOffset;
7848             this.shadowOffset = 0;
7849         }
7850     },
7851
7852     enableShadow : function(show){
7853         if(this.shadow){
7854             this.shadowDisabled = false;
7855             this.shadowOffset = this.lastShadowOffset;
7856             delete this.lastShadowOffset;
7857             if(show){
7858                 this.sync(true);
7859             }
7860         }
7861     },
7862
7863     // private
7864     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7865     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7866     sync : function(doShow){
7867         var sw = this.shadow;
7868         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7869             var sh = this.getShim();
7870
7871             var w = this.getWidth(),
7872                 h = this.getHeight();
7873
7874             var l = this.getLeft(true),
7875                 t = this.getTop(true);
7876
7877             if(sw && !this.shadowDisabled){
7878                 if(doShow && !sw.isVisible()){
7879                     sw.show(this);
7880                 }else{
7881                     sw.realign(l, t, w, h);
7882                 }
7883                 if(sh){
7884                     if(doShow){
7885                        sh.show();
7886                     }
7887                     // fit the shim behind the shadow, so it is shimmed too
7888                     var a = sw.adjusts, s = sh.dom.style;
7889                     s.left = (Math.min(l, l+a.l))+"px";
7890                     s.top = (Math.min(t, t+a.t))+"px";
7891                     s.width = (w+a.w)+"px";
7892                     s.height = (h+a.h)+"px";
7893                 }
7894             }else if(sh){
7895                 if(doShow){
7896                    sh.show();
7897                 }
7898                 sh.setSize(w, h);
7899                 sh.setLeftTop(l, t);
7900             }
7901             
7902         }
7903     },
7904
7905     // private
7906     destroy : function(){
7907         this.hideShim();
7908         if(this.shadow){
7909             this.shadow.hide();
7910         }
7911         this.removeAllListeners();
7912         var pn = this.dom.parentNode;
7913         if(pn){
7914             pn.removeChild(this.dom);
7915         }
7916         Roo.Element.uncache(this.id);
7917     },
7918
7919     remove : function(){
7920         this.destroy();
7921     },
7922
7923     // private
7924     beginUpdate : function(){
7925         this.updating = true;
7926     },
7927
7928     // private
7929     endUpdate : function(){
7930         this.updating = false;
7931         this.sync(true);
7932     },
7933
7934     // private
7935     hideUnders : function(negOffset){
7936         if(this.shadow){
7937             this.shadow.hide();
7938         }
7939         this.hideShim();
7940     },
7941
7942     // private
7943     constrainXY : function(){
7944         if(this.constrain){
7945             var vw = Roo.lib.Dom.getViewWidth(),
7946                 vh = Roo.lib.Dom.getViewHeight();
7947             var s = Roo.get(document).getScroll();
7948
7949             var xy = this.getXY();
7950             var x = xy[0], y = xy[1];   
7951             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7952             // only move it if it needs it
7953             var moved = false;
7954             // first validate right/bottom
7955             if((x + w) > vw+s.left){
7956                 x = vw - w - this.shadowOffset;
7957                 moved = true;
7958             }
7959             if((y + h) > vh+s.top){
7960                 y = vh - h - this.shadowOffset;
7961                 moved = true;
7962             }
7963             // then make sure top/left isn't negative
7964             if(x < s.left){
7965                 x = s.left;
7966                 moved = true;
7967             }
7968             if(y < s.top){
7969                 y = s.top;
7970                 moved = true;
7971             }
7972             if(moved){
7973                 if(this.avoidY){
7974                     var ay = this.avoidY;
7975                     if(y <= ay && (y+h) >= ay){
7976                         y = ay-h-5;   
7977                     }
7978                 }
7979                 xy = [x, y];
7980                 this.storeXY(xy);
7981                 supr.setXY.call(this, xy);
7982                 this.sync();
7983             }
7984         }
7985     },
7986
7987     isVisible : function(){
7988         return this.visible;    
7989     },
7990
7991     // private
7992     showAction : function(){
7993         this.visible = true; // track visibility to prevent getStyle calls
7994         if(this.useDisplay === true){
7995             this.setDisplayed("");
7996         }else if(this.lastXY){
7997             supr.setXY.call(this, this.lastXY);
7998         }else if(this.lastLT){
7999             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8000         }
8001     },
8002
8003     // private
8004     hideAction : function(){
8005         this.visible = false;
8006         if(this.useDisplay === true){
8007             this.setDisplayed(false);
8008         }else{
8009             this.setLeftTop(-10000,-10000);
8010         }
8011     },
8012
8013     // overridden Element method
8014     setVisible : function(v, a, d, c, e){
8015         if(v){
8016             this.showAction();
8017         }
8018         if(a && v){
8019             var cb = function(){
8020                 this.sync(true);
8021                 if(c){
8022                     c();
8023                 }
8024             }.createDelegate(this);
8025             supr.setVisible.call(this, true, true, d, cb, e);
8026         }else{
8027             if(!v){
8028                 this.hideUnders(true);
8029             }
8030             var cb = c;
8031             if(a){
8032                 cb = function(){
8033                     this.hideAction();
8034                     if(c){
8035                         c();
8036                     }
8037                 }.createDelegate(this);
8038             }
8039             supr.setVisible.call(this, v, a, d, cb, e);
8040             if(v){
8041                 this.sync(true);
8042             }else if(!a){
8043                 this.hideAction();
8044             }
8045         }
8046     },
8047
8048     storeXY : function(xy){
8049         delete this.lastLT;
8050         this.lastXY = xy;
8051     },
8052
8053     storeLeftTop : function(left, top){
8054         delete this.lastXY;
8055         this.lastLT = [left, top];
8056     },
8057
8058     // private
8059     beforeFx : function(){
8060         this.beforeAction();
8061         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8062     },
8063
8064     // private
8065     afterFx : function(){
8066         Roo.Layer.superclass.afterFx.apply(this, arguments);
8067         this.sync(this.isVisible());
8068     },
8069
8070     // private
8071     beforeAction : function(){
8072         if(!this.updating && this.shadow){
8073             this.shadow.hide();
8074         }
8075     },
8076
8077     // overridden Element method
8078     setLeft : function(left){
8079         this.storeLeftTop(left, this.getTop(true));
8080         supr.setLeft.apply(this, arguments);
8081         this.sync();
8082     },
8083
8084     setTop : function(top){
8085         this.storeLeftTop(this.getLeft(true), top);
8086         supr.setTop.apply(this, arguments);
8087         this.sync();
8088     },
8089
8090     setLeftTop : function(left, top){
8091         this.storeLeftTop(left, top);
8092         supr.setLeftTop.apply(this, arguments);
8093         this.sync();
8094     },
8095
8096     setXY : function(xy, a, d, c, e){
8097         this.fixDisplay();
8098         this.beforeAction();
8099         this.storeXY(xy);
8100         var cb = this.createCB(c);
8101         supr.setXY.call(this, xy, a, d, cb, e);
8102         if(!a){
8103             cb();
8104         }
8105     },
8106
8107     // private
8108     createCB : function(c){
8109         var el = this;
8110         return function(){
8111             el.constrainXY();
8112             el.sync(true);
8113             if(c){
8114                 c();
8115             }
8116         };
8117     },
8118
8119     // overridden Element method
8120     setX : function(x, a, d, c, e){
8121         this.setXY([x, this.getY()], a, d, c, e);
8122     },
8123
8124     // overridden Element method
8125     setY : function(y, a, d, c, e){
8126         this.setXY([this.getX(), y], a, d, c, e);
8127     },
8128
8129     // overridden Element method
8130     setSize : function(w, h, a, d, c, e){
8131         this.beforeAction();
8132         var cb = this.createCB(c);
8133         supr.setSize.call(this, w, h, a, d, cb, e);
8134         if(!a){
8135             cb();
8136         }
8137     },
8138
8139     // overridden Element method
8140     setWidth : function(w, a, d, c, e){
8141         this.beforeAction();
8142         var cb = this.createCB(c);
8143         supr.setWidth.call(this, w, a, d, cb, e);
8144         if(!a){
8145             cb();
8146         }
8147     },
8148
8149     // overridden Element method
8150     setHeight : function(h, a, d, c, e){
8151         this.beforeAction();
8152         var cb = this.createCB(c);
8153         supr.setHeight.call(this, h, a, d, cb, e);
8154         if(!a){
8155             cb();
8156         }
8157     },
8158
8159     // overridden Element method
8160     setBounds : function(x, y, w, h, a, d, c, e){
8161         this.beforeAction();
8162         var cb = this.createCB(c);
8163         if(!a){
8164             this.storeXY([x, y]);
8165             supr.setXY.call(this, [x, y]);
8166             supr.setSize.call(this, w, h, a, d, cb, e);
8167             cb();
8168         }else{
8169             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8170         }
8171         return this;
8172     },
8173     
8174     /**
8175      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8176      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8177      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8178      * @param {Number} zindex The new z-index to set
8179      * @return {this} The Layer
8180      */
8181     setZIndex : function(zindex){
8182         this.zindex = zindex;
8183         this.setStyle("z-index", zindex + 2);
8184         if(this.shadow){
8185             this.shadow.setZIndex(zindex + 1);
8186         }
8187         if(this.shim){
8188             this.shim.setStyle("z-index", zindex);
8189         }
8190     }
8191 });
8192 })();/*
8193  * Based on:
8194  * Ext JS Library 1.1.1
8195  * Copyright(c) 2006-2007, Ext JS, LLC.
8196  *
8197  * Originally Released Under LGPL - original licence link has changed is not relivant.
8198  *
8199  * Fork - LGPL
8200  * <script type="text/javascript">
8201  */
8202
8203
8204 /**
8205  * @class Roo.Shadow
8206  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8207  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8208  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8209  * @constructor
8210  * Create a new Shadow
8211  * @param {Object} config The config object
8212  */
8213 Roo.Shadow = function(config){
8214     Roo.apply(this, config);
8215     if(typeof this.mode != "string"){
8216         this.mode = this.defaultMode;
8217     }
8218     var o = this.offset, a = {h: 0};
8219     var rad = Math.floor(this.offset/2);
8220     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8221         case "drop":
8222             a.w = 0;
8223             a.l = a.t = o;
8224             a.t -= 1;
8225             if(Roo.isIE){
8226                 a.l -= this.offset + rad;
8227                 a.t -= this.offset + rad;
8228                 a.w -= rad;
8229                 a.h -= rad;
8230                 a.t += 1;
8231             }
8232         break;
8233         case "sides":
8234             a.w = (o*2);
8235             a.l = -o;
8236             a.t = o-1;
8237             if(Roo.isIE){
8238                 a.l -= (this.offset - rad);
8239                 a.t -= this.offset + rad;
8240                 a.l += 1;
8241                 a.w -= (this.offset - rad)*2;
8242                 a.w -= rad + 1;
8243                 a.h -= 1;
8244             }
8245         break;
8246         case "frame":
8247             a.w = a.h = (o*2);
8248             a.l = a.t = -o;
8249             a.t += 1;
8250             a.h -= 2;
8251             if(Roo.isIE){
8252                 a.l -= (this.offset - rad);
8253                 a.t -= (this.offset - rad);
8254                 a.l += 1;
8255                 a.w -= (this.offset + rad + 1);
8256                 a.h -= (this.offset + rad);
8257                 a.h += 1;
8258             }
8259         break;
8260     };
8261
8262     this.adjusts = a;
8263 };
8264
8265 Roo.Shadow.prototype = {
8266     /**
8267      * @cfg {String} mode
8268      * The shadow display mode.  Supports the following options:<br />
8269      * sides: Shadow displays on both sides and bottom only<br />
8270      * frame: Shadow displays equally on all four sides<br />
8271      * drop: Traditional bottom-right drop shadow (default)
8272      */
8273     /**
8274      * @cfg {String} offset
8275      * The number of pixels to offset the shadow from the element (defaults to 4)
8276      */
8277     offset: 4,
8278
8279     // private
8280     defaultMode: "drop",
8281
8282     /**
8283      * Displays the shadow under the target element
8284      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8285      */
8286     show : function(target){
8287         target = Roo.get(target);
8288         if(!this.el){
8289             this.el = Roo.Shadow.Pool.pull();
8290             if(this.el.dom.nextSibling != target.dom){
8291                 this.el.insertBefore(target);
8292             }
8293         }
8294         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8295         if(Roo.isIE){
8296             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8297         }
8298         this.realign(
8299             target.getLeft(true),
8300             target.getTop(true),
8301             target.getWidth(),
8302             target.getHeight()
8303         );
8304         this.el.dom.style.display = "block";
8305     },
8306
8307     /**
8308      * Returns true if the shadow is visible, else false
8309      */
8310     isVisible : function(){
8311         return this.el ? true : false;  
8312     },
8313
8314     /**
8315      * Direct alignment when values are already available. Show must be called at least once before
8316      * calling this method to ensure it is initialized.
8317      * @param {Number} left The target element left position
8318      * @param {Number} top The target element top position
8319      * @param {Number} width The target element width
8320      * @param {Number} height The target element height
8321      */
8322     realign : function(l, t, w, h){
8323         if(!this.el){
8324             return;
8325         }
8326         var a = this.adjusts, d = this.el.dom, s = d.style;
8327         var iea = 0;
8328         s.left = (l+a.l)+"px";
8329         s.top = (t+a.t)+"px";
8330         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8331  
8332         if(s.width != sws || s.height != shs){
8333             s.width = sws;
8334             s.height = shs;
8335             if(!Roo.isIE){
8336                 var cn = d.childNodes;
8337                 var sww = Math.max(0, (sw-12))+"px";
8338                 cn[0].childNodes[1].style.width = sww;
8339                 cn[1].childNodes[1].style.width = sww;
8340                 cn[2].childNodes[1].style.width = sww;
8341                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8342             }
8343         }
8344     },
8345
8346     /**
8347      * Hides this shadow
8348      */
8349     hide : function(){
8350         if(this.el){
8351             this.el.dom.style.display = "none";
8352             Roo.Shadow.Pool.push(this.el);
8353             delete this.el;
8354         }
8355     },
8356
8357     /**
8358      * Adjust the z-index of this shadow
8359      * @param {Number} zindex The new z-index
8360      */
8361     setZIndex : function(z){
8362         this.zIndex = z;
8363         if(this.el){
8364             this.el.setStyle("z-index", z);
8365         }
8366     }
8367 };
8368
8369 // Private utility class that manages the internal Shadow cache
8370 Roo.Shadow.Pool = function(){
8371     var p = [];
8372     var markup = Roo.isIE ?
8373                  '<div class="x-ie-shadow"></div>' :
8374                  '<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>';
8375     return {
8376         pull : function(){
8377             var sh = p.shift();
8378             if(!sh){
8379                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8380                 sh.autoBoxAdjust = false;
8381             }
8382             return sh;
8383         },
8384
8385         push : function(sh){
8386             p.push(sh);
8387         }
8388     };
8389 }();/*
8390  * Based on:
8391  * Ext JS Library 1.1.1
8392  * Copyright(c) 2006-2007, Ext JS, LLC.
8393  *
8394  * Originally Released Under LGPL - original licence link has changed is not relivant.
8395  *
8396  * Fork - LGPL
8397  * <script type="text/javascript">
8398  */
8399
8400 /**
8401  * @class Roo.BoxComponent
8402  * @extends Roo.Component
8403  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8404  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8405  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8406  * layout containers.
8407  * @constructor
8408  * @param {Roo.Element/String/Object} config The configuration options.
8409  */
8410 Roo.BoxComponent = function(config){
8411     Roo.Component.call(this, config);
8412     this.addEvents({
8413         /**
8414          * @event resize
8415          * Fires after the component is resized.
8416              * @param {Roo.Component} this
8417              * @param {Number} adjWidth The box-adjusted width that was set
8418              * @param {Number} adjHeight The box-adjusted height that was set
8419              * @param {Number} rawWidth The width that was originally specified
8420              * @param {Number} rawHeight The height that was originally specified
8421              */
8422         resize : true,
8423         /**
8424          * @event move
8425          * Fires after the component is moved.
8426              * @param {Roo.Component} this
8427              * @param {Number} x The new x position
8428              * @param {Number} y The new y position
8429              */
8430         move : true
8431     });
8432 };
8433
8434 Roo.extend(Roo.BoxComponent, Roo.Component, {
8435     // private, set in afterRender to signify that the component has been rendered
8436     boxReady : false,
8437     // private, used to defer height settings to subclasses
8438     deferHeight: false,
8439     /** @cfg {Number} width
8440      * width (optional) size of component
8441      */
8442      /** @cfg {Number} height
8443      * height (optional) size of component
8444      */
8445      
8446     /**
8447      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8448      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8449      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8450      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8451      * @return {Roo.BoxComponent} this
8452      */
8453     setSize : function(w, h){
8454         // support for standard size objects
8455         if(typeof w == 'object'){
8456             h = w.height;
8457             w = w.width;
8458         }
8459         // not rendered
8460         if(!this.boxReady){
8461             this.width = w;
8462             this.height = h;
8463             return this;
8464         }
8465
8466         // prevent recalcs when not needed
8467         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8468             return this;
8469         }
8470         this.lastSize = {width: w, height: h};
8471
8472         var adj = this.adjustSize(w, h);
8473         var aw = adj.width, ah = adj.height;
8474         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8475             var rz = this.getResizeEl();
8476             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8477                 rz.setSize(aw, ah);
8478             }else if(!this.deferHeight && ah !== undefined){
8479                 rz.setHeight(ah);
8480             }else if(aw !== undefined){
8481                 rz.setWidth(aw);
8482             }
8483             this.onResize(aw, ah, w, h);
8484             this.fireEvent('resize', this, aw, ah, w, h);
8485         }
8486         return this;
8487     },
8488
8489     /**
8490      * Gets the current size of the component's underlying element.
8491      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8492      */
8493     getSize : function(){
8494         return this.el.getSize();
8495     },
8496
8497     /**
8498      * Gets the current XY position of the component's underlying element.
8499      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8500      * @return {Array} The XY position of the element (e.g., [100, 200])
8501      */
8502     getPosition : function(local){
8503         if(local === true){
8504             return [this.el.getLeft(true), this.el.getTop(true)];
8505         }
8506         return this.xy || this.el.getXY();
8507     },
8508
8509     /**
8510      * Gets the current box measurements of the component's underlying element.
8511      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8512      * @returns {Object} box An object in the format {x, y, width, height}
8513      */
8514     getBox : function(local){
8515         var s = this.el.getSize();
8516         if(local){
8517             s.x = this.el.getLeft(true);
8518             s.y = this.el.getTop(true);
8519         }else{
8520             var xy = this.xy || this.el.getXY();
8521             s.x = xy[0];
8522             s.y = xy[1];
8523         }
8524         return s;
8525     },
8526
8527     /**
8528      * Sets the current box measurements of the component's underlying element.
8529      * @param {Object} box An object in the format {x, y, width, height}
8530      * @returns {Roo.BoxComponent} this
8531      */
8532     updateBox : function(box){
8533         this.setSize(box.width, box.height);
8534         this.setPagePosition(box.x, box.y);
8535         return this;
8536     },
8537
8538     // protected
8539     getResizeEl : function(){
8540         return this.resizeEl || this.el;
8541     },
8542
8543     // protected
8544     getPositionEl : function(){
8545         return this.positionEl || this.el;
8546     },
8547
8548     /**
8549      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8550      * This method fires the move event.
8551      * @param {Number} left The new left
8552      * @param {Number} top The new top
8553      * @returns {Roo.BoxComponent} this
8554      */
8555     setPosition : function(x, y){
8556         this.x = x;
8557         this.y = y;
8558         if(!this.boxReady){
8559             return this;
8560         }
8561         var adj = this.adjustPosition(x, y);
8562         var ax = adj.x, ay = adj.y;
8563
8564         var el = this.getPositionEl();
8565         if(ax !== undefined || ay !== undefined){
8566             if(ax !== undefined && ay !== undefined){
8567                 el.setLeftTop(ax, ay);
8568             }else if(ax !== undefined){
8569                 el.setLeft(ax);
8570             }else if(ay !== undefined){
8571                 el.setTop(ay);
8572             }
8573             this.onPosition(ax, ay);
8574             this.fireEvent('move', this, ax, ay);
8575         }
8576         return this;
8577     },
8578
8579     /**
8580      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8581      * This method fires the move event.
8582      * @param {Number} x The new x position
8583      * @param {Number} y The new y position
8584      * @returns {Roo.BoxComponent} this
8585      */
8586     setPagePosition : function(x, y){
8587         this.pageX = x;
8588         this.pageY = y;
8589         if(!this.boxReady){
8590             return;
8591         }
8592         if(x === undefined || y === undefined){ // cannot translate undefined points
8593             return;
8594         }
8595         var p = this.el.translatePoints(x, y);
8596         this.setPosition(p.left, p.top);
8597         return this;
8598     },
8599
8600     // private
8601     onRender : function(ct, position){
8602         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8603         if(this.resizeEl){
8604             this.resizeEl = Roo.get(this.resizeEl);
8605         }
8606         if(this.positionEl){
8607             this.positionEl = Roo.get(this.positionEl);
8608         }
8609     },
8610
8611     // private
8612     afterRender : function(){
8613         Roo.BoxComponent.superclass.afterRender.call(this);
8614         this.boxReady = true;
8615         this.setSize(this.width, this.height);
8616         if(this.x || this.y){
8617             this.setPosition(this.x, this.y);
8618         }
8619         if(this.pageX || this.pageY){
8620             this.setPagePosition(this.pageX, this.pageY);
8621         }
8622     },
8623
8624     /**
8625      * Force the component's size to recalculate based on the underlying element's current height and width.
8626      * @returns {Roo.BoxComponent} this
8627      */
8628     syncSize : function(){
8629         delete this.lastSize;
8630         this.setSize(this.el.getWidth(), this.el.getHeight());
8631         return this;
8632     },
8633
8634     /**
8635      * Called after the component is resized, this method is empty by default but can be implemented by any
8636      * subclass that needs to perform custom logic after a resize occurs.
8637      * @param {Number} adjWidth The box-adjusted width that was set
8638      * @param {Number} adjHeight The box-adjusted height that was set
8639      * @param {Number} rawWidth The width that was originally specified
8640      * @param {Number} rawHeight The height that was originally specified
8641      */
8642     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8643
8644     },
8645
8646     /**
8647      * Called after the component is moved, this method is empty by default but can be implemented by any
8648      * subclass that needs to perform custom logic after a move occurs.
8649      * @param {Number} x The new x position
8650      * @param {Number} y The new y position
8651      */
8652     onPosition : function(x, y){
8653
8654     },
8655
8656     // private
8657     adjustSize : function(w, h){
8658         if(this.autoWidth){
8659             w = 'auto';
8660         }
8661         if(this.autoHeight){
8662             h = 'auto';
8663         }
8664         return {width : w, height: h};
8665     },
8666
8667     // private
8668     adjustPosition : function(x, y){
8669         return {x : x, y: y};
8670     }
8671 });/*
8672  * Based on:
8673  * Ext JS Library 1.1.1
8674  * Copyright(c) 2006-2007, Ext JS, LLC.
8675  *
8676  * Originally Released Under LGPL - original licence link has changed is not relivant.
8677  *
8678  * Fork - LGPL
8679  * <script type="text/javascript">
8680  */
8681
8682
8683 /**
8684  * @class Roo.SplitBar
8685  * @extends Roo.util.Observable
8686  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8687  * <br><br>
8688  * Usage:
8689  * <pre><code>
8690 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8691                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8692 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8693 split.minSize = 100;
8694 split.maxSize = 600;
8695 split.animate = true;
8696 split.on('moved', splitterMoved);
8697 </code></pre>
8698  * @constructor
8699  * Create a new SplitBar
8700  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8701  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8702  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8703  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8704                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8705                         position of the SplitBar).
8706  */
8707 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8708     
8709     /** @private */
8710     this.el = Roo.get(dragElement, true);
8711     this.el.dom.unselectable = "on";
8712     /** @private */
8713     this.resizingEl = Roo.get(resizingElement, true);
8714
8715     /**
8716      * @private
8717      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8718      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8719      * @type Number
8720      */
8721     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8722     
8723     /**
8724      * The minimum size of the resizing element. (Defaults to 0)
8725      * @type Number
8726      */
8727     this.minSize = 0;
8728     
8729     /**
8730      * The maximum size of the resizing element. (Defaults to 2000)
8731      * @type Number
8732      */
8733     this.maxSize = 2000;
8734     
8735     /**
8736      * Whether to animate the transition to the new size
8737      * @type Boolean
8738      */
8739     this.animate = false;
8740     
8741     /**
8742      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8743      * @type Boolean
8744      */
8745     this.useShim = false;
8746     
8747     /** @private */
8748     this.shim = null;
8749     
8750     if(!existingProxy){
8751         /** @private */
8752         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8753     }else{
8754         this.proxy = Roo.get(existingProxy).dom;
8755     }
8756     /** @private */
8757     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8758     
8759     /** @private */
8760     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8761     
8762     /** @private */
8763     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8764     
8765     /** @private */
8766     this.dragSpecs = {};
8767     
8768     /**
8769      * @private The adapter to use to positon and resize elements
8770      */
8771     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8772     this.adapter.init(this);
8773     
8774     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8775         /** @private */
8776         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8777         this.el.addClass("x-splitbar-h");
8778     }else{
8779         /** @private */
8780         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8781         this.el.addClass("x-splitbar-v");
8782     }
8783     
8784     this.addEvents({
8785         /**
8786          * @event resize
8787          * Fires when the splitter is moved (alias for {@link #event-moved})
8788          * @param {Roo.SplitBar} this
8789          * @param {Number} newSize the new width or height
8790          */
8791         "resize" : true,
8792         /**
8793          * @event moved
8794          * Fires when the splitter is moved
8795          * @param {Roo.SplitBar} this
8796          * @param {Number} newSize the new width or height
8797          */
8798         "moved" : true,
8799         /**
8800          * @event beforeresize
8801          * Fires before the splitter is dragged
8802          * @param {Roo.SplitBar} this
8803          */
8804         "beforeresize" : true,
8805
8806         "beforeapply" : true
8807     });
8808
8809     Roo.util.Observable.call(this);
8810 };
8811
8812 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8813     onStartProxyDrag : function(x, y){
8814         this.fireEvent("beforeresize", this);
8815         if(!this.overlay){
8816             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8817             o.unselectable();
8818             o.enableDisplayMode("block");
8819             // all splitbars share the same overlay
8820             Roo.SplitBar.prototype.overlay = o;
8821         }
8822         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8823         this.overlay.show();
8824         Roo.get(this.proxy).setDisplayed("block");
8825         var size = this.adapter.getElementSize(this);
8826         this.activeMinSize = this.getMinimumSize();;
8827         this.activeMaxSize = this.getMaximumSize();;
8828         var c1 = size - this.activeMinSize;
8829         var c2 = Math.max(this.activeMaxSize - size, 0);
8830         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8831             this.dd.resetConstraints();
8832             this.dd.setXConstraint(
8833                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8834                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8835             );
8836             this.dd.setYConstraint(0, 0);
8837         }else{
8838             this.dd.resetConstraints();
8839             this.dd.setXConstraint(0, 0);
8840             this.dd.setYConstraint(
8841                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8842                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8843             );
8844          }
8845         this.dragSpecs.startSize = size;
8846         this.dragSpecs.startPoint = [x, y];
8847         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8848     },
8849     
8850     /** 
8851      * @private Called after the drag operation by the DDProxy
8852      */
8853     onEndProxyDrag : function(e){
8854         Roo.get(this.proxy).setDisplayed(false);
8855         var endPoint = Roo.lib.Event.getXY(e);
8856         if(this.overlay){
8857             this.overlay.hide();
8858         }
8859         var newSize;
8860         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8861             newSize = this.dragSpecs.startSize + 
8862                 (this.placement == Roo.SplitBar.LEFT ?
8863                     endPoint[0] - this.dragSpecs.startPoint[0] :
8864                     this.dragSpecs.startPoint[0] - endPoint[0]
8865                 );
8866         }else{
8867             newSize = this.dragSpecs.startSize + 
8868                 (this.placement == Roo.SplitBar.TOP ?
8869                     endPoint[1] - this.dragSpecs.startPoint[1] :
8870                     this.dragSpecs.startPoint[1] - endPoint[1]
8871                 );
8872         }
8873         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8874         if(newSize != this.dragSpecs.startSize){
8875             if(this.fireEvent('beforeapply', this, newSize) !== false){
8876                 this.adapter.setElementSize(this, newSize);
8877                 this.fireEvent("moved", this, newSize);
8878                 this.fireEvent("resize", this, newSize);
8879             }
8880         }
8881     },
8882     
8883     /**
8884      * Get the adapter this SplitBar uses
8885      * @return The adapter object
8886      */
8887     getAdapter : function(){
8888         return this.adapter;
8889     },
8890     
8891     /**
8892      * Set the adapter this SplitBar uses
8893      * @param {Object} adapter A SplitBar adapter object
8894      */
8895     setAdapter : function(adapter){
8896         this.adapter = adapter;
8897         this.adapter.init(this);
8898     },
8899     
8900     /**
8901      * Gets the minimum size for the resizing element
8902      * @return {Number} The minimum size
8903      */
8904     getMinimumSize : function(){
8905         return this.minSize;
8906     },
8907     
8908     /**
8909      * Sets the minimum size for the resizing element
8910      * @param {Number} minSize The minimum size
8911      */
8912     setMinimumSize : function(minSize){
8913         this.minSize = minSize;
8914     },
8915     
8916     /**
8917      * Gets the maximum size for the resizing element
8918      * @return {Number} The maximum size
8919      */
8920     getMaximumSize : function(){
8921         return this.maxSize;
8922     },
8923     
8924     /**
8925      * Sets the maximum size for the resizing element
8926      * @param {Number} maxSize The maximum size
8927      */
8928     setMaximumSize : function(maxSize){
8929         this.maxSize = maxSize;
8930     },
8931     
8932     /**
8933      * Sets the initialize size for the resizing element
8934      * @param {Number} size The initial size
8935      */
8936     setCurrentSize : function(size){
8937         var oldAnimate = this.animate;
8938         this.animate = false;
8939         this.adapter.setElementSize(this, size);
8940         this.animate = oldAnimate;
8941     },
8942     
8943     /**
8944      * Destroy this splitbar. 
8945      * @param {Boolean} removeEl True to remove the element
8946      */
8947     destroy : function(removeEl){
8948         if(this.shim){
8949             this.shim.remove();
8950         }
8951         this.dd.unreg();
8952         this.proxy.parentNode.removeChild(this.proxy);
8953         if(removeEl){
8954             this.el.remove();
8955         }
8956     }
8957 });
8958
8959 /**
8960  * @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.
8961  */
8962 Roo.SplitBar.createProxy = function(dir){
8963     var proxy = new Roo.Element(document.createElement("div"));
8964     proxy.unselectable();
8965     var cls = 'x-splitbar-proxy';
8966     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8967     document.body.appendChild(proxy.dom);
8968     return proxy.dom;
8969 };
8970
8971 /** 
8972  * @class Roo.SplitBar.BasicLayoutAdapter
8973  * Default Adapter. It assumes the splitter and resizing element are not positioned
8974  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8975  */
8976 Roo.SplitBar.BasicLayoutAdapter = function(){
8977 };
8978
8979 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8980     // do nothing for now
8981     init : function(s){
8982     
8983     },
8984     /**
8985      * Called before drag operations to get the current size of the resizing element. 
8986      * @param {Roo.SplitBar} s The SplitBar using this adapter
8987      */
8988      getElementSize : function(s){
8989         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8990             return s.resizingEl.getWidth();
8991         }else{
8992             return s.resizingEl.getHeight();
8993         }
8994     },
8995     
8996     /**
8997      * Called after drag operations to set the size of the resizing element.
8998      * @param {Roo.SplitBar} s The SplitBar using this adapter
8999      * @param {Number} newSize The new size to set
9000      * @param {Function} onComplete A function to be invoked when resizing is complete
9001      */
9002     setElementSize : function(s, newSize, onComplete){
9003         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9004             if(!s.animate){
9005                 s.resizingEl.setWidth(newSize);
9006                 if(onComplete){
9007                     onComplete(s, newSize);
9008                 }
9009             }else{
9010                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9011             }
9012         }else{
9013             
9014             if(!s.animate){
9015                 s.resizingEl.setHeight(newSize);
9016                 if(onComplete){
9017                     onComplete(s, newSize);
9018                 }
9019             }else{
9020                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9021             }
9022         }
9023     }
9024 };
9025
9026 /** 
9027  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9028  * @extends Roo.SplitBar.BasicLayoutAdapter
9029  * Adapter that  moves the splitter element to align with the resized sizing element. 
9030  * Used with an absolute positioned SplitBar.
9031  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9032  * document.body, make sure you assign an id to the body element.
9033  */
9034 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9035     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9036     this.container = Roo.get(container);
9037 };
9038
9039 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9040     init : function(s){
9041         this.basic.init(s);
9042     },
9043     
9044     getElementSize : function(s){
9045         return this.basic.getElementSize(s);
9046     },
9047     
9048     setElementSize : function(s, newSize, onComplete){
9049         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9050     },
9051     
9052     moveSplitter : function(s){
9053         var yes = Roo.SplitBar;
9054         switch(s.placement){
9055             case yes.LEFT:
9056                 s.el.setX(s.resizingEl.getRight());
9057                 break;
9058             case yes.RIGHT:
9059                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9060                 break;
9061             case yes.TOP:
9062                 s.el.setY(s.resizingEl.getBottom());
9063                 break;
9064             case yes.BOTTOM:
9065                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9066                 break;
9067         }
9068     }
9069 };
9070
9071 /**
9072  * Orientation constant - Create a vertical SplitBar
9073  * @static
9074  * @type Number
9075  */
9076 Roo.SplitBar.VERTICAL = 1;
9077
9078 /**
9079  * Orientation constant - Create a horizontal SplitBar
9080  * @static
9081  * @type Number
9082  */
9083 Roo.SplitBar.HORIZONTAL = 2;
9084
9085 /**
9086  * Placement constant - The resizing element is to the left of the splitter element
9087  * @static
9088  * @type Number
9089  */
9090 Roo.SplitBar.LEFT = 1;
9091
9092 /**
9093  * Placement constant - The resizing element is to the right of the splitter element
9094  * @static
9095  * @type Number
9096  */
9097 Roo.SplitBar.RIGHT = 2;
9098
9099 /**
9100  * Placement constant - The resizing element is positioned above the splitter element
9101  * @static
9102  * @type Number
9103  */
9104 Roo.SplitBar.TOP = 3;
9105
9106 /**
9107  * Placement constant - The resizing element is positioned under splitter element
9108  * @static
9109  * @type Number
9110  */
9111 Roo.SplitBar.BOTTOM = 4;
9112 /*
9113  * Based on:
9114  * Ext JS Library 1.1.1
9115  * Copyright(c) 2006-2007, Ext JS, LLC.
9116  *
9117  * Originally Released Under LGPL - original licence link has changed is not relivant.
9118  *
9119  * Fork - LGPL
9120  * <script type="text/javascript">
9121  */
9122
9123 /**
9124  * @class Roo.View
9125  * @extends Roo.util.Observable
9126  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9127  * This class also supports single and multi selection modes. <br>
9128  * Create a data model bound view:
9129  <pre><code>
9130  var store = new Roo.data.Store(...);
9131
9132  var view = new Roo.View({
9133     el : "my-element",
9134     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9135  
9136     singleSelect: true,
9137     selectedClass: "ydataview-selected",
9138     store: store
9139  });
9140
9141  // listen for node click?
9142  view.on("click", function(vw, index, node, e){
9143  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9144  });
9145
9146  // load XML data
9147  dataModel.load("foobar.xml");
9148  </code></pre>
9149  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9150  * <br><br>
9151  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9152  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9153  * 
9154  * Note: old style constructor is still suported (container, template, config)
9155  * 
9156  * @constructor
9157  * Create a new View
9158  * @param {Object} config The config object
9159  * 
9160  */
9161 Roo.View = function(config, depreciated_tpl, depreciated_config){
9162     
9163     if (typeof(depreciated_tpl) == 'undefined') {
9164         // new way.. - universal constructor.
9165         Roo.apply(this, config);
9166         this.el  = Roo.get(this.el);
9167     } else {
9168         // old format..
9169         this.el  = Roo.get(config);
9170         this.tpl = depreciated_tpl;
9171         Roo.apply(this, depreciated_config);
9172     }
9173     this.wrapEl  = this.el.wrap().wrap();
9174     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9175     
9176     
9177     if(typeof(this.tpl) == "string"){
9178         this.tpl = new Roo.Template(this.tpl);
9179     } else {
9180         // support xtype ctors..
9181         this.tpl = new Roo.factory(this.tpl, Roo);
9182     }
9183     
9184     
9185     this.tpl.compile();
9186    
9187   
9188     
9189      
9190     /** @private */
9191     this.addEvents({
9192         /**
9193          * @event beforeclick
9194          * Fires before a click is processed. Returns false to cancel the default action.
9195          * @param {Roo.View} this
9196          * @param {Number} index The index of the target node
9197          * @param {HTMLElement} node The target node
9198          * @param {Roo.EventObject} e The raw event object
9199          */
9200             "beforeclick" : true,
9201         /**
9202          * @event click
9203          * Fires when a template node is clicked.
9204          * @param {Roo.View} this
9205          * @param {Number} index The index of the target node
9206          * @param {HTMLElement} node The target node
9207          * @param {Roo.EventObject} e The raw event object
9208          */
9209             "click" : true,
9210         /**
9211          * @event dblclick
9212          * Fires when a template node is double clicked.
9213          * @param {Roo.View} this
9214          * @param {Number} index The index of the target node
9215          * @param {HTMLElement} node The target node
9216          * @param {Roo.EventObject} e The raw event object
9217          */
9218             "dblclick" : true,
9219         /**
9220          * @event contextmenu
9221          * Fires when a template node is right clicked.
9222          * @param {Roo.View} this
9223          * @param {Number} index The index of the target node
9224          * @param {HTMLElement} node The target node
9225          * @param {Roo.EventObject} e The raw event object
9226          */
9227             "contextmenu" : true,
9228         /**
9229          * @event selectionchange
9230          * Fires when the selected nodes change.
9231          * @param {Roo.View} this
9232          * @param {Array} selections Array of the selected nodes
9233          */
9234             "selectionchange" : true,
9235     
9236         /**
9237          * @event beforeselect
9238          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9239          * @param {Roo.View} this
9240          * @param {HTMLElement} node The node to be selected
9241          * @param {Array} selections Array of currently selected nodes
9242          */
9243             "beforeselect" : true,
9244         /**
9245          * @event preparedata
9246          * Fires on every row to render, to allow you to change the data.
9247          * @param {Roo.View} this
9248          * @param {Object} data to be rendered (change this)
9249          */
9250           "preparedata" : true
9251           
9252           
9253         });
9254
9255
9256
9257     this.el.on({
9258         "click": this.onClick,
9259         "dblclick": this.onDblClick,
9260         "contextmenu": this.onContextMenu,
9261         scope:this
9262     });
9263
9264     this.selections = [];
9265     this.nodes = [];
9266     this.cmp = new Roo.CompositeElementLite([]);
9267     if(this.store){
9268         this.store = Roo.factory(this.store, Roo.data);
9269         this.setStore(this.store, true);
9270     }
9271     
9272     if ( this.footer && this.footer.xtype) {
9273            
9274          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9275         
9276         this.footer.dataSource = this.store
9277         this.footer.container = fctr;
9278         this.footer = Roo.factory(this.footer, Roo);
9279         fctr.insertFirst(this.el);
9280         
9281         // this is a bit insane - as the paging toolbar seems to detach the el..
9282 //        dom.parentNode.parentNode.parentNode
9283          // they get detached?
9284     }
9285     
9286     
9287     Roo.View.superclass.constructor.call(this);
9288     
9289     
9290 };
9291
9292 Roo.extend(Roo.View, Roo.util.Observable, {
9293     
9294      /**
9295      * @cfg {Roo.data.Store} store Data store to load data from.
9296      */
9297     store : false,
9298     
9299     /**
9300      * @cfg {String|Roo.Element} el The container element.
9301      */
9302     el : '',
9303     
9304     /**
9305      * @cfg {String|Roo.Template} tpl The template used by this View 
9306      */
9307     tpl : false,
9308     /**
9309      * @cfg {String} dataName the named area of the template to use as the data area
9310      *                          Works with domtemplates roo-name="name"
9311      */
9312     dataName: false,
9313     /**
9314      * @cfg {String} selectedClass The css class to add to selected nodes
9315      */
9316     selectedClass : "x-view-selected",
9317      /**
9318      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9319      */
9320     emptyText : "",
9321     
9322     /**
9323      * @cfg {String} text to display on mask (default Loading)
9324      */
9325     mask : false,
9326     /**
9327      * @cfg {Boolean} multiSelect Allow multiple selection
9328      */
9329     multiSelect : false,
9330     /**
9331      * @cfg {Boolean} singleSelect Allow single selection
9332      */
9333     singleSelect:  false,
9334     
9335     /**
9336      * @cfg {Boolean} toggleSelect - selecting 
9337      */
9338     toggleSelect : false,
9339     
9340     /**
9341      * Returns the element this view is bound to.
9342      * @return {Roo.Element}
9343      */
9344     getEl : function(){
9345         return this.wrapEl;
9346     },
9347     
9348     
9349
9350     /**
9351      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9352      */
9353     refresh : function(){
9354         var t = this.tpl;
9355         
9356         // if we are using something like 'domtemplate', then
9357         // the what gets used is:
9358         // t.applySubtemplate(NAME, data, wrapping data..)
9359         // the outer template then get' applied with
9360         //     the store 'extra data'
9361         // and the body get's added to the
9362         //      roo-name="data" node?
9363         //      <span class='roo-tpl-{name}'></span> ?????
9364         
9365         
9366         
9367         this.clearSelections();
9368         this.el.update("");
9369         var html = [];
9370         var records = this.store.getRange();
9371         if(records.length < 1) {
9372             
9373             // is this valid??  = should it render a template??
9374             
9375             this.el.update(this.emptyText);
9376             return;
9377         }
9378         var el = this.el;
9379         if (this.dataName) {
9380             this.el.update(t.apply(this.store.meta)); //????
9381             el = this.el.child('.roo-tpl-' + this.dataName);
9382         }
9383         
9384         for(var i = 0, len = records.length; i < len; i++){
9385             var data = this.prepareData(records[i].data, i, records[i]);
9386             this.fireEvent("preparedata", this, data, i, records[i]);
9387             html[html.length] = Roo.util.Format.trim(
9388                 this.dataName ?
9389                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9390                     t.apply(data)
9391             );
9392         }
9393         
9394         
9395         
9396         el.update(html.join(""));
9397         this.nodes = el.dom.childNodes;
9398         this.updateIndexes(0);
9399     },
9400
9401     /**
9402      * Function to override to reformat the data that is sent to
9403      * the template for each node.
9404      * DEPRICATED - use the preparedata event handler.
9405      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9406      * a JSON object for an UpdateManager bound view).
9407      */
9408     prepareData : function(data, index, record)
9409     {
9410         this.fireEvent("preparedata", this, data, index, record);
9411         return data;
9412     },
9413
9414     onUpdate : function(ds, record){
9415         this.clearSelections();
9416         var index = this.store.indexOf(record);
9417         var n = this.nodes[index];
9418         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9419         n.parentNode.removeChild(n);
9420         this.updateIndexes(index, index);
9421     },
9422
9423     
9424     
9425 // --------- FIXME     
9426     onAdd : function(ds, records, index)
9427     {
9428         this.clearSelections();
9429         if(this.nodes.length == 0){
9430             this.refresh();
9431             return;
9432         }
9433         var n = this.nodes[index];
9434         for(var i = 0, len = records.length; i < len; i++){
9435             var d = this.prepareData(records[i].data, i, records[i]);
9436             if(n){
9437                 this.tpl.insertBefore(n, d);
9438             }else{
9439                 
9440                 this.tpl.append(this.el, d);
9441             }
9442         }
9443         this.updateIndexes(index);
9444     },
9445
9446     onRemove : function(ds, record, index){
9447         this.clearSelections();
9448         var el = this.dataName  ?
9449             this.el.child('.roo-tpl-' + this.dataName) :
9450             this.el; 
9451         el.dom.removeChild(this.nodes[index]);
9452         this.updateIndexes(index);
9453     },
9454
9455     /**
9456      * Refresh an individual node.
9457      * @param {Number} index
9458      */
9459     refreshNode : function(index){
9460         this.onUpdate(this.store, this.store.getAt(index));
9461     },
9462
9463     updateIndexes : function(startIndex, endIndex){
9464         var ns = this.nodes;
9465         startIndex = startIndex || 0;
9466         endIndex = endIndex || ns.length - 1;
9467         for(var i = startIndex; i <= endIndex; i++){
9468             ns[i].nodeIndex = i;
9469         }
9470     },
9471
9472     /**
9473      * Changes the data store this view uses and refresh the view.
9474      * @param {Store} store
9475      */
9476     setStore : function(store, initial){
9477         if(!initial && this.store){
9478             this.store.un("datachanged", this.refresh);
9479             this.store.un("add", this.onAdd);
9480             this.store.un("remove", this.onRemove);
9481             this.store.un("update", this.onUpdate);
9482             this.store.un("clear", this.refresh);
9483             this.store.un("beforeload", this.onBeforeLoad);
9484             this.store.un("load", this.onLoad);
9485             this.store.un("loadexception", this.onLoad);
9486         }
9487         if(store){
9488           
9489             store.on("datachanged", this.refresh, this);
9490             store.on("add", this.onAdd, this);
9491             store.on("remove", this.onRemove, this);
9492             store.on("update", this.onUpdate, this);
9493             store.on("clear", this.refresh, this);
9494             store.on("beforeload", this.onBeforeLoad, this);
9495             store.on("load", this.onLoad, this);
9496             store.on("loadexception", this.onLoad, this);
9497         }
9498         
9499         if(store){
9500             this.refresh();
9501         }
9502     },
9503     /**
9504      * onbeforeLoad - masks the loading area.
9505      *
9506      */
9507     onBeforeLoad : function()
9508     {
9509         this.el.update("");
9510         this.el.mask(this.mask ? this.mask : "Loading" ); 
9511     },
9512     onLoad : function ()
9513     {
9514         this.el.unmask();
9515     },
9516     
9517
9518     /**
9519      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9520      * @param {HTMLElement} node
9521      * @return {HTMLElement} The template node
9522      */
9523     findItemFromChild : function(node){
9524         var el = this.dataName  ?
9525             this.el.child('.roo-tpl-' + this.dataName,true) :
9526             this.el.dom; 
9527         
9528         if(!node || node.parentNode == el){
9529                     return node;
9530             }
9531             var p = node.parentNode;
9532             while(p && p != el){
9533             if(p.parentNode == el){
9534                 return p;
9535             }
9536             p = p.parentNode;
9537         }
9538             return null;
9539     },
9540
9541     /** @ignore */
9542     onClick : function(e){
9543         var item = this.findItemFromChild(e.getTarget());
9544         if(item){
9545             var index = this.indexOf(item);
9546             if(this.onItemClick(item, index, e) !== false){
9547                 this.fireEvent("click", this, index, item, e);
9548             }
9549         }else{
9550             this.clearSelections();
9551         }
9552     },
9553
9554     /** @ignore */
9555     onContextMenu : function(e){
9556         var item = this.findItemFromChild(e.getTarget());
9557         if(item){
9558             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9559         }
9560     },
9561
9562     /** @ignore */
9563     onDblClick : function(e){
9564         var item = this.findItemFromChild(e.getTarget());
9565         if(item){
9566             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9567         }
9568     },
9569
9570     onItemClick : function(item, index, e)
9571     {
9572         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9573             return false;
9574         }
9575         if (this.toggleSelect) {
9576             var m = this.isSelected(item) ? 'unselect' : 'select';
9577             Roo.log(m);
9578             var _t = this;
9579             _t[m](item, true, false);
9580             return true;
9581         }
9582         if(this.multiSelect || this.singleSelect){
9583             if(this.multiSelect && e.shiftKey && this.lastSelection){
9584                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9585             }else{
9586                 this.select(item, this.multiSelect && e.ctrlKey);
9587                 this.lastSelection = item;
9588             }
9589             e.preventDefault();
9590         }
9591         return true;
9592     },
9593
9594     /**
9595      * Get the number of selected nodes.
9596      * @return {Number}
9597      */
9598     getSelectionCount : function(){
9599         return this.selections.length;
9600     },
9601
9602     /**
9603      * Get the currently selected nodes.
9604      * @return {Array} An array of HTMLElements
9605      */
9606     getSelectedNodes : function(){
9607         return this.selections;
9608     },
9609
9610     /**
9611      * Get the indexes of the selected nodes.
9612      * @return {Array}
9613      */
9614     getSelectedIndexes : function(){
9615         var indexes = [], s = this.selections;
9616         for(var i = 0, len = s.length; i < len; i++){
9617             indexes.push(s[i].nodeIndex);
9618         }
9619         return indexes;
9620     },
9621
9622     /**
9623      * Clear all selections
9624      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9625      */
9626     clearSelections : function(suppressEvent){
9627         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9628             this.cmp.elements = this.selections;
9629             this.cmp.removeClass(this.selectedClass);
9630             this.selections = [];
9631             if(!suppressEvent){
9632                 this.fireEvent("selectionchange", this, this.selections);
9633             }
9634         }
9635     },
9636
9637     /**
9638      * Returns true if the passed node is selected
9639      * @param {HTMLElement/Number} node The node or node index
9640      * @return {Boolean}
9641      */
9642     isSelected : function(node){
9643         var s = this.selections;
9644         if(s.length < 1){
9645             return false;
9646         }
9647         node = this.getNode(node);
9648         return s.indexOf(node) !== -1;
9649     },
9650
9651     /**
9652      * Selects nodes.
9653      * @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
9654      * @param {Boolean} keepExisting (optional) true to keep existing selections
9655      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9656      */
9657     select : function(nodeInfo, keepExisting, suppressEvent){
9658         if(nodeInfo instanceof Array){
9659             if(!keepExisting){
9660                 this.clearSelections(true);
9661             }
9662             for(var i = 0, len = nodeInfo.length; i < len; i++){
9663                 this.select(nodeInfo[i], true, true);
9664             }
9665             return;
9666         } 
9667         var node = this.getNode(nodeInfo);
9668         if(!node || this.isSelected(node)){
9669             return; // already selected.
9670         }
9671         if(!keepExisting){
9672             this.clearSelections(true);
9673         }
9674         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9675             Roo.fly(node).addClass(this.selectedClass);
9676             this.selections.push(node);
9677             if(!suppressEvent){
9678                 this.fireEvent("selectionchange", this, this.selections);
9679             }
9680         }
9681         
9682         
9683     },
9684       /**
9685      * Unselects nodes.
9686      * @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
9687      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9688      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9689      */
9690     unselect : function(nodeInfo, keepExisting, suppressEvent)
9691     {
9692         if(nodeInfo instanceof Array){
9693             Roo.each(this.selections, function(s) {
9694                 this.unselect(s, nodeInfo);
9695             }, this);
9696             return;
9697         }
9698         var node = this.getNode(nodeInfo);
9699         if(!node || !this.isSelected(node)){
9700             Roo.log("not selected");
9701             return; // not selected.
9702         }
9703         // fireevent???
9704         var ns = [];
9705         Roo.each(this.selections, function(s) {
9706             if (s == node ) {
9707                 Roo.fly(node).removeClass(this.selectedClass);
9708
9709                 return;
9710             }
9711             ns.push(s);
9712         },this);
9713         
9714         this.selections= ns;
9715         this.fireEvent("selectionchange", this, this.selections);
9716     },
9717
9718     /**
9719      * Gets a template node.
9720      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9721      * @return {HTMLElement} The node or null if it wasn't found
9722      */
9723     getNode : function(nodeInfo){
9724         if(typeof nodeInfo == "string"){
9725             return document.getElementById(nodeInfo);
9726         }else if(typeof nodeInfo == "number"){
9727             return this.nodes[nodeInfo];
9728         }
9729         return nodeInfo;
9730     },
9731
9732     /**
9733      * Gets a range template nodes.
9734      * @param {Number} startIndex
9735      * @param {Number} endIndex
9736      * @return {Array} An array of nodes
9737      */
9738     getNodes : function(start, end){
9739         var ns = this.nodes;
9740         start = start || 0;
9741         end = typeof end == "undefined" ? ns.length - 1 : end;
9742         var nodes = [];
9743         if(start <= end){
9744             for(var i = start; i <= end; i++){
9745                 nodes.push(ns[i]);
9746             }
9747         } else{
9748             for(var i = start; i >= end; i--){
9749                 nodes.push(ns[i]);
9750             }
9751         }
9752         return nodes;
9753     },
9754
9755     /**
9756      * Finds the index of the passed node
9757      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9758      * @return {Number} The index of the node or -1
9759      */
9760     indexOf : function(node){
9761         node = this.getNode(node);
9762         if(typeof node.nodeIndex == "number"){
9763             return node.nodeIndex;
9764         }
9765         var ns = this.nodes;
9766         for(var i = 0, len = ns.length; i < len; i++){
9767             if(ns[i] == node){
9768                 return i;
9769             }
9770         }
9771         return -1;
9772     }
9773 });
9774 /*
9775  * Based on:
9776  * Ext JS Library 1.1.1
9777  * Copyright(c) 2006-2007, Ext JS, LLC.
9778  *
9779  * Originally Released Under LGPL - original licence link has changed is not relivant.
9780  *
9781  * Fork - LGPL
9782  * <script type="text/javascript">
9783  */
9784
9785 /**
9786  * @class Roo.JsonView
9787  * @extends Roo.View
9788  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9789 <pre><code>
9790 var view = new Roo.JsonView({
9791     container: "my-element",
9792     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9793     multiSelect: true, 
9794     jsonRoot: "data" 
9795 });
9796
9797 // listen for node click?
9798 view.on("click", function(vw, index, node, e){
9799     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9800 });
9801
9802 // direct load of JSON data
9803 view.load("foobar.php");
9804
9805 // Example from my blog list
9806 var tpl = new Roo.Template(
9807     '&lt;div class="entry"&gt;' +
9808     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9809     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9810     "&lt;/div&gt;&lt;hr /&gt;"
9811 );
9812
9813 var moreView = new Roo.JsonView({
9814     container :  "entry-list", 
9815     template : tpl,
9816     jsonRoot: "posts"
9817 });
9818 moreView.on("beforerender", this.sortEntries, this);
9819 moreView.load({
9820     url: "/blog/get-posts.php",
9821     params: "allposts=true",
9822     text: "Loading Blog Entries..."
9823 });
9824 </code></pre>
9825
9826 * Note: old code is supported with arguments : (container, template, config)
9827
9828
9829  * @constructor
9830  * Create a new JsonView
9831  * 
9832  * @param {Object} config The config object
9833  * 
9834  */
9835 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9836     
9837     
9838     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9839
9840     var um = this.el.getUpdateManager();
9841     um.setRenderer(this);
9842     um.on("update", this.onLoad, this);
9843     um.on("failure", this.onLoadException, this);
9844
9845     /**
9846      * @event beforerender
9847      * Fires before rendering of the downloaded JSON data.
9848      * @param {Roo.JsonView} this
9849      * @param {Object} data The JSON data loaded
9850      */
9851     /**
9852      * @event load
9853      * Fires when data is loaded.
9854      * @param {Roo.JsonView} this
9855      * @param {Object} data The JSON data loaded
9856      * @param {Object} response The raw Connect response object
9857      */
9858     /**
9859      * @event loadexception
9860      * Fires when loading fails.
9861      * @param {Roo.JsonView} this
9862      * @param {Object} response The raw Connect response object
9863      */
9864     this.addEvents({
9865         'beforerender' : true,
9866         'load' : true,
9867         'loadexception' : true
9868     });
9869 };
9870 Roo.extend(Roo.JsonView, Roo.View, {
9871     /**
9872      * @type {String} The root property in the loaded JSON object that contains the data
9873      */
9874     jsonRoot : "",
9875
9876     /**
9877      * Refreshes the view.
9878      */
9879     refresh : function(){
9880         this.clearSelections();
9881         this.el.update("");
9882         var html = [];
9883         var o = this.jsonData;
9884         if(o && o.length > 0){
9885             for(var i = 0, len = o.length; i < len; i++){
9886                 var data = this.prepareData(o[i], i, o);
9887                 html[html.length] = this.tpl.apply(data);
9888             }
9889         }else{
9890             html.push(this.emptyText);
9891         }
9892         this.el.update(html.join(""));
9893         this.nodes = this.el.dom.childNodes;
9894         this.updateIndexes(0);
9895     },
9896
9897     /**
9898      * 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.
9899      * @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:
9900      <pre><code>
9901      view.load({
9902          url: "your-url.php",
9903          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9904          callback: yourFunction,
9905          scope: yourObject, //(optional scope)
9906          discardUrl: false,
9907          nocache: false,
9908          text: "Loading...",
9909          timeout: 30,
9910          scripts: false
9911      });
9912      </code></pre>
9913      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9914      * 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.
9915      * @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}
9916      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9917      * @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.
9918      */
9919     load : function(){
9920         var um = this.el.getUpdateManager();
9921         um.update.apply(um, arguments);
9922     },
9923
9924     render : function(el, response){
9925         this.clearSelections();
9926         this.el.update("");
9927         var o;
9928         try{
9929             o = Roo.util.JSON.decode(response.responseText);
9930             if(this.jsonRoot){
9931                 
9932                 o = o[this.jsonRoot];
9933             }
9934         } catch(e){
9935         }
9936         /**
9937          * The current JSON data or null
9938          */
9939         this.jsonData = o;
9940         this.beforeRender();
9941         this.refresh();
9942     },
9943
9944 /**
9945  * Get the number of records in the current JSON dataset
9946  * @return {Number}
9947  */
9948     getCount : function(){
9949         return this.jsonData ? this.jsonData.length : 0;
9950     },
9951
9952 /**
9953  * Returns the JSON object for the specified node(s)
9954  * @param {HTMLElement/Array} node The node or an array of nodes
9955  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9956  * you get the JSON object for the node
9957  */
9958     getNodeData : function(node){
9959         if(node instanceof Array){
9960             var data = [];
9961             for(var i = 0, len = node.length; i < len; i++){
9962                 data.push(this.getNodeData(node[i]));
9963             }
9964             return data;
9965         }
9966         return this.jsonData[this.indexOf(node)] || null;
9967     },
9968
9969     beforeRender : function(){
9970         this.snapshot = this.jsonData;
9971         if(this.sortInfo){
9972             this.sort.apply(this, this.sortInfo);
9973         }
9974         this.fireEvent("beforerender", this, this.jsonData);
9975     },
9976
9977     onLoad : function(el, o){
9978         this.fireEvent("load", this, this.jsonData, o);
9979     },
9980
9981     onLoadException : function(el, o){
9982         this.fireEvent("loadexception", this, o);
9983     },
9984
9985 /**
9986  * Filter the data by a specific property.
9987  * @param {String} property A property on your JSON objects
9988  * @param {String/RegExp} value Either string that the property values
9989  * should start with, or a RegExp to test against the property
9990  */
9991     filter : function(property, value){
9992         if(this.jsonData){
9993             var data = [];
9994             var ss = this.snapshot;
9995             if(typeof value == "string"){
9996                 var vlen = value.length;
9997                 if(vlen == 0){
9998                     this.clearFilter();
9999                     return;
10000                 }
10001                 value = value.toLowerCase();
10002                 for(var i = 0, len = ss.length; i < len; i++){
10003                     var o = ss[i];
10004                     if(o[property].substr(0, vlen).toLowerCase() == value){
10005                         data.push(o);
10006                     }
10007                 }
10008             } else if(value.exec){ // regex?
10009                 for(var i = 0, len = ss.length; i < len; i++){
10010                     var o = ss[i];
10011                     if(value.test(o[property])){
10012                         data.push(o);
10013                     }
10014                 }
10015             } else{
10016                 return;
10017             }
10018             this.jsonData = data;
10019             this.refresh();
10020         }
10021     },
10022
10023 /**
10024  * Filter by a function. The passed function will be called with each
10025  * object in the current dataset. If the function returns true the value is kept,
10026  * otherwise it is filtered.
10027  * @param {Function} fn
10028  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10029  */
10030     filterBy : function(fn, scope){
10031         if(this.jsonData){
10032             var data = [];
10033             var ss = this.snapshot;
10034             for(var i = 0, len = ss.length; i < len; i++){
10035                 var o = ss[i];
10036                 if(fn.call(scope || this, o)){
10037                     data.push(o);
10038                 }
10039             }
10040             this.jsonData = data;
10041             this.refresh();
10042         }
10043     },
10044
10045 /**
10046  * Clears the current filter.
10047  */
10048     clearFilter : function(){
10049         if(this.snapshot && this.jsonData != this.snapshot){
10050             this.jsonData = this.snapshot;
10051             this.refresh();
10052         }
10053     },
10054
10055
10056 /**
10057  * Sorts the data for this view and refreshes it.
10058  * @param {String} property A property on your JSON objects to sort on
10059  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10060  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10061  */
10062     sort : function(property, dir, sortType){
10063         this.sortInfo = Array.prototype.slice.call(arguments, 0);
10064         if(this.jsonData){
10065             var p = property;
10066             var dsc = dir && dir.toLowerCase() == "desc";
10067             var f = function(o1, o2){
10068                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10069                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10070                 ;
10071                 if(v1 < v2){
10072                     return dsc ? +1 : -1;
10073                 } else if(v1 > v2){
10074                     return dsc ? -1 : +1;
10075                 } else{
10076                     return 0;
10077                 }
10078             };
10079             this.jsonData.sort(f);
10080             this.refresh();
10081             if(this.jsonData != this.snapshot){
10082                 this.snapshot.sort(f);
10083             }
10084         }
10085     }
10086 });/*
10087  * Based on:
10088  * Ext JS Library 1.1.1
10089  * Copyright(c) 2006-2007, Ext JS, LLC.
10090  *
10091  * Originally Released Under LGPL - original licence link has changed is not relivant.
10092  *
10093  * Fork - LGPL
10094  * <script type="text/javascript">
10095  */
10096  
10097
10098 /**
10099  * @class Roo.ColorPalette
10100  * @extends Roo.Component
10101  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10102  * Here's an example of typical usage:
10103  * <pre><code>
10104 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10105 cp.render('my-div');
10106
10107 cp.on('select', function(palette, selColor){
10108     // do something with selColor
10109 });
10110 </code></pre>
10111  * @constructor
10112  * Create a new ColorPalette
10113  * @param {Object} config The config object
10114  */
10115 Roo.ColorPalette = function(config){
10116     Roo.ColorPalette.superclass.constructor.call(this, config);
10117     this.addEvents({
10118         /**
10119              * @event select
10120              * Fires when a color is selected
10121              * @param {ColorPalette} this
10122              * @param {String} color The 6-digit color hex code (without the # symbol)
10123              */
10124         select: true
10125     });
10126
10127     if(this.handler){
10128         this.on("select", this.handler, this.scope, true);
10129     }
10130 };
10131 Roo.extend(Roo.ColorPalette, Roo.Component, {
10132     /**
10133      * @cfg {String} itemCls
10134      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10135      */
10136     itemCls : "x-color-palette",
10137     /**
10138      * @cfg {String} value
10139      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10140      * the hex codes are case-sensitive.
10141      */
10142     value : null,
10143     clickEvent:'click',
10144     // private
10145     ctype: "Roo.ColorPalette",
10146
10147     /**
10148      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10149      */
10150     allowReselect : false,
10151
10152     /**
10153      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10154      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10155      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10156      * of colors with the width setting until the box is symmetrical.</p>
10157      * <p>You can override individual colors if needed:</p>
10158      * <pre><code>
10159 var cp = new Roo.ColorPalette();
10160 cp.colors[0] = "FF0000";  // change the first box to red
10161 </code></pre>
10162
10163 Or you can provide a custom array of your own for complete control:
10164 <pre><code>
10165 var cp = new Roo.ColorPalette();
10166 cp.colors = ["000000", "993300", "333300"];
10167 </code></pre>
10168      * @type Array
10169      */
10170     colors : [
10171         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10172         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10173         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10174         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10175         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10176     ],
10177
10178     // private
10179     onRender : function(container, position){
10180         var t = new Roo.MasterTemplate(
10181             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10182         );
10183         var c = this.colors;
10184         for(var i = 0, len = c.length; i < len; i++){
10185             t.add([c[i]]);
10186         }
10187         var el = document.createElement("div");
10188         el.className = this.itemCls;
10189         t.overwrite(el);
10190         container.dom.insertBefore(el, position);
10191         this.el = Roo.get(el);
10192         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10193         if(this.clickEvent != 'click'){
10194             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10195         }
10196     },
10197
10198     // private
10199     afterRender : function(){
10200         Roo.ColorPalette.superclass.afterRender.call(this);
10201         if(this.value){
10202             var s = this.value;
10203             this.value = null;
10204             this.select(s);
10205         }
10206     },
10207
10208     // private
10209     handleClick : function(e, t){
10210         e.preventDefault();
10211         if(!this.disabled){
10212             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10213             this.select(c.toUpperCase());
10214         }
10215     },
10216
10217     /**
10218      * Selects the specified color in the palette (fires the select event)
10219      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10220      */
10221     select : function(color){
10222         color = color.replace("#", "");
10223         if(color != this.value || this.allowReselect){
10224             var el = this.el;
10225             if(this.value){
10226                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10227             }
10228             el.child("a.color-"+color).addClass("x-color-palette-sel");
10229             this.value = color;
10230             this.fireEvent("select", this, color);
10231         }
10232     }
10233 });/*
10234  * Based on:
10235  * Ext JS Library 1.1.1
10236  * Copyright(c) 2006-2007, Ext JS, LLC.
10237  *
10238  * Originally Released Under LGPL - original licence link has changed is not relivant.
10239  *
10240  * Fork - LGPL
10241  * <script type="text/javascript">
10242  */
10243  
10244 /**
10245  * @class Roo.DatePicker
10246  * @extends Roo.Component
10247  * Simple date picker class.
10248  * @constructor
10249  * Create a new DatePicker
10250  * @param {Object} config The config object
10251  */
10252 Roo.DatePicker = function(config){
10253     Roo.DatePicker.superclass.constructor.call(this, config);
10254
10255     this.value = config && config.value ?
10256                  config.value.clearTime() : new Date().clearTime();
10257
10258     this.addEvents({
10259         /**
10260              * @event select
10261              * Fires when a date is selected
10262              * @param {DatePicker} this
10263              * @param {Date} date The selected date
10264              */
10265         'select': true,
10266         /**
10267              * @event monthchange
10268              * Fires when the displayed month changes 
10269              * @param {DatePicker} this
10270              * @param {Date} date The selected month
10271              */
10272         'monthchange': true
10273     });
10274
10275     if(this.handler){
10276         this.on("select", this.handler,  this.scope || this);
10277     }
10278     // build the disabledDatesRE
10279     if(!this.disabledDatesRE && this.disabledDates){
10280         var dd = this.disabledDates;
10281         var re = "(?:";
10282         for(var i = 0; i < dd.length; i++){
10283             re += dd[i];
10284             if(i != dd.length-1) re += "|";
10285         }
10286         this.disabledDatesRE = new RegExp(re + ")");
10287     }
10288 };
10289
10290 Roo.extend(Roo.DatePicker, Roo.Component, {
10291     /**
10292      * @cfg {String} todayText
10293      * The text to display on the button that selects the current date (defaults to "Today")
10294      */
10295     todayText : "Today",
10296     /**
10297      * @cfg {String} okText
10298      * The text to display on the ok button
10299      */
10300     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10301     /**
10302      * @cfg {String} cancelText
10303      * The text to display on the cancel button
10304      */
10305     cancelText : "Cancel",
10306     /**
10307      * @cfg {String} todayTip
10308      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10309      */
10310     todayTip : "{0} (Spacebar)",
10311     /**
10312      * @cfg {Date} minDate
10313      * Minimum allowable date (JavaScript date object, defaults to null)
10314      */
10315     minDate : null,
10316     /**
10317      * @cfg {Date} maxDate
10318      * Maximum allowable date (JavaScript date object, defaults to null)
10319      */
10320     maxDate : null,
10321     /**
10322      * @cfg {String} minText
10323      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10324      */
10325     minText : "This date is before the minimum date",
10326     /**
10327      * @cfg {String} maxText
10328      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10329      */
10330     maxText : "This date is after the maximum date",
10331     /**
10332      * @cfg {String} format
10333      * The default date format string which can be overriden for localization support.  The format must be
10334      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10335      */
10336     format : "m/d/y",
10337     /**
10338      * @cfg {Array} disabledDays
10339      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10340      */
10341     disabledDays : null,
10342     /**
10343      * @cfg {String} disabledDaysText
10344      * The tooltip to display when the date falls on a disabled day (defaults to "")
10345      */
10346     disabledDaysText : "",
10347     /**
10348      * @cfg {RegExp} disabledDatesRE
10349      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10350      */
10351     disabledDatesRE : null,
10352     /**
10353      * @cfg {String} disabledDatesText
10354      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10355      */
10356     disabledDatesText : "",
10357     /**
10358      * @cfg {Boolean} constrainToViewport
10359      * True to constrain the date picker to the viewport (defaults to true)
10360      */
10361     constrainToViewport : true,
10362     /**
10363      * @cfg {Array} monthNames
10364      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10365      */
10366     monthNames : Date.monthNames,
10367     /**
10368      * @cfg {Array} dayNames
10369      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10370      */
10371     dayNames : Date.dayNames,
10372     /**
10373      * @cfg {String} nextText
10374      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10375      */
10376     nextText: 'Next Month (Control+Right)',
10377     /**
10378      * @cfg {String} prevText
10379      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10380      */
10381     prevText: 'Previous Month (Control+Left)',
10382     /**
10383      * @cfg {String} monthYearText
10384      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10385      */
10386     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10387     /**
10388      * @cfg {Number} startDay
10389      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10390      */
10391     startDay : 0,
10392     /**
10393      * @cfg {Bool} showClear
10394      * Show a clear button (usefull for date form elements that can be blank.)
10395      */
10396     
10397     showClear: false,
10398     
10399     /**
10400      * Sets the value of the date field
10401      * @param {Date} value The date to set
10402      */
10403     setValue : function(value){
10404         var old = this.value;
10405         
10406         if (typeof(value) == 'string') {
10407          
10408             value = Date.parseDate(value, this.format);
10409         }
10410         if (!value) {
10411             value = new Date();
10412         }
10413         
10414         this.value = value.clearTime(true);
10415         if(this.el){
10416             this.update(this.value);
10417         }
10418     },
10419
10420     /**
10421      * Gets the current selected value of the date field
10422      * @return {Date} The selected date
10423      */
10424     getValue : function(){
10425         return this.value;
10426     },
10427
10428     // private
10429     focus : function(){
10430         if(this.el){
10431             this.update(this.activeDate);
10432         }
10433     },
10434
10435     // privateval
10436     onRender : function(container, position){
10437         
10438         var m = [
10439              '<table cellspacing="0">',
10440                 '<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>',
10441                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10442         var dn = this.dayNames;
10443         for(var i = 0; i < 7; i++){
10444             var d = this.startDay+i;
10445             if(d > 6){
10446                 d = d-7;
10447             }
10448             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10449         }
10450         m[m.length] = "</tr></thead><tbody><tr>";
10451         for(var i = 0; i < 42; i++) {
10452             if(i % 7 == 0 && i != 0){
10453                 m[m.length] = "</tr><tr>";
10454             }
10455             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10456         }
10457         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10458             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10459
10460         var el = document.createElement("div");
10461         el.className = "x-date-picker";
10462         el.innerHTML = m.join("");
10463
10464         container.dom.insertBefore(el, position);
10465
10466         this.el = Roo.get(el);
10467         this.eventEl = Roo.get(el.firstChild);
10468
10469         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10470             handler: this.showPrevMonth,
10471             scope: this,
10472             preventDefault:true,
10473             stopDefault:true
10474         });
10475
10476         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10477             handler: this.showNextMonth,
10478             scope: this,
10479             preventDefault:true,
10480             stopDefault:true
10481         });
10482
10483         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10484
10485         this.monthPicker = this.el.down('div.x-date-mp');
10486         this.monthPicker.enableDisplayMode('block');
10487         
10488         var kn = new Roo.KeyNav(this.eventEl, {
10489             "left" : function(e){
10490                 e.ctrlKey ?
10491                     this.showPrevMonth() :
10492                     this.update(this.activeDate.add("d", -1));
10493             },
10494
10495             "right" : function(e){
10496                 e.ctrlKey ?
10497                     this.showNextMonth() :
10498                     this.update(this.activeDate.add("d", 1));
10499             },
10500
10501             "up" : function(e){
10502                 e.ctrlKey ?
10503                     this.showNextYear() :
10504                     this.update(this.activeDate.add("d", -7));
10505             },
10506
10507             "down" : function(e){
10508                 e.ctrlKey ?
10509                     this.showPrevYear() :
10510                     this.update(this.activeDate.add("d", 7));
10511             },
10512
10513             "pageUp" : function(e){
10514                 this.showNextMonth();
10515             },
10516
10517             "pageDown" : function(e){
10518                 this.showPrevMonth();
10519             },
10520
10521             "enter" : function(e){
10522                 e.stopPropagation();
10523                 return true;
10524             },
10525
10526             scope : this
10527         });
10528
10529         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10530
10531         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10532
10533         this.el.unselectable();
10534         
10535         this.cells = this.el.select("table.x-date-inner tbody td");
10536         this.textNodes = this.el.query("table.x-date-inner tbody span");
10537
10538         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10539             text: "&#160;",
10540             tooltip: this.monthYearText
10541         });
10542
10543         this.mbtn.on('click', this.showMonthPicker, this);
10544         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10545
10546
10547         var today = (new Date()).dateFormat(this.format);
10548         
10549         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10550         if (this.showClear) {
10551             baseTb.add( new Roo.Toolbar.Fill());
10552         }
10553         baseTb.add({
10554             text: String.format(this.todayText, today),
10555             tooltip: String.format(this.todayTip, today),
10556             handler: this.selectToday,
10557             scope: this
10558         });
10559         
10560         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10561             
10562         //});
10563         if (this.showClear) {
10564             
10565             baseTb.add( new Roo.Toolbar.Fill());
10566             baseTb.add({
10567                 text: '&#160;',
10568                 cls: 'x-btn-icon x-btn-clear',
10569                 handler: function() {
10570                     //this.value = '';
10571                     this.fireEvent("select", this, '');
10572                 },
10573                 scope: this
10574             });
10575         }
10576         
10577         
10578         if(Roo.isIE){
10579             this.el.repaint();
10580         }
10581         this.update(this.value);
10582     },
10583
10584     createMonthPicker : function(){
10585         if(!this.monthPicker.dom.firstChild){
10586             var buf = ['<table border="0" cellspacing="0">'];
10587             for(var i = 0; i < 6; i++){
10588                 buf.push(
10589                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10590                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10591                     i == 0 ?
10592                     '<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>' :
10593                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10594                 );
10595             }
10596             buf.push(
10597                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10598                     this.okText,
10599                     '</button><button type="button" class="x-date-mp-cancel">',
10600                     this.cancelText,
10601                     '</button></td></tr>',
10602                 '</table>'
10603             );
10604             this.monthPicker.update(buf.join(''));
10605             this.monthPicker.on('click', this.onMonthClick, this);
10606             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10607
10608             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10609             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10610
10611             this.mpMonths.each(function(m, a, i){
10612                 i += 1;
10613                 if((i%2) == 0){
10614                     m.dom.xmonth = 5 + Math.round(i * .5);
10615                 }else{
10616                     m.dom.xmonth = Math.round((i-1) * .5);
10617                 }
10618             });
10619         }
10620     },
10621
10622     showMonthPicker : function(){
10623         this.createMonthPicker();
10624         var size = this.el.getSize();
10625         this.monthPicker.setSize(size);
10626         this.monthPicker.child('table').setSize(size);
10627
10628         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10629         this.updateMPMonth(this.mpSelMonth);
10630         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10631         this.updateMPYear(this.mpSelYear);
10632
10633         this.monthPicker.slideIn('t', {duration:.2});
10634     },
10635
10636     updateMPYear : function(y){
10637         this.mpyear = y;
10638         var ys = this.mpYears.elements;
10639         for(var i = 1; i <= 10; i++){
10640             var td = ys[i-1], y2;
10641             if((i%2) == 0){
10642                 y2 = y + Math.round(i * .5);
10643                 td.firstChild.innerHTML = y2;
10644                 td.xyear = y2;
10645             }else{
10646                 y2 = y - (5-Math.round(i * .5));
10647                 td.firstChild.innerHTML = y2;
10648                 td.xyear = y2;
10649             }
10650             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10651         }
10652     },
10653
10654     updateMPMonth : function(sm){
10655         this.mpMonths.each(function(m, a, i){
10656             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10657         });
10658     },
10659
10660     selectMPMonth: function(m){
10661         
10662     },
10663
10664     onMonthClick : function(e, t){
10665         e.stopEvent();
10666         var el = new Roo.Element(t), pn;
10667         if(el.is('button.x-date-mp-cancel')){
10668             this.hideMonthPicker();
10669         }
10670         else if(el.is('button.x-date-mp-ok')){
10671             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10672             this.hideMonthPicker();
10673         }
10674         else if(pn = el.up('td.x-date-mp-month', 2)){
10675             this.mpMonths.removeClass('x-date-mp-sel');
10676             pn.addClass('x-date-mp-sel');
10677             this.mpSelMonth = pn.dom.xmonth;
10678         }
10679         else if(pn = el.up('td.x-date-mp-year', 2)){
10680             this.mpYears.removeClass('x-date-mp-sel');
10681             pn.addClass('x-date-mp-sel');
10682             this.mpSelYear = pn.dom.xyear;
10683         }
10684         else if(el.is('a.x-date-mp-prev')){
10685             this.updateMPYear(this.mpyear-10);
10686         }
10687         else if(el.is('a.x-date-mp-next')){
10688             this.updateMPYear(this.mpyear+10);
10689         }
10690     },
10691
10692     onMonthDblClick : function(e, t){
10693         e.stopEvent();
10694         var el = new Roo.Element(t), pn;
10695         if(pn = el.up('td.x-date-mp-month', 2)){
10696             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10697             this.hideMonthPicker();
10698         }
10699         else if(pn = el.up('td.x-date-mp-year', 2)){
10700             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10701             this.hideMonthPicker();
10702         }
10703     },
10704
10705     hideMonthPicker : function(disableAnim){
10706         if(this.monthPicker){
10707             if(disableAnim === true){
10708                 this.monthPicker.hide();
10709             }else{
10710                 this.monthPicker.slideOut('t', {duration:.2});
10711             }
10712         }
10713     },
10714
10715     // private
10716     showPrevMonth : function(e){
10717         this.update(this.activeDate.add("mo", -1));
10718     },
10719
10720     // private
10721     showNextMonth : function(e){
10722         this.update(this.activeDate.add("mo", 1));
10723     },
10724
10725     // private
10726     showPrevYear : function(){
10727         this.update(this.activeDate.add("y", -1));
10728     },
10729
10730     // private
10731     showNextYear : function(){
10732         this.update(this.activeDate.add("y", 1));
10733     },
10734
10735     // private
10736     handleMouseWheel : function(e){
10737         var delta = e.getWheelDelta();
10738         if(delta > 0){
10739             this.showPrevMonth();
10740             e.stopEvent();
10741         } else if(delta < 0){
10742             this.showNextMonth();
10743             e.stopEvent();
10744         }
10745     },
10746
10747     // private
10748     handleDateClick : function(e, t){
10749         e.stopEvent();
10750         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10751             this.setValue(new Date(t.dateValue));
10752             this.fireEvent("select", this, this.value);
10753         }
10754     },
10755
10756     // private
10757     selectToday : function(){
10758         this.setValue(new Date().clearTime());
10759         this.fireEvent("select", this, this.value);
10760     },
10761
10762     // private
10763     update : function(date)
10764     {
10765         var vd = this.activeDate;
10766         this.activeDate = date;
10767         if(vd && this.el){
10768             var t = date.getTime();
10769             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10770                 this.cells.removeClass("x-date-selected");
10771                 this.cells.each(function(c){
10772                    if(c.dom.firstChild.dateValue == t){
10773                        c.addClass("x-date-selected");
10774                        setTimeout(function(){
10775                             try{c.dom.firstChild.focus();}catch(e){}
10776                        }, 50);
10777                        return false;
10778                    }
10779                 });
10780                 return;
10781             }
10782         }
10783         
10784         var days = date.getDaysInMonth();
10785         var firstOfMonth = date.getFirstDateOfMonth();
10786         var startingPos = firstOfMonth.getDay()-this.startDay;
10787
10788         if(startingPos <= this.startDay){
10789             startingPos += 7;
10790         }
10791
10792         var pm = date.add("mo", -1);
10793         var prevStart = pm.getDaysInMonth()-startingPos;
10794
10795         var cells = this.cells.elements;
10796         var textEls = this.textNodes;
10797         days += startingPos;
10798
10799         // convert everything to numbers so it's fast
10800         var day = 86400000;
10801         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10802         var today = new Date().clearTime().getTime();
10803         var sel = date.clearTime().getTime();
10804         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10805         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10806         var ddMatch = this.disabledDatesRE;
10807         var ddText = this.disabledDatesText;
10808         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10809         var ddaysText = this.disabledDaysText;
10810         var format = this.format;
10811
10812         var setCellClass = function(cal, cell){
10813             cell.title = "";
10814             var t = d.getTime();
10815             cell.firstChild.dateValue = t;
10816             if(t == today){
10817                 cell.className += " x-date-today";
10818                 cell.title = cal.todayText;
10819             }
10820             if(t == sel){
10821                 cell.className += " x-date-selected";
10822                 setTimeout(function(){
10823                     try{cell.firstChild.focus();}catch(e){}
10824                 }, 50);
10825             }
10826             // disabling
10827             if(t < min) {
10828                 cell.className = " x-date-disabled";
10829                 cell.title = cal.minText;
10830                 return;
10831             }
10832             if(t > max) {
10833                 cell.className = " x-date-disabled";
10834                 cell.title = cal.maxText;
10835                 return;
10836             }
10837             if(ddays){
10838                 if(ddays.indexOf(d.getDay()) != -1){
10839                     cell.title = ddaysText;
10840                     cell.className = " x-date-disabled";
10841                 }
10842             }
10843             if(ddMatch && format){
10844                 var fvalue = d.dateFormat(format);
10845                 if(ddMatch.test(fvalue)){
10846                     cell.title = ddText.replace("%0", fvalue);
10847                     cell.className = " x-date-disabled";
10848                 }
10849             }
10850         };
10851
10852         var i = 0;
10853         for(; i < startingPos; i++) {
10854             textEls[i].innerHTML = (++prevStart);
10855             d.setDate(d.getDate()+1);
10856             cells[i].className = "x-date-prevday";
10857             setCellClass(this, cells[i]);
10858         }
10859         for(; i < days; i++){
10860             intDay = i - startingPos + 1;
10861             textEls[i].innerHTML = (intDay);
10862             d.setDate(d.getDate()+1);
10863             cells[i].className = "x-date-active";
10864             setCellClass(this, cells[i]);
10865         }
10866         var extraDays = 0;
10867         for(; i < 42; i++) {
10868              textEls[i].innerHTML = (++extraDays);
10869              d.setDate(d.getDate()+1);
10870              cells[i].className = "x-date-nextday";
10871              setCellClass(this, cells[i]);
10872         }
10873
10874         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10875         this.fireEvent('monthchange', this, date);
10876         
10877         if(!this.internalRender){
10878             var main = this.el.dom.firstChild;
10879             var w = main.offsetWidth;
10880             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10881             Roo.fly(main).setWidth(w);
10882             this.internalRender = true;
10883             // opera does not respect the auto grow header center column
10884             // then, after it gets a width opera refuses to recalculate
10885             // without a second pass
10886             if(Roo.isOpera && !this.secondPass){
10887                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10888                 this.secondPass = true;
10889                 this.update.defer(10, this, [date]);
10890             }
10891         }
10892         
10893         
10894     }
10895 });        /*
10896  * Based on:
10897  * Ext JS Library 1.1.1
10898  * Copyright(c) 2006-2007, Ext JS, LLC.
10899  *
10900  * Originally Released Under LGPL - original licence link has changed is not relivant.
10901  *
10902  * Fork - LGPL
10903  * <script type="text/javascript">
10904  */
10905 /**
10906  * @class Roo.TabPanel
10907  * @extends Roo.util.Observable
10908  * A lightweight tab container.
10909  * <br><br>
10910  * Usage:
10911  * <pre><code>
10912 // basic tabs 1, built from existing content
10913 var tabs = new Roo.TabPanel("tabs1");
10914 tabs.addTab("script", "View Script");
10915 tabs.addTab("markup", "View Markup");
10916 tabs.activate("script");
10917
10918 // more advanced tabs, built from javascript
10919 var jtabs = new Roo.TabPanel("jtabs");
10920 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10921
10922 // set up the UpdateManager
10923 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10924 var updater = tab2.getUpdateManager();
10925 updater.setDefaultUrl("ajax1.htm");
10926 tab2.on('activate', updater.refresh, updater, true);
10927
10928 // Use setUrl for Ajax loading
10929 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10930 tab3.setUrl("ajax2.htm", null, true);
10931
10932 // Disabled tab
10933 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10934 tab4.disable();
10935
10936 jtabs.activate("jtabs-1");
10937  * </code></pre>
10938  * @constructor
10939  * Create a new TabPanel.
10940  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10941  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10942  */
10943 Roo.TabPanel = function(container, config){
10944     /**
10945     * The container element for this TabPanel.
10946     * @type Roo.Element
10947     */
10948     this.el = Roo.get(container, true);
10949     if(config){
10950         if(typeof config == "boolean"){
10951             this.tabPosition = config ? "bottom" : "top";
10952         }else{
10953             Roo.apply(this, config);
10954         }
10955     }
10956     if(this.tabPosition == "bottom"){
10957         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10958         this.el.addClass("x-tabs-bottom");
10959     }
10960     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10961     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10962     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10963     if(Roo.isIE){
10964         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10965     }
10966     if(this.tabPosition != "bottom"){
10967         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10968          * @type Roo.Element
10969          */
10970         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10971         this.el.addClass("x-tabs-top");
10972     }
10973     this.items = [];
10974
10975     this.bodyEl.setStyle("position", "relative");
10976
10977     this.active = null;
10978     this.activateDelegate = this.activate.createDelegate(this);
10979
10980     this.addEvents({
10981         /**
10982          * @event tabchange
10983          * Fires when the active tab changes
10984          * @param {Roo.TabPanel} this
10985          * @param {Roo.TabPanelItem} activePanel The new active tab
10986          */
10987         "tabchange": true,
10988         /**
10989          * @event beforetabchange
10990          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10991          * @param {Roo.TabPanel} this
10992          * @param {Object} e Set cancel to true on this object to cancel the tab change
10993          * @param {Roo.TabPanelItem} tab The tab being changed to
10994          */
10995         "beforetabchange" : true
10996     });
10997
10998     Roo.EventManager.onWindowResize(this.onResize, this);
10999     this.cpad = this.el.getPadding("lr");
11000     this.hiddenCount = 0;
11001
11002
11003     // toolbar on the tabbar support...
11004     if (this.toolbar) {
11005         var tcfg = this.toolbar;
11006         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
11007         this.toolbar = new Roo.Toolbar(tcfg);
11008         if (Roo.isSafari) {
11009             var tbl = tcfg.container.child('table', true);
11010             tbl.setAttribute('width', '100%');
11011         }
11012         
11013     }
11014    
11015
11016
11017     Roo.TabPanel.superclass.constructor.call(this);
11018 };
11019
11020 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
11021     /*
11022      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
11023      */
11024     tabPosition : "top",
11025     /*
11026      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11027      */
11028     currentTabWidth : 0,
11029     /*
11030      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11031      */
11032     minTabWidth : 40,
11033     /*
11034      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11035      */
11036     maxTabWidth : 250,
11037     /*
11038      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11039      */
11040     preferredTabWidth : 175,
11041     /*
11042      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11043      */
11044     resizeTabs : false,
11045     /*
11046      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11047      */
11048     monitorResize : true,
11049     /*
11050      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
11051      */
11052     toolbar : false,
11053
11054     /**
11055      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11056      * @param {String} id The id of the div to use <b>or create</b>
11057      * @param {String} text The text for the tab
11058      * @param {String} content (optional) Content to put in the TabPanelItem body
11059      * @param {Boolean} closable (optional) True to create a close icon on the tab
11060      * @return {Roo.TabPanelItem} The created TabPanelItem
11061      */
11062     addTab : function(id, text, content, closable){
11063         var item = new Roo.TabPanelItem(this, id, text, closable);
11064         this.addTabItem(item);
11065         if(content){
11066             item.setContent(content);
11067         }
11068         return item;
11069     },
11070
11071     /**
11072      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11073      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11074      * @return {Roo.TabPanelItem}
11075      */
11076     getTab : function(id){
11077         return this.items[id];
11078     },
11079
11080     /**
11081      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11082      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11083      */
11084     hideTab : function(id){
11085         var t = this.items[id];
11086         if(!t.isHidden()){
11087            t.setHidden(true);
11088            this.hiddenCount++;
11089            this.autoSizeTabs();
11090         }
11091     },
11092
11093     /**
11094      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11095      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11096      */
11097     unhideTab : function(id){
11098         var t = this.items[id];
11099         if(t.isHidden()){
11100            t.setHidden(false);
11101            this.hiddenCount--;
11102            this.autoSizeTabs();
11103         }
11104     },
11105
11106     /**
11107      * Adds an existing {@link Roo.TabPanelItem}.
11108      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11109      */
11110     addTabItem : function(item){
11111         this.items[item.id] = item;
11112         this.items.push(item);
11113         if(this.resizeTabs){
11114            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11115            this.autoSizeTabs();
11116         }else{
11117             item.autoSize();
11118         }
11119     },
11120
11121     /**
11122      * Removes a {@link Roo.TabPanelItem}.
11123      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11124      */
11125     removeTab : function(id){
11126         var items = this.items;
11127         var tab = items[id];
11128         if(!tab) { return; }
11129         var index = items.indexOf(tab);
11130         if(this.active == tab && items.length > 1){
11131             var newTab = this.getNextAvailable(index);
11132             if(newTab) {
11133                 newTab.activate();
11134             }
11135         }
11136         this.stripEl.dom.removeChild(tab.pnode.dom);
11137         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11138             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11139         }
11140         items.splice(index, 1);
11141         delete this.items[tab.id];
11142         tab.fireEvent("close", tab);
11143         tab.purgeListeners();
11144         this.autoSizeTabs();
11145     },
11146
11147     getNextAvailable : function(start){
11148         var items = this.items;
11149         var index = start;
11150         // look for a next tab that will slide over to
11151         // replace the one being removed
11152         while(index < items.length){
11153             var item = items[++index];
11154             if(item && !item.isHidden()){
11155                 return item;
11156             }
11157         }
11158         // if one isn't found select the previous tab (on the left)
11159         index = start;
11160         while(index >= 0){
11161             var item = items[--index];
11162             if(item && !item.isHidden()){
11163                 return item;
11164             }
11165         }
11166         return null;
11167     },
11168
11169     /**
11170      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11171      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11172      */
11173     disableTab : function(id){
11174         var tab = this.items[id];
11175         if(tab && this.active != tab){
11176             tab.disable();
11177         }
11178     },
11179
11180     /**
11181      * Enables a {@link Roo.TabPanelItem} that is disabled.
11182      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11183      */
11184     enableTab : function(id){
11185         var tab = this.items[id];
11186         tab.enable();
11187     },
11188
11189     /**
11190      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11191      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11192      * @return {Roo.TabPanelItem} The TabPanelItem.
11193      */
11194     activate : function(id){
11195         var tab = this.items[id];
11196         if(!tab){
11197             return null;
11198         }
11199         if(tab == this.active || tab.disabled){
11200             return tab;
11201         }
11202         var e = {};
11203         this.fireEvent("beforetabchange", this, e, tab);
11204         if(e.cancel !== true && !tab.disabled){
11205             if(this.active){
11206                 this.active.hide();
11207             }
11208             this.active = this.items[id];
11209             this.active.show();
11210             this.fireEvent("tabchange", this, this.active);
11211         }
11212         return tab;
11213     },
11214
11215     /**
11216      * Gets the active {@link Roo.TabPanelItem}.
11217      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11218      */
11219     getActiveTab : function(){
11220         return this.active;
11221     },
11222
11223     /**
11224      * Updates the tab body element to fit the height of the container element
11225      * for overflow scrolling
11226      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11227      */
11228     syncHeight : function(targetHeight){
11229         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11230         var bm = this.bodyEl.getMargins();
11231         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11232         this.bodyEl.setHeight(newHeight);
11233         return newHeight;
11234     },
11235
11236     onResize : function(){
11237         if(this.monitorResize){
11238             this.autoSizeTabs();
11239         }
11240     },
11241
11242     /**
11243      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11244      */
11245     beginUpdate : function(){
11246         this.updating = true;
11247     },
11248
11249     /**
11250      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11251      */
11252     endUpdate : function(){
11253         this.updating = false;
11254         this.autoSizeTabs();
11255     },
11256
11257     /**
11258      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11259      */
11260     autoSizeTabs : function(){
11261         var count = this.items.length;
11262         var vcount = count - this.hiddenCount;
11263         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11264         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11265         var availWidth = Math.floor(w / vcount);
11266         var b = this.stripBody;
11267         if(b.getWidth() > w){
11268             var tabs = this.items;
11269             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11270             if(availWidth < this.minTabWidth){
11271                 /*if(!this.sleft){    // incomplete scrolling code
11272                     this.createScrollButtons();
11273                 }
11274                 this.showScroll();
11275                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11276             }
11277         }else{
11278             if(this.currentTabWidth < this.preferredTabWidth){
11279                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11280             }
11281         }
11282     },
11283
11284     /**
11285      * Returns the number of tabs in this TabPanel.
11286      * @return {Number}
11287      */
11288      getCount : function(){
11289          return this.items.length;
11290      },
11291
11292     /**
11293      * Resizes all the tabs to the passed width
11294      * @param {Number} The new width
11295      */
11296     setTabWidth : function(width){
11297         this.currentTabWidth = width;
11298         for(var i = 0, len = this.items.length; i < len; i++) {
11299                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11300         }
11301     },
11302
11303     /**
11304      * Destroys this TabPanel
11305      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11306      */
11307     destroy : function(removeEl){
11308         Roo.EventManager.removeResizeListener(this.onResize, this);
11309         for(var i = 0, len = this.items.length; i < len; i++){
11310             this.items[i].purgeListeners();
11311         }
11312         if(removeEl === true){
11313             this.el.update("");
11314             this.el.remove();
11315         }
11316     }
11317 });
11318
11319 /**
11320  * @class Roo.TabPanelItem
11321  * @extends Roo.util.Observable
11322  * Represents an individual item (tab plus body) in a TabPanel.
11323  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11324  * @param {String} id The id of this TabPanelItem
11325  * @param {String} text The text for the tab of this TabPanelItem
11326  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11327  */
11328 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11329     /**
11330      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11331      * @type Roo.TabPanel
11332      */
11333     this.tabPanel = tabPanel;
11334     /**
11335      * The id for this TabPanelItem
11336      * @type String
11337      */
11338     this.id = id;
11339     /** @private */
11340     this.disabled = false;
11341     /** @private */
11342     this.text = text;
11343     /** @private */
11344     this.loaded = false;
11345     this.closable = closable;
11346
11347     /**
11348      * The body element for this TabPanelItem.
11349      * @type Roo.Element
11350      */
11351     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11352     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11353     this.bodyEl.setStyle("display", "block");
11354     this.bodyEl.setStyle("zoom", "1");
11355     this.hideAction();
11356
11357     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11358     /** @private */
11359     this.el = Roo.get(els.el, true);
11360     this.inner = Roo.get(els.inner, true);
11361     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11362     this.pnode = Roo.get(els.el.parentNode, true);
11363     this.el.on("mousedown", this.onTabMouseDown, this);
11364     this.el.on("click", this.onTabClick, this);
11365     /** @private */
11366     if(closable){
11367         var c = Roo.get(els.close, true);
11368         c.dom.title = this.closeText;
11369         c.addClassOnOver("close-over");
11370         c.on("click", this.closeClick, this);
11371      }
11372
11373     this.addEvents({
11374          /**
11375          * @event activate
11376          * Fires when this tab becomes the active tab.
11377          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11378          * @param {Roo.TabPanelItem} this
11379          */
11380         "activate": true,
11381         /**
11382          * @event beforeclose
11383          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11384          * @param {Roo.TabPanelItem} this
11385          * @param {Object} e Set cancel to true on this object to cancel the close.
11386          */
11387         "beforeclose": true,
11388         /**
11389          * @event close
11390          * Fires when this tab is closed.
11391          * @param {Roo.TabPanelItem} this
11392          */
11393          "close": true,
11394         /**
11395          * @event deactivate
11396          * Fires when this tab is no longer the active tab.
11397          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11398          * @param {Roo.TabPanelItem} this
11399          */
11400          "deactivate" : true
11401     });
11402     this.hidden = false;
11403
11404     Roo.TabPanelItem.superclass.constructor.call(this);
11405 };
11406
11407 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11408     purgeListeners : function(){
11409        Roo.util.Observable.prototype.purgeListeners.call(this);
11410        this.el.removeAllListeners();
11411     },
11412     /**
11413      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11414      */
11415     show : function(){
11416         this.pnode.addClass("on");
11417         this.showAction();
11418         if(Roo.isOpera){
11419             this.tabPanel.stripWrap.repaint();
11420         }
11421         this.fireEvent("activate", this.tabPanel, this);
11422     },
11423
11424     /**
11425      * Returns true if this tab is the active tab.
11426      * @return {Boolean}
11427      */
11428     isActive : function(){
11429         return this.tabPanel.getActiveTab() == this;
11430     },
11431
11432     /**
11433      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11434      */
11435     hide : function(){
11436         this.pnode.removeClass("on");
11437         this.hideAction();
11438         this.fireEvent("deactivate", this.tabPanel, this);
11439     },
11440
11441     hideAction : function(){
11442         this.bodyEl.hide();
11443         this.bodyEl.setStyle("position", "absolute");
11444         this.bodyEl.setLeft("-20000px");
11445         this.bodyEl.setTop("-20000px");
11446     },
11447
11448     showAction : function(){
11449         this.bodyEl.setStyle("position", "relative");
11450         this.bodyEl.setTop("");
11451         this.bodyEl.setLeft("");
11452         this.bodyEl.show();
11453     },
11454
11455     /**
11456      * Set the tooltip for the tab.
11457      * @param {String} tooltip The tab's tooltip
11458      */
11459     setTooltip : function(text){
11460         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11461             this.textEl.dom.qtip = text;
11462             this.textEl.dom.removeAttribute('title');
11463         }else{
11464             this.textEl.dom.title = text;
11465         }
11466     },
11467
11468     onTabClick : function(e){
11469         e.preventDefault();
11470         this.tabPanel.activate(this.id);
11471     },
11472
11473     onTabMouseDown : function(e){
11474         e.preventDefault();
11475         this.tabPanel.activate(this.id);
11476     },
11477
11478     getWidth : function(){
11479         return this.inner.getWidth();
11480     },
11481
11482     setWidth : function(width){
11483         var iwidth = width - this.pnode.getPadding("lr");
11484         this.inner.setWidth(iwidth);
11485         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11486         this.pnode.setWidth(width);
11487     },
11488
11489     /**
11490      * Show or hide the tab
11491      * @param {Boolean} hidden True to hide or false to show.
11492      */
11493     setHidden : function(hidden){
11494         this.hidden = hidden;
11495         this.pnode.setStyle("display", hidden ? "none" : "");
11496     },
11497
11498     /**
11499      * Returns true if this tab is "hidden"
11500      * @return {Boolean}
11501      */
11502     isHidden : function(){
11503         return this.hidden;
11504     },
11505
11506     /**
11507      * Returns the text for this tab
11508      * @return {String}
11509      */
11510     getText : function(){
11511         return this.text;
11512     },
11513
11514     autoSize : function(){
11515         //this.el.beginMeasure();
11516         this.textEl.setWidth(1);
11517         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11518         //this.el.endMeasure();
11519     },
11520
11521     /**
11522      * Sets the text for the tab (Note: this also sets the tooltip text)
11523      * @param {String} text The tab's text and tooltip
11524      */
11525     setText : function(text){
11526         this.text = text;
11527         this.textEl.update(text);
11528         this.setTooltip(text);
11529         if(!this.tabPanel.resizeTabs){
11530             this.autoSize();
11531         }
11532     },
11533     /**
11534      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11535      */
11536     activate : function(){
11537         this.tabPanel.activate(this.id);
11538     },
11539
11540     /**
11541      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11542      */
11543     disable : function(){
11544         if(this.tabPanel.active != this){
11545             this.disabled = true;
11546             this.pnode.addClass("disabled");
11547         }
11548     },
11549
11550     /**
11551      * Enables this TabPanelItem if it was previously disabled.
11552      */
11553     enable : function(){
11554         this.disabled = false;
11555         this.pnode.removeClass("disabled");
11556     },
11557
11558     /**
11559      * Sets the content for this TabPanelItem.
11560      * @param {String} content The content
11561      * @param {Boolean} loadScripts true to look for and load scripts
11562      */
11563     setContent : function(content, loadScripts){
11564         this.bodyEl.update(content, loadScripts);
11565     },
11566
11567     /**
11568      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11569      * @return {Roo.UpdateManager} The UpdateManager
11570      */
11571     getUpdateManager : function(){
11572         return this.bodyEl.getUpdateManager();
11573     },
11574
11575     /**
11576      * Set a URL to be used to load the content for this TabPanelItem.
11577      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11578      * @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)
11579      * @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)
11580      * @return {Roo.UpdateManager} The UpdateManager
11581      */
11582     setUrl : function(url, params, loadOnce){
11583         if(this.refreshDelegate){
11584             this.un('activate', this.refreshDelegate);
11585         }
11586         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11587         this.on("activate", this.refreshDelegate);
11588         return this.bodyEl.getUpdateManager();
11589     },
11590
11591     /** @private */
11592     _handleRefresh : function(url, params, loadOnce){
11593         if(!loadOnce || !this.loaded){
11594             var updater = this.bodyEl.getUpdateManager();
11595             updater.update(url, params, this._setLoaded.createDelegate(this));
11596         }
11597     },
11598
11599     /**
11600      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11601      *   Will fail silently if the setUrl method has not been called.
11602      *   This does not activate the panel, just updates its content.
11603      */
11604     refresh : function(){
11605         if(this.refreshDelegate){
11606            this.loaded = false;
11607            this.refreshDelegate();
11608         }
11609     },
11610
11611     /** @private */
11612     _setLoaded : function(){
11613         this.loaded = true;
11614     },
11615
11616     /** @private */
11617     closeClick : function(e){
11618         var o = {};
11619         e.stopEvent();
11620         this.fireEvent("beforeclose", this, o);
11621         if(o.cancel !== true){
11622             this.tabPanel.removeTab(this.id);
11623         }
11624     },
11625     /**
11626      * The text displayed in the tooltip for the close icon.
11627      * @type String
11628      */
11629     closeText : "Close this tab"
11630 });
11631
11632 /** @private */
11633 Roo.TabPanel.prototype.createStrip = function(container){
11634     var strip = document.createElement("div");
11635     strip.className = "x-tabs-wrap";
11636     container.appendChild(strip);
11637     return strip;
11638 };
11639 /** @private */
11640 Roo.TabPanel.prototype.createStripList = function(strip){
11641     // div wrapper for retard IE
11642     // returns the "tr" element.
11643     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11644         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11645         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11646     return strip.firstChild.firstChild.firstChild.firstChild;
11647 };
11648 /** @private */
11649 Roo.TabPanel.prototype.createBody = function(container){
11650     var body = document.createElement("div");
11651     Roo.id(body, "tab-body");
11652     Roo.fly(body).addClass("x-tabs-body");
11653     container.appendChild(body);
11654     return body;
11655 };
11656 /** @private */
11657 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11658     var body = Roo.getDom(id);
11659     if(!body){
11660         body = document.createElement("div");
11661         body.id = id;
11662     }
11663     Roo.fly(body).addClass("x-tabs-item-body");
11664     bodyEl.insertBefore(body, bodyEl.firstChild);
11665     return body;
11666 };
11667 /** @private */
11668 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11669     var td = document.createElement("td");
11670     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11671     //stripEl.appendChild(td);
11672     if(closable){
11673         td.className = "x-tabs-closable";
11674         if(!this.closeTpl){
11675             this.closeTpl = new Roo.Template(
11676                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11677                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11678                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11679             );
11680         }
11681         var el = this.closeTpl.overwrite(td, {"text": text});
11682         var close = el.getElementsByTagName("div")[0];
11683         var inner = el.getElementsByTagName("em")[0];
11684         return {"el": el, "close": close, "inner": inner};
11685     } else {
11686         if(!this.tabTpl){
11687             this.tabTpl = new Roo.Template(
11688                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11689                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11690             );
11691         }
11692         var el = this.tabTpl.overwrite(td, {"text": text});
11693         var inner = el.getElementsByTagName("em")[0];
11694         return {"el": el, "inner": inner};
11695     }
11696 };/*
11697  * Based on:
11698  * Ext JS Library 1.1.1
11699  * Copyright(c) 2006-2007, Ext JS, LLC.
11700  *
11701  * Originally Released Under LGPL - original licence link has changed is not relivant.
11702  *
11703  * Fork - LGPL
11704  * <script type="text/javascript">
11705  */
11706
11707 /**
11708  * @class Roo.Button
11709  * @extends Roo.util.Observable
11710  * Simple Button class
11711  * @cfg {String} text The button text
11712  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11713  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11714  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11715  * @cfg {Object} scope The scope of the handler
11716  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11717  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11718  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11719  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11720  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11721  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11722    applies if enableToggle = true)
11723  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11724  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11725   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11726  * @constructor
11727  * Create a new button
11728  * @param {Object} config The config object
11729  */
11730 Roo.Button = function(renderTo, config)
11731 {
11732     if (!config) {
11733         config = renderTo;
11734         renderTo = config.renderTo || false;
11735     }
11736     
11737     Roo.apply(this, config);
11738     this.addEvents({
11739         /**
11740              * @event click
11741              * Fires when this button is clicked
11742              * @param {Button} this
11743              * @param {EventObject} e The click event
11744              */
11745             "click" : true,
11746         /**
11747              * @event toggle
11748              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11749              * @param {Button} this
11750              * @param {Boolean} pressed
11751              */
11752             "toggle" : true,
11753         /**
11754              * @event mouseover
11755              * Fires when the mouse hovers over the button
11756              * @param {Button} this
11757              * @param {Event} e The event object
11758              */
11759         'mouseover' : true,
11760         /**
11761              * @event mouseout
11762              * Fires when the mouse exits the button
11763              * @param {Button} this
11764              * @param {Event} e The event object
11765              */
11766         'mouseout': true,
11767          /**
11768              * @event render
11769              * Fires when the button is rendered
11770              * @param {Button} this
11771              */
11772         'render': true
11773     });
11774     if(this.menu){
11775         this.menu = Roo.menu.MenuMgr.get(this.menu);
11776     }
11777     // register listeners first!!  - so render can be captured..
11778     Roo.util.Observable.call(this);
11779     if(renderTo){
11780         this.render(renderTo);
11781     }
11782     
11783   
11784 };
11785
11786 Roo.extend(Roo.Button, Roo.util.Observable, {
11787     /**
11788      * 
11789      */
11790     
11791     /**
11792      * Read-only. True if this button is hidden
11793      * @type Boolean
11794      */
11795     hidden : false,
11796     /**
11797      * Read-only. True if this button is disabled
11798      * @type Boolean
11799      */
11800     disabled : false,
11801     /**
11802      * Read-only. True if this button is pressed (only if enableToggle = true)
11803      * @type Boolean
11804      */
11805     pressed : false,
11806
11807     /**
11808      * @cfg {Number} tabIndex 
11809      * The DOM tabIndex for this button (defaults to undefined)
11810      */
11811     tabIndex : undefined,
11812
11813     /**
11814      * @cfg {Boolean} enableToggle
11815      * True to enable pressed/not pressed toggling (defaults to false)
11816      */
11817     enableToggle: false,
11818     /**
11819      * @cfg {Mixed} menu
11820      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11821      */
11822     menu : undefined,
11823     /**
11824      * @cfg {String} menuAlign
11825      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11826      */
11827     menuAlign : "tl-bl?",
11828
11829     /**
11830      * @cfg {String} iconCls
11831      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11832      */
11833     iconCls : undefined,
11834     /**
11835      * @cfg {String} type
11836      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11837      */
11838     type : 'button',
11839
11840     // private
11841     menuClassTarget: 'tr',
11842
11843     /**
11844      * @cfg {String} clickEvent
11845      * The type of event to map to the button's event handler (defaults to 'click')
11846      */
11847     clickEvent : 'click',
11848
11849     /**
11850      * @cfg {Boolean} handleMouseEvents
11851      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11852      */
11853     handleMouseEvents : true,
11854
11855     /**
11856      * @cfg {String} tooltipType
11857      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11858      */
11859     tooltipType : 'qtip',
11860
11861     /**
11862      * @cfg {String} cls
11863      * A CSS class to apply to the button's main element.
11864      */
11865     
11866     /**
11867      * @cfg {Roo.Template} template (Optional)
11868      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11869      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11870      * require code modifications if required elements (e.g. a button) aren't present.
11871      */
11872
11873     // private
11874     render : function(renderTo){
11875         var btn;
11876         if(this.hideParent){
11877             this.parentEl = Roo.get(renderTo);
11878         }
11879         if(!this.dhconfig){
11880             if(!this.template){
11881                 if(!Roo.Button.buttonTemplate){
11882                     // hideous table template
11883                     Roo.Button.buttonTemplate = new Roo.Template(
11884                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11885                         '<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>',
11886                         "</tr></tbody></table>");
11887                 }
11888                 this.template = Roo.Button.buttonTemplate;
11889             }
11890             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11891             var btnEl = btn.child("button:first");
11892             btnEl.on('focus', this.onFocus, this);
11893             btnEl.on('blur', this.onBlur, this);
11894             if(this.cls){
11895                 btn.addClass(this.cls);
11896             }
11897             if(this.icon){
11898                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11899             }
11900             if(this.iconCls){
11901                 btnEl.addClass(this.iconCls);
11902                 if(!this.cls){
11903                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11904                 }
11905             }
11906             if(this.tabIndex !== undefined){
11907                 btnEl.dom.tabIndex = this.tabIndex;
11908             }
11909             if(this.tooltip){
11910                 if(typeof this.tooltip == 'object'){
11911                     Roo.QuickTips.tips(Roo.apply({
11912                           target: btnEl.id
11913                     }, this.tooltip));
11914                 } else {
11915                     btnEl.dom[this.tooltipType] = this.tooltip;
11916                 }
11917             }
11918         }else{
11919             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11920         }
11921         this.el = btn;
11922         if(this.id){
11923             this.el.dom.id = this.el.id = this.id;
11924         }
11925         if(this.menu){
11926             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11927             this.menu.on("show", this.onMenuShow, this);
11928             this.menu.on("hide", this.onMenuHide, this);
11929         }
11930         btn.addClass("x-btn");
11931         if(Roo.isIE && !Roo.isIE7){
11932             this.autoWidth.defer(1, this);
11933         }else{
11934             this.autoWidth();
11935         }
11936         if(this.handleMouseEvents){
11937             btn.on("mouseover", this.onMouseOver, this);
11938             btn.on("mouseout", this.onMouseOut, this);
11939             btn.on("mousedown", this.onMouseDown, this);
11940         }
11941         btn.on(this.clickEvent, this.onClick, this);
11942         //btn.on("mouseup", this.onMouseUp, this);
11943         if(this.hidden){
11944             this.hide();
11945         }
11946         if(this.disabled){
11947             this.disable();
11948         }
11949         Roo.ButtonToggleMgr.register(this);
11950         if(this.pressed){
11951             this.el.addClass("x-btn-pressed");
11952         }
11953         if(this.repeat){
11954             var repeater = new Roo.util.ClickRepeater(btn,
11955                 typeof this.repeat == "object" ? this.repeat : {}
11956             );
11957             repeater.on("click", this.onClick,  this);
11958         }
11959         
11960         this.fireEvent('render', this);
11961         
11962     },
11963     /**
11964      * Returns the button's underlying element
11965      * @return {Roo.Element} The element
11966      */
11967     getEl : function(){
11968         return this.el;  
11969     },
11970     
11971     /**
11972      * Destroys this Button and removes any listeners.
11973      */
11974     destroy : function(){
11975         Roo.ButtonToggleMgr.unregister(this);
11976         this.el.removeAllListeners();
11977         this.purgeListeners();
11978         this.el.remove();
11979     },
11980
11981     // private
11982     autoWidth : function(){
11983         if(this.el){
11984             this.el.setWidth("auto");
11985             if(Roo.isIE7 && Roo.isStrict){
11986                 var ib = this.el.child('button');
11987                 if(ib && ib.getWidth() > 20){
11988                     ib.clip();
11989                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11990                 }
11991             }
11992             if(this.minWidth){
11993                 if(this.hidden){
11994                     this.el.beginMeasure();
11995                 }
11996                 if(this.el.getWidth() < this.minWidth){
11997                     this.el.setWidth(this.minWidth);
11998                 }
11999                 if(this.hidden){
12000                     this.el.endMeasure();
12001                 }
12002             }
12003         }
12004     },
12005
12006     /**
12007      * Assigns this button's click handler
12008      * @param {Function} handler The function to call when the button is clicked
12009      * @param {Object} scope (optional) Scope for the function passed in
12010      */
12011     setHandler : function(handler, scope){
12012         this.handler = handler;
12013         this.scope = scope;  
12014     },
12015     
12016     /**
12017      * Sets this button's text
12018      * @param {String} text The button text
12019      */
12020     setText : function(text){
12021         this.text = text;
12022         if(this.el){
12023             this.el.child("td.x-btn-center button.x-btn-text").update(text);
12024         }
12025         this.autoWidth();
12026     },
12027     
12028     /**
12029      * Gets the text for this button
12030      * @return {String} The button text
12031      */
12032     getText : function(){
12033         return this.text;  
12034     },
12035     
12036     /**
12037      * Show this button
12038      */
12039     show: function(){
12040         this.hidden = false;
12041         if(this.el){
12042             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12043         }
12044     },
12045     
12046     /**
12047      * Hide this button
12048      */
12049     hide: function(){
12050         this.hidden = true;
12051         if(this.el){
12052             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12053         }
12054     },
12055     
12056     /**
12057      * Convenience function for boolean show/hide
12058      * @param {Boolean} visible True to show, false to hide
12059      */
12060     setVisible: function(visible){
12061         if(visible) {
12062             this.show();
12063         }else{
12064             this.hide();
12065         }
12066     },
12067     
12068     /**
12069      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12070      * @param {Boolean} state (optional) Force a particular state
12071      */
12072     toggle : function(state){
12073         state = state === undefined ? !this.pressed : state;
12074         if(state != this.pressed){
12075             if(state){
12076                 this.el.addClass("x-btn-pressed");
12077                 this.pressed = true;
12078                 this.fireEvent("toggle", this, true);
12079             }else{
12080                 this.el.removeClass("x-btn-pressed");
12081                 this.pressed = false;
12082                 this.fireEvent("toggle", this, false);
12083             }
12084             if(this.toggleHandler){
12085                 this.toggleHandler.call(this.scope || this, this, state);
12086             }
12087         }
12088     },
12089     
12090     /**
12091      * Focus the button
12092      */
12093     focus : function(){
12094         this.el.child('button:first').focus();
12095     },
12096     
12097     /**
12098      * Disable this button
12099      */
12100     disable : function(){
12101         if(this.el){
12102             this.el.addClass("x-btn-disabled");
12103         }
12104         this.disabled = true;
12105     },
12106     
12107     /**
12108      * Enable this button
12109      */
12110     enable : function(){
12111         if(this.el){
12112             this.el.removeClass("x-btn-disabled");
12113         }
12114         this.disabled = false;
12115     },
12116
12117     /**
12118      * Convenience function for boolean enable/disable
12119      * @param {Boolean} enabled True to enable, false to disable
12120      */
12121     setDisabled : function(v){
12122         this[v !== true ? "enable" : "disable"]();
12123     },
12124
12125     // private
12126     onClick : function(e){
12127         if(e){
12128             e.preventDefault();
12129         }
12130         if(e.button != 0){
12131             return;
12132         }
12133         if(!this.disabled){
12134             if(this.enableToggle){
12135                 this.toggle();
12136             }
12137             if(this.menu && !this.menu.isVisible()){
12138                 this.menu.show(this.el, this.menuAlign);
12139             }
12140             this.fireEvent("click", this, e);
12141             if(this.handler){
12142                 this.el.removeClass("x-btn-over");
12143                 this.handler.call(this.scope || this, this, e);
12144             }
12145         }
12146     },
12147     // private
12148     onMouseOver : function(e){
12149         if(!this.disabled){
12150             this.el.addClass("x-btn-over");
12151             this.fireEvent('mouseover', this, e);
12152         }
12153     },
12154     // private
12155     onMouseOut : function(e){
12156         if(!e.within(this.el,  true)){
12157             this.el.removeClass("x-btn-over");
12158             this.fireEvent('mouseout', this, e);
12159         }
12160     },
12161     // private
12162     onFocus : function(e){
12163         if(!this.disabled){
12164             this.el.addClass("x-btn-focus");
12165         }
12166     },
12167     // private
12168     onBlur : function(e){
12169         this.el.removeClass("x-btn-focus");
12170     },
12171     // private
12172     onMouseDown : function(e){
12173         if(!this.disabled && e.button == 0){
12174             this.el.addClass("x-btn-click");
12175             Roo.get(document).on('mouseup', this.onMouseUp, this);
12176         }
12177     },
12178     // private
12179     onMouseUp : function(e){
12180         if(e.button == 0){
12181             this.el.removeClass("x-btn-click");
12182             Roo.get(document).un('mouseup', this.onMouseUp, this);
12183         }
12184     },
12185     // private
12186     onMenuShow : function(e){
12187         this.el.addClass("x-btn-menu-active");
12188     },
12189     // private
12190     onMenuHide : function(e){
12191         this.el.removeClass("x-btn-menu-active");
12192     }   
12193 });
12194
12195 // Private utility class used by Button
12196 Roo.ButtonToggleMgr = function(){
12197    var groups = {};
12198    
12199    function toggleGroup(btn, state){
12200        if(state){
12201            var g = groups[btn.toggleGroup];
12202            for(var i = 0, l = g.length; i < l; i++){
12203                if(g[i] != btn){
12204                    g[i].toggle(false);
12205                }
12206            }
12207        }
12208    }
12209    
12210    return {
12211        register : function(btn){
12212            if(!btn.toggleGroup){
12213                return;
12214            }
12215            var g = groups[btn.toggleGroup];
12216            if(!g){
12217                g = groups[btn.toggleGroup] = [];
12218            }
12219            g.push(btn);
12220            btn.on("toggle", toggleGroup);
12221        },
12222        
12223        unregister : function(btn){
12224            if(!btn.toggleGroup){
12225                return;
12226            }
12227            var g = groups[btn.toggleGroup];
12228            if(g){
12229                g.remove(btn);
12230                btn.un("toggle", toggleGroup);
12231            }
12232        }
12233    };
12234 }();/*
12235  * Based on:
12236  * Ext JS Library 1.1.1
12237  * Copyright(c) 2006-2007, Ext JS, LLC.
12238  *
12239  * Originally Released Under LGPL - original licence link has changed is not relivant.
12240  *
12241  * Fork - LGPL
12242  * <script type="text/javascript">
12243  */
12244  
12245 /**
12246  * @class Roo.SplitButton
12247  * @extends Roo.Button
12248  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12249  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12250  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12251  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12252  * @cfg {String} arrowTooltip The title attribute of the arrow
12253  * @constructor
12254  * Create a new menu button
12255  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12256  * @param {Object} config The config object
12257  */
12258 Roo.SplitButton = function(renderTo, config){
12259     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12260     /**
12261      * @event arrowclick
12262      * Fires when this button's arrow is clicked
12263      * @param {SplitButton} this
12264      * @param {EventObject} e The click event
12265      */
12266     this.addEvents({"arrowclick":true});
12267 };
12268
12269 Roo.extend(Roo.SplitButton, Roo.Button, {
12270     render : function(renderTo){
12271         // this is one sweet looking template!
12272         var tpl = new Roo.Template(
12273             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12274             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12275             '<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>',
12276             "</tbody></table></td><td>",
12277             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12278             '<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>',
12279             "</tbody></table></td></tr></table>"
12280         );
12281         var btn = tpl.append(renderTo, [this.text, this.type], true);
12282         var btnEl = btn.child("button");
12283         if(this.cls){
12284             btn.addClass(this.cls);
12285         }
12286         if(this.icon){
12287             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12288         }
12289         if(this.iconCls){
12290             btnEl.addClass(this.iconCls);
12291             if(!this.cls){
12292                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12293             }
12294         }
12295         this.el = btn;
12296         if(this.handleMouseEvents){
12297             btn.on("mouseover", this.onMouseOver, this);
12298             btn.on("mouseout", this.onMouseOut, this);
12299             btn.on("mousedown", this.onMouseDown, this);
12300             btn.on("mouseup", this.onMouseUp, this);
12301         }
12302         btn.on(this.clickEvent, this.onClick, this);
12303         if(this.tooltip){
12304             if(typeof this.tooltip == 'object'){
12305                 Roo.QuickTips.tips(Roo.apply({
12306                       target: btnEl.id
12307                 }, this.tooltip));
12308             } else {
12309                 btnEl.dom[this.tooltipType] = this.tooltip;
12310             }
12311         }
12312         if(this.arrowTooltip){
12313             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12314         }
12315         if(this.hidden){
12316             this.hide();
12317         }
12318         if(this.disabled){
12319             this.disable();
12320         }
12321         if(this.pressed){
12322             this.el.addClass("x-btn-pressed");
12323         }
12324         if(Roo.isIE && !Roo.isIE7){
12325             this.autoWidth.defer(1, this);
12326         }else{
12327             this.autoWidth();
12328         }
12329         if(this.menu){
12330             this.menu.on("show", this.onMenuShow, this);
12331             this.menu.on("hide", this.onMenuHide, this);
12332         }
12333         this.fireEvent('render', this);
12334     },
12335
12336     // private
12337     autoWidth : function(){
12338         if(this.el){
12339             var tbl = this.el.child("table:first");
12340             var tbl2 = this.el.child("table:last");
12341             this.el.setWidth("auto");
12342             tbl.setWidth("auto");
12343             if(Roo.isIE7 && Roo.isStrict){
12344                 var ib = this.el.child('button:first');
12345                 if(ib && ib.getWidth() > 20){
12346                     ib.clip();
12347                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12348                 }
12349             }
12350             if(this.minWidth){
12351                 if(this.hidden){
12352                     this.el.beginMeasure();
12353                 }
12354                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12355                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12356                 }
12357                 if(this.hidden){
12358                     this.el.endMeasure();
12359                 }
12360             }
12361             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12362         } 
12363     },
12364     /**
12365      * Sets this button's click handler
12366      * @param {Function} handler The function to call when the button is clicked
12367      * @param {Object} scope (optional) Scope for the function passed above
12368      */
12369     setHandler : function(handler, scope){
12370         this.handler = handler;
12371         this.scope = scope;  
12372     },
12373     
12374     /**
12375      * Sets this button's arrow click handler
12376      * @param {Function} handler The function to call when the arrow is clicked
12377      * @param {Object} scope (optional) Scope for the function passed above
12378      */
12379     setArrowHandler : function(handler, scope){
12380         this.arrowHandler = handler;
12381         this.scope = scope;  
12382     },
12383     
12384     /**
12385      * Focus the button
12386      */
12387     focus : function(){
12388         if(this.el){
12389             this.el.child("button:first").focus();
12390         }
12391     },
12392
12393     // private
12394     onClick : function(e){
12395         e.preventDefault();
12396         if(!this.disabled){
12397             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12398                 if(this.menu && !this.menu.isVisible()){
12399                     this.menu.show(this.el, this.menuAlign);
12400                 }
12401                 this.fireEvent("arrowclick", this, e);
12402                 if(this.arrowHandler){
12403                     this.arrowHandler.call(this.scope || this, this, e);
12404                 }
12405             }else{
12406                 this.fireEvent("click", this, e);
12407                 if(this.handler){
12408                     this.handler.call(this.scope || this, this, e);
12409                 }
12410             }
12411         }
12412     },
12413     // private
12414     onMouseDown : function(e){
12415         if(!this.disabled){
12416             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12417         }
12418     },
12419     // private
12420     onMouseUp : function(e){
12421         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12422     }   
12423 });
12424
12425
12426 // backwards compat
12427 Roo.MenuButton = Roo.SplitButton;/*
12428  * Based on:
12429  * Ext JS Library 1.1.1
12430  * Copyright(c) 2006-2007, Ext JS, LLC.
12431  *
12432  * Originally Released Under LGPL - original licence link has changed is not relivant.
12433  *
12434  * Fork - LGPL
12435  * <script type="text/javascript">
12436  */
12437
12438 /**
12439  * @class Roo.Toolbar
12440  * Basic Toolbar class.
12441  * @constructor
12442  * Creates a new Toolbar
12443  * @param {Object} container The config object
12444  */ 
12445 Roo.Toolbar = function(container, buttons, config)
12446 {
12447     /// old consturctor format still supported..
12448     if(container instanceof Array){ // omit the container for later rendering
12449         buttons = container;
12450         config = buttons;
12451         container = null;
12452     }
12453     if (typeof(container) == 'object' && container.xtype) {
12454         config = container;
12455         container = config.container;
12456         buttons = config.buttons || []; // not really - use items!!
12457     }
12458     var xitems = [];
12459     if (config && config.items) {
12460         xitems = config.items;
12461         delete config.items;
12462     }
12463     Roo.apply(this, config);
12464     this.buttons = buttons;
12465     
12466     if(container){
12467         this.render(container);
12468     }
12469     this.xitems = xitems;
12470     Roo.each(xitems, function(b) {
12471         this.add(b);
12472     }, this);
12473     
12474 };
12475
12476 Roo.Toolbar.prototype = {
12477     /**
12478      * @cfg {Array} items
12479      * array of button configs or elements to add (will be converted to a MixedCollection)
12480      */
12481     
12482     /**
12483      * @cfg {String/HTMLElement/Element} container
12484      * The id or element that will contain the toolbar
12485      */
12486     // private
12487     render : function(ct){
12488         this.el = Roo.get(ct);
12489         if(this.cls){
12490             this.el.addClass(this.cls);
12491         }
12492         // using a table allows for vertical alignment
12493         // 100% width is needed by Safari...
12494         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12495         this.tr = this.el.child("tr", true);
12496         var autoId = 0;
12497         this.items = new Roo.util.MixedCollection(false, function(o){
12498             return o.id || ("item" + (++autoId));
12499         });
12500         if(this.buttons){
12501             this.add.apply(this, this.buttons);
12502             delete this.buttons;
12503         }
12504     },
12505
12506     /**
12507      * Adds element(s) to the toolbar -- this function takes a variable number of 
12508      * arguments of mixed type and adds them to the toolbar.
12509      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12510      * <ul>
12511      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12512      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12513      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12514      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12515      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12516      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12517      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12518      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12519      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12520      * </ul>
12521      * @param {Mixed} arg2
12522      * @param {Mixed} etc.
12523      */
12524     add : function(){
12525         var a = arguments, l = a.length;
12526         for(var i = 0; i < l; i++){
12527             this._add(a[i]);
12528         }
12529     },
12530     // private..
12531     _add : function(el) {
12532         
12533         if (el.xtype) {
12534             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12535         }
12536         
12537         if (el.applyTo){ // some kind of form field
12538             return this.addField(el);
12539         } 
12540         if (el.render){ // some kind of Toolbar.Item
12541             return this.addItem(el);
12542         }
12543         if (typeof el == "string"){ // string
12544             if(el == "separator" || el == "-"){
12545                 return this.addSeparator();
12546             }
12547             if (el == " "){
12548                 return this.addSpacer();
12549             }
12550             if(el == "->"){
12551                 return this.addFill();
12552             }
12553             return this.addText(el);
12554             
12555         }
12556         if(el.tagName){ // element
12557             return this.addElement(el);
12558         }
12559         if(typeof el == "object"){ // must be button config?
12560             return this.addButton(el);
12561         }
12562         // and now what?!?!
12563         return false;
12564         
12565     },
12566     
12567     /**
12568      * Add an Xtype element
12569      * @param {Object} xtype Xtype Object
12570      * @return {Object} created Object
12571      */
12572     addxtype : function(e){
12573         return this.add(e);  
12574     },
12575     
12576     /**
12577      * Returns the Element for this toolbar.
12578      * @return {Roo.Element}
12579      */
12580     getEl : function(){
12581         return this.el;  
12582     },
12583     
12584     /**
12585      * Adds a separator
12586      * @return {Roo.Toolbar.Item} The separator item
12587      */
12588     addSeparator : function(){
12589         return this.addItem(new Roo.Toolbar.Separator());
12590     },
12591
12592     /**
12593      * Adds a spacer element
12594      * @return {Roo.Toolbar.Spacer} The spacer item
12595      */
12596     addSpacer : function(){
12597         return this.addItem(new Roo.Toolbar.Spacer());
12598     },
12599
12600     /**
12601      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12602      * @return {Roo.Toolbar.Fill} The fill item
12603      */
12604     addFill : function(){
12605         return this.addItem(new Roo.Toolbar.Fill());
12606     },
12607
12608     /**
12609      * Adds any standard HTML element to the toolbar
12610      * @param {String/HTMLElement/Element} el The element or id of the element to add
12611      * @return {Roo.Toolbar.Item} The element's item
12612      */
12613     addElement : function(el){
12614         return this.addItem(new Roo.Toolbar.Item(el));
12615     },
12616     /**
12617      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12618      * @type Roo.util.MixedCollection  
12619      */
12620     items : false,
12621      
12622     /**
12623      * Adds any Toolbar.Item or subclass
12624      * @param {Roo.Toolbar.Item} item
12625      * @return {Roo.Toolbar.Item} The item
12626      */
12627     addItem : function(item){
12628         var td = this.nextBlock();
12629         item.render(td);
12630         this.items.add(item);
12631         return item;
12632     },
12633     
12634     /**
12635      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12636      * @param {Object/Array} config A button config or array of configs
12637      * @return {Roo.Toolbar.Button/Array}
12638      */
12639     addButton : function(config){
12640         if(config instanceof Array){
12641             var buttons = [];
12642             for(var i = 0, len = config.length; i < len; i++) {
12643                 buttons.push(this.addButton(config[i]));
12644             }
12645             return buttons;
12646         }
12647         var b = config;
12648         if(!(config instanceof Roo.Toolbar.Button)){
12649             b = config.split ?
12650                 new Roo.Toolbar.SplitButton(config) :
12651                 new Roo.Toolbar.Button(config);
12652         }
12653         var td = this.nextBlock();
12654         b.render(td);
12655         this.items.add(b);
12656         return b;
12657     },
12658     
12659     /**
12660      * Adds text to the toolbar
12661      * @param {String} text The text to add
12662      * @return {Roo.Toolbar.Item} The element's item
12663      */
12664     addText : function(text){
12665         return this.addItem(new Roo.Toolbar.TextItem(text));
12666     },
12667     
12668     /**
12669      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12670      * @param {Number} index The index where the item is to be inserted
12671      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12672      * @return {Roo.Toolbar.Button/Item}
12673      */
12674     insertButton : function(index, item){
12675         if(item instanceof Array){
12676             var buttons = [];
12677             for(var i = 0, len = item.length; i < len; i++) {
12678                buttons.push(this.insertButton(index + i, item[i]));
12679             }
12680             return buttons;
12681         }
12682         if (!(item instanceof Roo.Toolbar.Button)){
12683            item = new Roo.Toolbar.Button(item);
12684         }
12685         var td = document.createElement("td");
12686         this.tr.insertBefore(td, this.tr.childNodes[index]);
12687         item.render(td);
12688         this.items.insert(index, item);
12689         return item;
12690     },
12691     
12692     /**
12693      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12694      * @param {Object} config
12695      * @return {Roo.Toolbar.Item} The element's item
12696      */
12697     addDom : function(config, returnEl){
12698         var td = this.nextBlock();
12699         Roo.DomHelper.overwrite(td, config);
12700         var ti = new Roo.Toolbar.Item(td.firstChild);
12701         ti.render(td);
12702         this.items.add(ti);
12703         return ti;
12704     },
12705
12706     /**
12707      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12708      * @type Roo.util.MixedCollection  
12709      */
12710     fields : false,
12711     
12712     /**
12713      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12714      * Note: the field should not have been rendered yet. For a field that has already been
12715      * rendered, use {@link #addElement}.
12716      * @param {Roo.form.Field} field
12717      * @return {Roo.ToolbarItem}
12718      */
12719      
12720       
12721     addField : function(field) {
12722         if (!this.fields) {
12723             var autoId = 0;
12724             this.fields = new Roo.util.MixedCollection(false, function(o){
12725                 return o.id || ("item" + (++autoId));
12726             });
12727
12728         }
12729         
12730         var td = this.nextBlock();
12731         field.render(td);
12732         var ti = new Roo.Toolbar.Item(td.firstChild);
12733         ti.render(td);
12734         this.items.add(ti);
12735         this.fields.add(field);
12736         return ti;
12737     },
12738     /**
12739      * Hide the toolbar
12740      * @method hide
12741      */
12742      
12743       
12744     hide : function()
12745     {
12746         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12747         this.el.child('div').hide();
12748     },
12749     /**
12750      * Show the toolbar
12751      * @method show
12752      */
12753     show : function()
12754     {
12755         this.el.child('div').show();
12756     },
12757       
12758     // private
12759     nextBlock : function(){
12760         var td = document.createElement("td");
12761         this.tr.appendChild(td);
12762         return td;
12763     },
12764
12765     // private
12766     destroy : function(){
12767         if(this.items){ // rendered?
12768             Roo.destroy.apply(Roo, this.items.items);
12769         }
12770         if(this.fields){ // rendered?
12771             Roo.destroy.apply(Roo, this.fields.items);
12772         }
12773         Roo.Element.uncache(this.el, this.tr);
12774     }
12775 };
12776
12777 /**
12778  * @class Roo.Toolbar.Item
12779  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12780  * @constructor
12781  * Creates a new Item
12782  * @param {HTMLElement} el 
12783  */
12784 Roo.Toolbar.Item = function(el){
12785     this.el = Roo.getDom(el);
12786     this.id = Roo.id(this.el);
12787     this.hidden = false;
12788 };
12789
12790 Roo.Toolbar.Item.prototype = {
12791     
12792     /**
12793      * Get this item's HTML Element
12794      * @return {HTMLElement}
12795      */
12796     getEl : function(){
12797        return this.el;  
12798     },
12799
12800     // private
12801     render : function(td){
12802         this.td = td;
12803         td.appendChild(this.el);
12804     },
12805     
12806     /**
12807      * Removes and destroys this item.
12808      */
12809     destroy : function(){
12810         this.td.parentNode.removeChild(this.td);
12811     },
12812     
12813     /**
12814      * Shows this item.
12815      */
12816     show: function(){
12817         this.hidden = false;
12818         this.td.style.display = "";
12819     },
12820     
12821     /**
12822      * Hides this item.
12823      */
12824     hide: function(){
12825         this.hidden = true;
12826         this.td.style.display = "none";
12827     },
12828     
12829     /**
12830      * Convenience function for boolean show/hide.
12831      * @param {Boolean} visible true to show/false to hide
12832      */
12833     setVisible: function(visible){
12834         if(visible) {
12835             this.show();
12836         }else{
12837             this.hide();
12838         }
12839     },
12840     
12841     /**
12842      * Try to focus this item.
12843      */
12844     focus : function(){
12845         Roo.fly(this.el).focus();
12846     },
12847     
12848     /**
12849      * Disables this item.
12850      */
12851     disable : function(){
12852         Roo.fly(this.td).addClass("x-item-disabled");
12853         this.disabled = true;
12854         this.el.disabled = true;
12855     },
12856     
12857     /**
12858      * Enables this item.
12859      */
12860     enable : function(){
12861         Roo.fly(this.td).removeClass("x-item-disabled");
12862         this.disabled = false;
12863         this.el.disabled = false;
12864     }
12865 };
12866
12867
12868 /**
12869  * @class Roo.Toolbar.Separator
12870  * @extends Roo.Toolbar.Item
12871  * A simple toolbar separator class
12872  * @constructor
12873  * Creates a new Separator
12874  */
12875 Roo.Toolbar.Separator = function(){
12876     var s = document.createElement("span");
12877     s.className = "ytb-sep";
12878     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12879 };
12880 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12881     enable:Roo.emptyFn,
12882     disable:Roo.emptyFn,
12883     focus:Roo.emptyFn
12884 });
12885
12886 /**
12887  * @class Roo.Toolbar.Spacer
12888  * @extends Roo.Toolbar.Item
12889  * A simple element that adds extra horizontal space to a toolbar.
12890  * @constructor
12891  * Creates a new Spacer
12892  */
12893 Roo.Toolbar.Spacer = function(){
12894     var s = document.createElement("div");
12895     s.className = "ytb-spacer";
12896     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12897 };
12898 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12899     enable:Roo.emptyFn,
12900     disable:Roo.emptyFn,
12901     focus:Roo.emptyFn
12902 });
12903
12904 /**
12905  * @class Roo.Toolbar.Fill
12906  * @extends Roo.Toolbar.Spacer
12907  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12908  * @constructor
12909  * Creates a new Spacer
12910  */
12911 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12912     // private
12913     render : function(td){
12914         td.style.width = '100%';
12915         Roo.Toolbar.Fill.superclass.render.call(this, td);
12916     }
12917 });
12918
12919 /**
12920  * @class Roo.Toolbar.TextItem
12921  * @extends Roo.Toolbar.Item
12922  * A simple class that renders text directly into a toolbar.
12923  * @constructor
12924  * Creates a new TextItem
12925  * @param {String} text
12926  */
12927 Roo.Toolbar.TextItem = function(text){
12928     if (typeof(text) == 'object') {
12929         text = text.text;
12930     }
12931     var s = document.createElement("span");
12932     s.className = "ytb-text";
12933     s.innerHTML = text;
12934     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12935 };
12936 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12937     enable:Roo.emptyFn,
12938     disable:Roo.emptyFn,
12939     focus:Roo.emptyFn
12940 });
12941
12942 /**
12943  * @class Roo.Toolbar.Button
12944  * @extends Roo.Button
12945  * A button that renders into a toolbar.
12946  * @constructor
12947  * Creates a new Button
12948  * @param {Object} config A standard {@link Roo.Button} config object
12949  */
12950 Roo.Toolbar.Button = function(config){
12951     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12952 };
12953 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12954     render : function(td){
12955         this.td = td;
12956         Roo.Toolbar.Button.superclass.render.call(this, td);
12957     },
12958     
12959     /**
12960      * Removes and destroys this button
12961      */
12962     destroy : function(){
12963         Roo.Toolbar.Button.superclass.destroy.call(this);
12964         this.td.parentNode.removeChild(this.td);
12965     },
12966     
12967     /**
12968      * Shows this button
12969      */
12970     show: function(){
12971         this.hidden = false;
12972         this.td.style.display = "";
12973     },
12974     
12975     /**
12976      * Hides this button
12977      */
12978     hide: function(){
12979         this.hidden = true;
12980         this.td.style.display = "none";
12981     },
12982
12983     /**
12984      * Disables this item
12985      */
12986     disable : function(){
12987         Roo.fly(this.td).addClass("x-item-disabled");
12988         this.disabled = true;
12989     },
12990
12991     /**
12992      * Enables this item
12993      */
12994     enable : function(){
12995         Roo.fly(this.td).removeClass("x-item-disabled");
12996         this.disabled = false;
12997     }
12998 });
12999 // backwards compat
13000 Roo.ToolbarButton = Roo.Toolbar.Button;
13001
13002 /**
13003  * @class Roo.Toolbar.SplitButton
13004  * @extends Roo.SplitButton
13005  * A menu button that renders into a toolbar.
13006  * @constructor
13007  * Creates a new SplitButton
13008  * @param {Object} config A standard {@link Roo.SplitButton} config object
13009  */
13010 Roo.Toolbar.SplitButton = function(config){
13011     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
13012 };
13013 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
13014     render : function(td){
13015         this.td = td;
13016         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
13017     },
13018     
13019     /**
13020      * Removes and destroys this button
13021      */
13022     destroy : function(){
13023         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
13024         this.td.parentNode.removeChild(this.td);
13025     },
13026     
13027     /**
13028      * Shows this button
13029      */
13030     show: function(){
13031         this.hidden = false;
13032         this.td.style.display = "";
13033     },
13034     
13035     /**
13036      * Hides this button
13037      */
13038     hide: function(){
13039         this.hidden = true;
13040         this.td.style.display = "none";
13041     }
13042 });
13043
13044 // backwards compat
13045 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13046  * Based on:
13047  * Ext JS Library 1.1.1
13048  * Copyright(c) 2006-2007, Ext JS, LLC.
13049  *
13050  * Originally Released Under LGPL - original licence link has changed is not relivant.
13051  *
13052  * Fork - LGPL
13053  * <script type="text/javascript">
13054  */
13055  
13056 /**
13057  * @class Roo.PagingToolbar
13058  * @extends Roo.Toolbar
13059  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13060  * @constructor
13061  * Create a new PagingToolbar
13062  * @param {Object} config The config object
13063  */
13064 Roo.PagingToolbar = function(el, ds, config)
13065 {
13066     // old args format still supported... - xtype is prefered..
13067     if (typeof(el) == 'object' && el.xtype) {
13068         // created from xtype...
13069         config = el;
13070         ds = el.dataSource;
13071         el = config.container;
13072     }
13073     var items = [];
13074     if (config.items) {
13075         items = config.items;
13076         config.items = [];
13077     }
13078     
13079     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13080     this.ds = ds;
13081     this.cursor = 0;
13082     this.renderButtons(this.el);
13083     this.bind(ds);
13084     
13085     // supprot items array.
13086    
13087     Roo.each(items, function(e) {
13088         this.add(Roo.factory(e));
13089     },this);
13090     
13091 };
13092
13093 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13094     /**
13095      * @cfg {Roo.data.Store} dataSource
13096      * The underlying data store providing the paged data
13097      */
13098     /**
13099      * @cfg {String/HTMLElement/Element} container
13100      * container The id or element that will contain the toolbar
13101      */
13102     /**
13103      * @cfg {Boolean} displayInfo
13104      * True to display the displayMsg (defaults to false)
13105      */
13106     /**
13107      * @cfg {Number} pageSize
13108      * The number of records to display per page (defaults to 20)
13109      */
13110     pageSize: 20,
13111     /**
13112      * @cfg {String} displayMsg
13113      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13114      */
13115     displayMsg : 'Displaying {0} - {1} of {2}',
13116     /**
13117      * @cfg {String} emptyMsg
13118      * The message to display when no records are found (defaults to "No data to display")
13119      */
13120     emptyMsg : 'No data to display',
13121     /**
13122      * Customizable piece of the default paging text (defaults to "Page")
13123      * @type String
13124      */
13125     beforePageText : "Page",
13126     /**
13127      * Customizable piece of the default paging text (defaults to "of %0")
13128      * @type String
13129      */
13130     afterPageText : "of {0}",
13131     /**
13132      * Customizable piece of the default paging text (defaults to "First Page")
13133      * @type String
13134      */
13135     firstText : "First Page",
13136     /**
13137      * Customizable piece of the default paging text (defaults to "Previous Page")
13138      * @type String
13139      */
13140     prevText : "Previous Page",
13141     /**
13142      * Customizable piece of the default paging text (defaults to "Next Page")
13143      * @type String
13144      */
13145     nextText : "Next Page",
13146     /**
13147      * Customizable piece of the default paging text (defaults to "Last Page")
13148      * @type String
13149      */
13150     lastText : "Last Page",
13151     /**
13152      * Customizable piece of the default paging text (defaults to "Refresh")
13153      * @type String
13154      */
13155     refreshText : "Refresh",
13156
13157     // private
13158     renderButtons : function(el){
13159         Roo.PagingToolbar.superclass.render.call(this, el);
13160         this.first = this.addButton({
13161             tooltip: this.firstText,
13162             cls: "x-btn-icon x-grid-page-first",
13163             disabled: true,
13164             handler: this.onClick.createDelegate(this, ["first"])
13165         });
13166         this.prev = this.addButton({
13167             tooltip: this.prevText,
13168             cls: "x-btn-icon x-grid-page-prev",
13169             disabled: true,
13170             handler: this.onClick.createDelegate(this, ["prev"])
13171         });
13172         //this.addSeparator();
13173         this.add(this.beforePageText);
13174         this.field = Roo.get(this.addDom({
13175            tag: "input",
13176            type: "text",
13177            size: "3",
13178            value: "1",
13179            cls: "x-grid-page-number"
13180         }).el);
13181         this.field.on("keydown", this.onPagingKeydown, this);
13182         this.field.on("focus", function(){this.dom.select();});
13183         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13184         this.field.setHeight(18);
13185         //this.addSeparator();
13186         this.next = this.addButton({
13187             tooltip: this.nextText,
13188             cls: "x-btn-icon x-grid-page-next",
13189             disabled: true,
13190             handler: this.onClick.createDelegate(this, ["next"])
13191         });
13192         this.last = this.addButton({
13193             tooltip: this.lastText,
13194             cls: "x-btn-icon x-grid-page-last",
13195             disabled: true,
13196             handler: this.onClick.createDelegate(this, ["last"])
13197         });
13198         //this.addSeparator();
13199         this.loading = this.addButton({
13200             tooltip: this.refreshText,
13201             cls: "x-btn-icon x-grid-loading",
13202             handler: this.onClick.createDelegate(this, ["refresh"])
13203         });
13204
13205         if(this.displayInfo){
13206             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13207         }
13208     },
13209
13210     // private
13211     updateInfo : function(){
13212         if(this.displayEl){
13213             var count = this.ds.getCount();
13214             var msg = count == 0 ?
13215                 this.emptyMsg :
13216                 String.format(
13217                     this.displayMsg,
13218                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13219                 );
13220             this.displayEl.update(msg);
13221         }
13222     },
13223
13224     // private
13225     onLoad : function(ds, r, o){
13226        this.cursor = o.params ? o.params.start : 0;
13227        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13228
13229        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13230        this.field.dom.value = ap;
13231        this.first.setDisabled(ap == 1);
13232        this.prev.setDisabled(ap == 1);
13233        this.next.setDisabled(ap == ps);
13234        this.last.setDisabled(ap == ps);
13235        this.loading.enable();
13236        this.updateInfo();
13237     },
13238
13239     // private
13240     getPageData : function(){
13241         var total = this.ds.getTotalCount();
13242         return {
13243             total : total,
13244             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13245             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13246         };
13247     },
13248
13249     // private
13250     onLoadError : function(){
13251         this.loading.enable();
13252     },
13253
13254     // private
13255     onPagingKeydown : function(e){
13256         var k = e.getKey();
13257         var d = this.getPageData();
13258         if(k == e.RETURN){
13259             var v = this.field.dom.value, pageNum;
13260             if(!v || isNaN(pageNum = parseInt(v, 10))){
13261                 this.field.dom.value = d.activePage;
13262                 return;
13263             }
13264             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13265             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13266             e.stopEvent();
13267         }
13268         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))
13269         {
13270           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13271           this.field.dom.value = pageNum;
13272           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13273           e.stopEvent();
13274         }
13275         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13276         {
13277           var v = this.field.dom.value, pageNum; 
13278           var increment = (e.shiftKey) ? 10 : 1;
13279           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13280             increment *= -1;
13281           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13282             this.field.dom.value = d.activePage;
13283             return;
13284           }
13285           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13286           {
13287             this.field.dom.value = parseInt(v, 10) + increment;
13288             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13289             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13290           }
13291           e.stopEvent();
13292         }
13293     },
13294
13295     // private
13296     beforeLoad : function(){
13297         if(this.loading){
13298             this.loading.disable();
13299         }
13300     },
13301
13302     // private
13303     onClick : function(which){
13304         var ds = this.ds;
13305         switch(which){
13306             case "first":
13307                 ds.load({params:{start: 0, limit: this.pageSize}});
13308             break;
13309             case "prev":
13310                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13311             break;
13312             case "next":
13313                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13314             break;
13315             case "last":
13316                 var total = ds.getTotalCount();
13317                 var extra = total % this.pageSize;
13318                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13319                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13320             break;
13321             case "refresh":
13322                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13323             break;
13324         }
13325     },
13326
13327     /**
13328      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13329      * @param {Roo.data.Store} store The data store to unbind
13330      */
13331     unbind : function(ds){
13332         ds.un("beforeload", this.beforeLoad, this);
13333         ds.un("load", this.onLoad, this);
13334         ds.un("loadexception", this.onLoadError, this);
13335         ds.un("remove", this.updateInfo, this);
13336         ds.un("add", this.updateInfo, this);
13337         this.ds = undefined;
13338     },
13339
13340     /**
13341      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13342      * @param {Roo.data.Store} store The data store to bind
13343      */
13344     bind : function(ds){
13345         ds.on("beforeload", this.beforeLoad, this);
13346         ds.on("load", this.onLoad, this);
13347         ds.on("loadexception", this.onLoadError, this);
13348         ds.on("remove", this.updateInfo, this);
13349         ds.on("add", this.updateInfo, this);
13350         this.ds = ds;
13351     }
13352 });/*
13353  * Based on:
13354  * Ext JS Library 1.1.1
13355  * Copyright(c) 2006-2007, Ext JS, LLC.
13356  *
13357  * Originally Released Under LGPL - original licence link has changed is not relivant.
13358  *
13359  * Fork - LGPL
13360  * <script type="text/javascript">
13361  */
13362
13363 /**
13364  * @class Roo.Resizable
13365  * @extends Roo.util.Observable
13366  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13367  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13368  * 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
13369  * the element will be wrapped for you automatically.</p>
13370  * <p>Here is the list of valid resize handles:</p>
13371  * <pre>
13372 Value   Description
13373 ------  -------------------
13374  'n'     north
13375  's'     south
13376  'e'     east
13377  'w'     west
13378  'nw'    northwest
13379  'sw'    southwest
13380  'se'    southeast
13381  'ne'    northeast
13382  'hd'    horizontal drag
13383  'all'   all
13384 </pre>
13385  * <p>Here's an example showing the creation of a typical Resizable:</p>
13386  * <pre><code>
13387 var resizer = new Roo.Resizable("element-id", {
13388     handles: 'all',
13389     minWidth: 200,
13390     minHeight: 100,
13391     maxWidth: 500,
13392     maxHeight: 400,
13393     pinned: true
13394 });
13395 resizer.on("resize", myHandler);
13396 </code></pre>
13397  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13398  * resizer.east.setDisplayed(false);</p>
13399  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13400  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13401  * resize operation's new size (defaults to [0, 0])
13402  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13403  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13404  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13405  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13406  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13407  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13408  * @cfg {Number} width The width of the element in pixels (defaults to null)
13409  * @cfg {Number} height The height of the element in pixels (defaults to null)
13410  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13411  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13412  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13413  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13414  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13415  * in favor of the handles config option (defaults to false)
13416  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13417  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13418  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13419  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13420  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13421  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13422  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13423  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13424  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13425  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13426  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13427  * @constructor
13428  * Create a new resizable component
13429  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13430  * @param {Object} config configuration options
13431   */
13432 Roo.Resizable = function(el, config)
13433 {
13434     this.el = Roo.get(el);
13435
13436     if(config && config.wrap){
13437         config.resizeChild = this.el;
13438         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13439         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13440         this.el.setStyle("overflow", "hidden");
13441         this.el.setPositioning(config.resizeChild.getPositioning());
13442         config.resizeChild.clearPositioning();
13443         if(!config.width || !config.height){
13444             var csize = config.resizeChild.getSize();
13445             this.el.setSize(csize.width, csize.height);
13446         }
13447         if(config.pinned && !config.adjustments){
13448             config.adjustments = "auto";
13449         }
13450     }
13451
13452     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13453     this.proxy.unselectable();
13454     this.proxy.enableDisplayMode('block');
13455
13456     Roo.apply(this, config);
13457
13458     if(this.pinned){
13459         this.disableTrackOver = true;
13460         this.el.addClass("x-resizable-pinned");
13461     }
13462     // if the element isn't positioned, make it relative
13463     var position = this.el.getStyle("position");
13464     if(position != "absolute" && position != "fixed"){
13465         this.el.setStyle("position", "relative");
13466     }
13467     if(!this.handles){ // no handles passed, must be legacy style
13468         this.handles = 's,e,se';
13469         if(this.multiDirectional){
13470             this.handles += ',n,w';
13471         }
13472     }
13473     if(this.handles == "all"){
13474         this.handles = "n s e w ne nw se sw";
13475     }
13476     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13477     var ps = Roo.Resizable.positions;
13478     for(var i = 0, len = hs.length; i < len; i++){
13479         if(hs[i] && ps[hs[i]]){
13480             var pos = ps[hs[i]];
13481             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13482         }
13483     }
13484     // legacy
13485     this.corner = this.southeast;
13486     
13487     // updateBox = the box can move..
13488     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13489         this.updateBox = true;
13490     }
13491
13492     this.activeHandle = null;
13493
13494     if(this.resizeChild){
13495         if(typeof this.resizeChild == "boolean"){
13496             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13497         }else{
13498             this.resizeChild = Roo.get(this.resizeChild, true);
13499         }
13500     }
13501     
13502     if(this.adjustments == "auto"){
13503         var rc = this.resizeChild;
13504         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13505         if(rc && (hw || hn)){
13506             rc.position("relative");
13507             rc.setLeft(hw ? hw.el.getWidth() : 0);
13508             rc.setTop(hn ? hn.el.getHeight() : 0);
13509         }
13510         this.adjustments = [
13511             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13512             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13513         ];
13514     }
13515
13516     if(this.draggable){
13517         this.dd = this.dynamic ?
13518             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13519         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13520     }
13521
13522     // public events
13523     this.addEvents({
13524         /**
13525          * @event beforeresize
13526          * Fired before resize is allowed. Set enabled to false to cancel resize.
13527          * @param {Roo.Resizable} this
13528          * @param {Roo.EventObject} e The mousedown event
13529          */
13530         "beforeresize" : true,
13531         /**
13532          * @event resizing
13533          * Fired a resizing.
13534          * @param {Roo.Resizable} this
13535          * @param {Number} x The new x position
13536          * @param {Number} y The new y position
13537          * @param {Number} w The new w width
13538          * @param {Number} h The new h hight
13539          * @param {Roo.EventObject} e The mouseup event
13540          */
13541         "resizing" : true,
13542         /**
13543          * @event resize
13544          * Fired after a resize.
13545          * @param {Roo.Resizable} this
13546          * @param {Number} width The new width
13547          * @param {Number} height The new height
13548          * @param {Roo.EventObject} e The mouseup event
13549          */
13550         "resize" : true
13551     });
13552
13553     if(this.width !== null && this.height !== null){
13554         this.resizeTo(this.width, this.height);
13555     }else{
13556         this.updateChildSize();
13557     }
13558     if(Roo.isIE){
13559         this.el.dom.style.zoom = 1;
13560     }
13561     Roo.Resizable.superclass.constructor.call(this);
13562 };
13563
13564 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13565         resizeChild : false,
13566         adjustments : [0, 0],
13567         minWidth : 5,
13568         minHeight : 5,
13569         maxWidth : 10000,
13570         maxHeight : 10000,
13571         enabled : true,
13572         animate : false,
13573         duration : .35,
13574         dynamic : false,
13575         handles : false,
13576         multiDirectional : false,
13577         disableTrackOver : false,
13578         easing : 'easeOutStrong',
13579         widthIncrement : 0,
13580         heightIncrement : 0,
13581         pinned : false,
13582         width : null,
13583         height : null,
13584         preserveRatio : false,
13585         transparent: false,
13586         minX: 0,
13587         minY: 0,
13588         draggable: false,
13589
13590         /**
13591          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13592          */
13593         constrainTo: undefined,
13594         /**
13595          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13596          */
13597         resizeRegion: undefined,
13598
13599
13600     /**
13601      * Perform a manual resize
13602      * @param {Number} width
13603      * @param {Number} height
13604      */
13605     resizeTo : function(width, height){
13606         this.el.setSize(width, height);
13607         this.updateChildSize();
13608         this.fireEvent("resize", this, width, height, null);
13609     },
13610
13611     // private
13612     startSizing : function(e, handle){
13613         this.fireEvent("beforeresize", this, e);
13614         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13615
13616             if(!this.overlay){
13617                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13618                 this.overlay.unselectable();
13619                 this.overlay.enableDisplayMode("block");
13620                 this.overlay.on("mousemove", this.onMouseMove, this);
13621                 this.overlay.on("mouseup", this.onMouseUp, this);
13622             }
13623             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13624
13625             this.resizing = true;
13626             this.startBox = this.el.getBox();
13627             this.startPoint = e.getXY();
13628             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13629                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13630
13631             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13632             this.overlay.show();
13633
13634             if(this.constrainTo) {
13635                 var ct = Roo.get(this.constrainTo);
13636                 this.resizeRegion = ct.getRegion().adjust(
13637                     ct.getFrameWidth('t'),
13638                     ct.getFrameWidth('l'),
13639                     -ct.getFrameWidth('b'),
13640                     -ct.getFrameWidth('r')
13641                 );
13642             }
13643
13644             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13645             this.proxy.show();
13646             this.proxy.setBox(this.startBox);
13647             if(!this.dynamic){
13648                 this.proxy.setStyle('visibility', 'visible');
13649             }
13650         }
13651     },
13652
13653     // private
13654     onMouseDown : function(handle, e){
13655         if(this.enabled){
13656             e.stopEvent();
13657             this.activeHandle = handle;
13658             this.startSizing(e, handle);
13659         }
13660     },
13661
13662     // private
13663     onMouseUp : function(e){
13664         var size = this.resizeElement();
13665         this.resizing = false;
13666         this.handleOut();
13667         this.overlay.hide();
13668         this.proxy.hide();
13669         this.fireEvent("resize", this, size.width, size.height, e);
13670     },
13671
13672     // private
13673     updateChildSize : function(){
13674         
13675         if(this.resizeChild){
13676             var el = this.el;
13677             var child = this.resizeChild;
13678             var adj = this.adjustments;
13679             if(el.dom.offsetWidth){
13680                 var b = el.getSize(true);
13681                 child.setSize(b.width+adj[0], b.height+adj[1]);
13682             }
13683             // Second call here for IE
13684             // The first call enables instant resizing and
13685             // the second call corrects scroll bars if they
13686             // exist
13687             if(Roo.isIE){
13688                 setTimeout(function(){
13689                     if(el.dom.offsetWidth){
13690                         var b = el.getSize(true);
13691                         child.setSize(b.width+adj[0], b.height+adj[1]);
13692                     }
13693                 }, 10);
13694             }
13695         }
13696     },
13697
13698     // private
13699     snap : function(value, inc, min){
13700         if(!inc || !value) return value;
13701         var newValue = value;
13702         var m = value % inc;
13703         if(m > 0){
13704             if(m > (inc/2)){
13705                 newValue = value + (inc-m);
13706             }else{
13707                 newValue = value - m;
13708             }
13709         }
13710         return Math.max(min, newValue);
13711     },
13712
13713     // private
13714     resizeElement : function(){
13715         var box = this.proxy.getBox();
13716         if(this.updateBox){
13717             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13718         }else{
13719             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13720         }
13721         this.updateChildSize();
13722         if(!this.dynamic){
13723             this.proxy.hide();
13724         }
13725         return box;
13726     },
13727
13728     // private
13729     constrain : function(v, diff, m, mx){
13730         if(v - diff < m){
13731             diff = v - m;
13732         }else if(v - diff > mx){
13733             diff = mx - v;
13734         }
13735         return diff;
13736     },
13737
13738     // private
13739     onMouseMove : function(e){
13740         
13741         if(this.enabled){
13742             try{// try catch so if something goes wrong the user doesn't get hung
13743
13744             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13745                 return;
13746             }
13747
13748             //var curXY = this.startPoint;
13749             var curSize = this.curSize || this.startBox;
13750             var x = this.startBox.x, y = this.startBox.y;
13751             var ox = x, oy = y;
13752             var w = curSize.width, h = curSize.height;
13753             var ow = w, oh = h;
13754             var mw = this.minWidth, mh = this.minHeight;
13755             var mxw = this.maxWidth, mxh = this.maxHeight;
13756             var wi = this.widthIncrement;
13757             var hi = this.heightIncrement;
13758
13759             var eventXY = e.getXY();
13760             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13761             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13762
13763             var pos = this.activeHandle.position;
13764
13765             switch(pos){
13766                 case "east":
13767                     w += diffX;
13768                     w = Math.min(Math.max(mw, w), mxw);
13769                     break;
13770              
13771                 case "south":
13772                     h += diffY;
13773                     h = Math.min(Math.max(mh, h), mxh);
13774                     break;
13775                 case "southeast":
13776                     w += diffX;
13777                     h += diffY;
13778                     w = Math.min(Math.max(mw, w), mxw);
13779                     h = Math.min(Math.max(mh, h), mxh);
13780                     break;
13781                 case "north":
13782                     diffY = this.constrain(h, diffY, mh, mxh);
13783                     y += diffY;
13784                     h -= diffY;
13785                     break;
13786                 case "hdrag":
13787                     
13788                     if (wi) {
13789                         var adiffX = Math.abs(diffX);
13790                         var sub = (adiffX % wi); // how much 
13791                         if (sub > (wi/2)) { // far enough to snap
13792                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13793                         } else {
13794                             // remove difference.. 
13795                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13796                         }
13797                     }
13798                     x += diffX;
13799                     x = Math.max(this.minX, x);
13800                     break;
13801                 case "west":
13802                     diffX = this.constrain(w, diffX, mw, mxw);
13803                     x += diffX;
13804                     w -= diffX;
13805                     break;
13806                 case "northeast":
13807                     w += diffX;
13808                     w = Math.min(Math.max(mw, w), mxw);
13809                     diffY = this.constrain(h, diffY, mh, mxh);
13810                     y += diffY;
13811                     h -= diffY;
13812                     break;
13813                 case "northwest":
13814                     diffX = this.constrain(w, diffX, mw, mxw);
13815                     diffY = this.constrain(h, diffY, mh, mxh);
13816                     y += diffY;
13817                     h -= diffY;
13818                     x += diffX;
13819                     w -= diffX;
13820                     break;
13821                case "southwest":
13822                     diffX = this.constrain(w, diffX, mw, mxw);
13823                     h += diffY;
13824                     h = Math.min(Math.max(mh, h), mxh);
13825                     x += diffX;
13826                     w -= diffX;
13827                     break;
13828             }
13829
13830             var sw = this.snap(w, wi, mw);
13831             var sh = this.snap(h, hi, mh);
13832             if(sw != w || sh != h){
13833                 switch(pos){
13834                     case "northeast":
13835                         y -= sh - h;
13836                     break;
13837                     case "north":
13838                         y -= sh - h;
13839                         break;
13840                     case "southwest":
13841                         x -= sw - w;
13842                     break;
13843                     case "west":
13844                         x -= sw - w;
13845                         break;
13846                     case "northwest":
13847                         x -= sw - w;
13848                         y -= sh - h;
13849                     break;
13850                 }
13851                 w = sw;
13852                 h = sh;
13853             }
13854
13855             if(this.preserveRatio){
13856                 switch(pos){
13857                     case "southeast":
13858                     case "east":
13859                         h = oh * (w/ow);
13860                         h = Math.min(Math.max(mh, h), mxh);
13861                         w = ow * (h/oh);
13862                        break;
13863                     case "south":
13864                         w = ow * (h/oh);
13865                         w = Math.min(Math.max(mw, w), mxw);
13866                         h = oh * (w/ow);
13867                         break;
13868                     case "northeast":
13869                         w = ow * (h/oh);
13870                         w = Math.min(Math.max(mw, w), mxw);
13871                         h = oh * (w/ow);
13872                     break;
13873                     case "north":
13874                         var tw = w;
13875                         w = ow * (h/oh);
13876                         w = Math.min(Math.max(mw, w), mxw);
13877                         h = oh * (w/ow);
13878                         x += (tw - w) / 2;
13879                         break;
13880                     case "southwest":
13881                         h = oh * (w/ow);
13882                         h = Math.min(Math.max(mh, h), mxh);
13883                         var tw = w;
13884                         w = ow * (h/oh);
13885                         x += tw - w;
13886                         break;
13887                     case "west":
13888                         var th = h;
13889                         h = oh * (w/ow);
13890                         h = Math.min(Math.max(mh, h), mxh);
13891                         y += (th - h) / 2;
13892                         var tw = w;
13893                         w = ow * (h/oh);
13894                         x += tw - w;
13895                        break;
13896                     case "northwest":
13897                         var tw = w;
13898                         var th = h;
13899                         h = oh * (w/ow);
13900                         h = Math.min(Math.max(mh, h), mxh);
13901                         w = ow * (h/oh);
13902                         y += th - h;
13903                         x += tw - w;
13904                        break;
13905
13906                 }
13907             }
13908             if (pos == 'hdrag') {
13909                 w = ow;
13910             }
13911             this.proxy.setBounds(x, y, w, h);
13912             if(this.dynamic){
13913                 this.resizeElement();
13914             }
13915             }catch(e){}
13916         }
13917         this.fireEvent("resizing", this, x, y, w, h, e);
13918     },
13919
13920     // private
13921     handleOver : function(){
13922         if(this.enabled){
13923             this.el.addClass("x-resizable-over");
13924         }
13925     },
13926
13927     // private
13928     handleOut : function(){
13929         if(!this.resizing){
13930             this.el.removeClass("x-resizable-over");
13931         }
13932     },
13933
13934     /**
13935      * Returns the element this component is bound to.
13936      * @return {Roo.Element}
13937      */
13938     getEl : function(){
13939         return this.el;
13940     },
13941
13942     /**
13943      * Returns the resizeChild element (or null).
13944      * @return {Roo.Element}
13945      */
13946     getResizeChild : function(){
13947         return this.resizeChild;
13948     },
13949     groupHandler : function()
13950     {
13951         
13952     },
13953     /**
13954      * Destroys this resizable. If the element was wrapped and
13955      * removeEl is not true then the element remains.
13956      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13957      */
13958     destroy : function(removeEl){
13959         this.proxy.remove();
13960         if(this.overlay){
13961             this.overlay.removeAllListeners();
13962             this.overlay.remove();
13963         }
13964         var ps = Roo.Resizable.positions;
13965         for(var k in ps){
13966             if(typeof ps[k] != "function" && this[ps[k]]){
13967                 var h = this[ps[k]];
13968                 h.el.removeAllListeners();
13969                 h.el.remove();
13970             }
13971         }
13972         if(removeEl){
13973             this.el.update("");
13974             this.el.remove();
13975         }
13976     }
13977 });
13978
13979 // private
13980 // hash to map config positions to true positions
13981 Roo.Resizable.positions = {
13982     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13983     hd: "hdrag"
13984 };
13985
13986 // private
13987 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13988     if(!this.tpl){
13989         // only initialize the template if resizable is used
13990         var tpl = Roo.DomHelper.createTemplate(
13991             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13992         );
13993         tpl.compile();
13994         Roo.Resizable.Handle.prototype.tpl = tpl;
13995     }
13996     this.position = pos;
13997     this.rz = rz;
13998     // show north drag fro topdra
13999     var handlepos = pos == 'hdrag' ? 'north' : pos;
14000     
14001     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
14002     if (pos == 'hdrag') {
14003         this.el.setStyle('cursor', 'pointer');
14004     }
14005     this.el.unselectable();
14006     if(transparent){
14007         this.el.setOpacity(0);
14008     }
14009     this.el.on("mousedown", this.onMouseDown, this);
14010     if(!disableTrackOver){
14011         this.el.on("mouseover", this.onMouseOver, this);
14012         this.el.on("mouseout", this.onMouseOut, this);
14013     }
14014 };
14015
14016 // private
14017 Roo.Resizable.Handle.prototype = {
14018     afterResize : function(rz){
14019         // do nothing
14020     },
14021     // private
14022     onMouseDown : function(e){
14023         this.rz.onMouseDown(this, e);
14024     },
14025     // private
14026     onMouseOver : function(e){
14027         this.rz.handleOver(this, e);
14028     },
14029     // private
14030     onMouseOut : function(e){
14031         this.rz.handleOut(this, e);
14032     }
14033 };/*
14034  * Based on:
14035  * Ext JS Library 1.1.1
14036  * Copyright(c) 2006-2007, Ext JS, LLC.
14037  *
14038  * Originally Released Under LGPL - original licence link has changed is not relivant.
14039  *
14040  * Fork - LGPL
14041  * <script type="text/javascript">
14042  */
14043
14044 /**
14045  * @class Roo.Editor
14046  * @extends Roo.Component
14047  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14048  * @constructor
14049  * Create a new Editor
14050  * @param {Roo.form.Field} field The Field object (or descendant)
14051  * @param {Object} config The config object
14052  */
14053 Roo.Editor = function(field, config){
14054     Roo.Editor.superclass.constructor.call(this, config);
14055     this.field = field;
14056     this.addEvents({
14057         /**
14058              * @event beforestartedit
14059              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14060              * false from the handler of this event.
14061              * @param {Editor} this
14062              * @param {Roo.Element} boundEl The underlying element bound to this editor
14063              * @param {Mixed} value The field value being set
14064              */
14065         "beforestartedit" : true,
14066         /**
14067              * @event startedit
14068              * Fires when this editor is displayed
14069              * @param {Roo.Element} boundEl The underlying element bound to this editor
14070              * @param {Mixed} value The starting field value
14071              */
14072         "startedit" : true,
14073         /**
14074              * @event beforecomplete
14075              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14076              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14077              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14078              * event will not fire since no edit actually occurred.
14079              * @param {Editor} this
14080              * @param {Mixed} value The current field value
14081              * @param {Mixed} startValue The original field value
14082              */
14083         "beforecomplete" : true,
14084         /**
14085              * @event complete
14086              * Fires after editing is complete and any changed value has been written to the underlying field.
14087              * @param {Editor} this
14088              * @param {Mixed} value The current field value
14089              * @param {Mixed} startValue The original field value
14090              */
14091         "complete" : true,
14092         /**
14093          * @event specialkey
14094          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14095          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14096          * @param {Roo.form.Field} this
14097          * @param {Roo.EventObject} e The event object
14098          */
14099         "specialkey" : true
14100     });
14101 };
14102
14103 Roo.extend(Roo.Editor, Roo.Component, {
14104     /**
14105      * @cfg {Boolean/String} autosize
14106      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14107      * or "height" to adopt the height only (defaults to false)
14108      */
14109     /**
14110      * @cfg {Boolean} revertInvalid
14111      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14112      * validation fails (defaults to true)
14113      */
14114     /**
14115      * @cfg {Boolean} ignoreNoChange
14116      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14117      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14118      * will never be ignored.
14119      */
14120     /**
14121      * @cfg {Boolean} hideEl
14122      * False to keep the bound element visible while the editor is displayed (defaults to true)
14123      */
14124     /**
14125      * @cfg {Mixed} value
14126      * The data value of the underlying field (defaults to "")
14127      */
14128     value : "",
14129     /**
14130      * @cfg {String} alignment
14131      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14132      */
14133     alignment: "c-c?",
14134     /**
14135      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14136      * for bottom-right shadow (defaults to "frame")
14137      */
14138     shadow : "frame",
14139     /**
14140      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14141      */
14142     constrain : false,
14143     /**
14144      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14145      */
14146     completeOnEnter : false,
14147     /**
14148      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14149      */
14150     cancelOnEsc : false,
14151     /**
14152      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14153      */
14154     updateEl : false,
14155
14156     // private
14157     onRender : function(ct, position){
14158         this.el = new Roo.Layer({
14159             shadow: this.shadow,
14160             cls: "x-editor",
14161             parentEl : ct,
14162             shim : this.shim,
14163             shadowOffset:4,
14164             id: this.id,
14165             constrain: this.constrain
14166         });
14167         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14168         if(this.field.msgTarget != 'title'){
14169             this.field.msgTarget = 'qtip';
14170         }
14171         this.field.render(this.el);
14172         if(Roo.isGecko){
14173             this.field.el.dom.setAttribute('autocomplete', 'off');
14174         }
14175         this.field.on("specialkey", this.onSpecialKey, this);
14176         if(this.swallowKeys){
14177             this.field.el.swallowEvent(['keydown','keypress']);
14178         }
14179         this.field.show();
14180         this.field.on("blur", this.onBlur, this);
14181         if(this.field.grow){
14182             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14183         }
14184     },
14185
14186     onSpecialKey : function(field, e)
14187     {
14188         //Roo.log('editor onSpecialKey');
14189         if(this.completeOnEnter && e.getKey() == e.ENTER){
14190             e.stopEvent();
14191             this.completeEdit();
14192             return;
14193         }
14194         // do not fire special key otherwise it might hide close the editor...
14195         if(e.getKey() == e.ENTER){    
14196             return;
14197         }
14198         if(this.cancelOnEsc && e.getKey() == e.ESC){
14199             this.cancelEdit();
14200             return;
14201         } 
14202         this.fireEvent('specialkey', field, e);
14203     
14204     },
14205
14206     /**
14207      * Starts the editing process and shows the editor.
14208      * @param {String/HTMLElement/Element} el The element to edit
14209      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14210       * to the innerHTML of el.
14211      */
14212     startEdit : function(el, value){
14213         if(this.editing){
14214             this.completeEdit();
14215         }
14216         this.boundEl = Roo.get(el);
14217         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14218         if(!this.rendered){
14219             this.render(this.parentEl || document.body);
14220         }
14221         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14222             return;
14223         }
14224         this.startValue = v;
14225         this.field.setValue(v);
14226         if(this.autoSize){
14227             var sz = this.boundEl.getSize();
14228             switch(this.autoSize){
14229                 case "width":
14230                 this.setSize(sz.width,  "");
14231                 break;
14232                 case "height":
14233                 this.setSize("",  sz.height);
14234                 break;
14235                 default:
14236                 this.setSize(sz.width,  sz.height);
14237             }
14238         }
14239         this.el.alignTo(this.boundEl, this.alignment);
14240         this.editing = true;
14241         if(Roo.QuickTips){
14242             Roo.QuickTips.disable();
14243         }
14244         this.show();
14245     },
14246
14247     /**
14248      * Sets the height and width of this editor.
14249      * @param {Number} width The new width
14250      * @param {Number} height The new height
14251      */
14252     setSize : function(w, h){
14253         this.field.setSize(w, h);
14254         if(this.el){
14255             this.el.sync();
14256         }
14257     },
14258
14259     /**
14260      * Realigns the editor to the bound field based on the current alignment config value.
14261      */
14262     realign : function(){
14263         this.el.alignTo(this.boundEl, this.alignment);
14264     },
14265
14266     /**
14267      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14268      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14269      */
14270     completeEdit : function(remainVisible){
14271         if(!this.editing){
14272             return;
14273         }
14274         var v = this.getValue();
14275         if(this.revertInvalid !== false && !this.field.isValid()){
14276             v = this.startValue;
14277             this.cancelEdit(true);
14278         }
14279         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14280             this.editing = false;
14281             this.hide();
14282             return;
14283         }
14284         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14285             this.editing = false;
14286             if(this.updateEl && this.boundEl){
14287                 this.boundEl.update(v);
14288             }
14289             if(remainVisible !== true){
14290                 this.hide();
14291             }
14292             this.fireEvent("complete", this, v, this.startValue);
14293         }
14294     },
14295
14296     // private
14297     onShow : function(){
14298         this.el.show();
14299         if(this.hideEl !== false){
14300             this.boundEl.hide();
14301         }
14302         this.field.show();
14303         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14304             this.fixIEFocus = true;
14305             this.deferredFocus.defer(50, this);
14306         }else{
14307             this.field.focus();
14308         }
14309         this.fireEvent("startedit", this.boundEl, this.startValue);
14310     },
14311
14312     deferredFocus : function(){
14313         if(this.editing){
14314             this.field.focus();
14315         }
14316     },
14317
14318     /**
14319      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14320      * reverted to the original starting value.
14321      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14322      * cancel (defaults to false)
14323      */
14324     cancelEdit : function(remainVisible){
14325         if(this.editing){
14326             this.setValue(this.startValue);
14327             if(remainVisible !== true){
14328                 this.hide();
14329             }
14330         }
14331     },
14332
14333     // private
14334     onBlur : function(){
14335         if(this.allowBlur !== true && this.editing){
14336             this.completeEdit();
14337         }
14338     },
14339
14340     // private
14341     onHide : function(){
14342         if(this.editing){
14343             this.completeEdit();
14344             return;
14345         }
14346         this.field.blur();
14347         if(this.field.collapse){
14348             this.field.collapse();
14349         }
14350         this.el.hide();
14351         if(this.hideEl !== false){
14352             this.boundEl.show();
14353         }
14354         if(Roo.QuickTips){
14355             Roo.QuickTips.enable();
14356         }
14357     },
14358
14359     /**
14360      * Sets the data value of the editor
14361      * @param {Mixed} value Any valid value supported by the underlying field
14362      */
14363     setValue : function(v){
14364         this.field.setValue(v);
14365     },
14366
14367     /**
14368      * Gets the data value of the editor
14369      * @return {Mixed} The data value
14370      */
14371     getValue : function(){
14372         return this.field.getValue();
14373     }
14374 });/*
14375  * Based on:
14376  * Ext JS Library 1.1.1
14377  * Copyright(c) 2006-2007, Ext JS, LLC.
14378  *
14379  * Originally Released Under LGPL - original licence link has changed is not relivant.
14380  *
14381  * Fork - LGPL
14382  * <script type="text/javascript">
14383  */
14384  
14385 /**
14386  * @class Roo.BasicDialog
14387  * @extends Roo.util.Observable
14388  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14389  * <pre><code>
14390 var dlg = new Roo.BasicDialog("my-dlg", {
14391     height: 200,
14392     width: 300,
14393     minHeight: 100,
14394     minWidth: 150,
14395     modal: true,
14396     proxyDrag: true,
14397     shadow: true
14398 });
14399 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14400 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14401 dlg.addButton('Cancel', dlg.hide, dlg);
14402 dlg.show();
14403 </code></pre>
14404   <b>A Dialog should always be a direct child of the body element.</b>
14405  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14406  * @cfg {String} title Default text to display in the title bar (defaults to null)
14407  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14408  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14409  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14410  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14411  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14412  * (defaults to null with no animation)
14413  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14414  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14415  * property for valid values (defaults to 'all')
14416  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14417  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14418  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14419  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14420  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14421  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14422  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14423  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14424  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14425  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14426  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14427  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14428  * draggable = true (defaults to false)
14429  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14430  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14431  * shadow (defaults to false)
14432  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14433  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14434  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14435  * @cfg {Array} buttons Array of buttons
14436  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14437  * @constructor
14438  * Create a new BasicDialog.
14439  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14440  * @param {Object} config Configuration options
14441  */
14442 Roo.BasicDialog = function(el, config){
14443     this.el = Roo.get(el);
14444     var dh = Roo.DomHelper;
14445     if(!this.el && config && config.autoCreate){
14446         if(typeof config.autoCreate == "object"){
14447             if(!config.autoCreate.id){
14448                 config.autoCreate.id = el;
14449             }
14450             this.el = dh.append(document.body,
14451                         config.autoCreate, true);
14452         }else{
14453             this.el = dh.append(document.body,
14454                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14455         }
14456     }
14457     el = this.el;
14458     el.setDisplayed(true);
14459     el.hide = this.hideAction;
14460     this.id = el.id;
14461     el.addClass("x-dlg");
14462
14463     Roo.apply(this, config);
14464
14465     this.proxy = el.createProxy("x-dlg-proxy");
14466     this.proxy.hide = this.hideAction;
14467     this.proxy.setOpacity(.5);
14468     this.proxy.hide();
14469
14470     if(config.width){
14471         el.setWidth(config.width);
14472     }
14473     if(config.height){
14474         el.setHeight(config.height);
14475     }
14476     this.size = el.getSize();
14477     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14478         this.xy = [config.x,config.y];
14479     }else{
14480         this.xy = el.getCenterXY(true);
14481     }
14482     /** The header element @type Roo.Element */
14483     this.header = el.child("> .x-dlg-hd");
14484     /** The body element @type Roo.Element */
14485     this.body = el.child("> .x-dlg-bd");
14486     /** The footer element @type Roo.Element */
14487     this.footer = el.child("> .x-dlg-ft");
14488
14489     if(!this.header){
14490         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14491     }
14492     if(!this.body){
14493         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14494     }
14495
14496     this.header.unselectable();
14497     if(this.title){
14498         this.header.update(this.title);
14499     }
14500     // this element allows the dialog to be focused for keyboard event
14501     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14502     this.focusEl.swallowEvent("click", true);
14503
14504     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14505
14506     // wrap the body and footer for special rendering
14507     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14508     if(this.footer){
14509         this.bwrap.dom.appendChild(this.footer.dom);
14510     }
14511
14512     this.bg = this.el.createChild({
14513         tag: "div", cls:"x-dlg-bg",
14514         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14515     });
14516     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14517
14518
14519     if(this.autoScroll !== false && !this.autoTabs){
14520         this.body.setStyle("overflow", "auto");
14521     }
14522
14523     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14524
14525     if(this.closable !== false){
14526         this.el.addClass("x-dlg-closable");
14527         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14528         this.close.on("click", this.closeClick, this);
14529         this.close.addClassOnOver("x-dlg-close-over");
14530     }
14531     if(this.collapsible !== false){
14532         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14533         this.collapseBtn.on("click", this.collapseClick, this);
14534         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14535         this.header.on("dblclick", this.collapseClick, this);
14536     }
14537     if(this.resizable !== false){
14538         this.el.addClass("x-dlg-resizable");
14539         this.resizer = new Roo.Resizable(el, {
14540             minWidth: this.minWidth || 80,
14541             minHeight:this.minHeight || 80,
14542             handles: this.resizeHandles || "all",
14543             pinned: true
14544         });
14545         this.resizer.on("beforeresize", this.beforeResize, this);
14546         this.resizer.on("resize", this.onResize, this);
14547     }
14548     if(this.draggable !== false){
14549         el.addClass("x-dlg-draggable");
14550         if (!this.proxyDrag) {
14551             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14552         }
14553         else {
14554             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14555         }
14556         dd.setHandleElId(this.header.id);
14557         dd.endDrag = this.endMove.createDelegate(this);
14558         dd.startDrag = this.startMove.createDelegate(this);
14559         dd.onDrag = this.onDrag.createDelegate(this);
14560         dd.scroll = false;
14561         this.dd = dd;
14562     }
14563     if(this.modal){
14564         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14565         this.mask.enableDisplayMode("block");
14566         this.mask.hide();
14567         this.el.addClass("x-dlg-modal");
14568     }
14569     if(this.shadow){
14570         this.shadow = new Roo.Shadow({
14571             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14572             offset : this.shadowOffset
14573         });
14574     }else{
14575         this.shadowOffset = 0;
14576     }
14577     if(Roo.useShims && this.shim !== false){
14578         this.shim = this.el.createShim();
14579         this.shim.hide = this.hideAction;
14580         this.shim.hide();
14581     }else{
14582         this.shim = false;
14583     }
14584     if(this.autoTabs){
14585         this.initTabs();
14586     }
14587     if (this.buttons) { 
14588         var bts= this.buttons;
14589         this.buttons = [];
14590         Roo.each(bts, function(b) {
14591             this.addButton(b);
14592         }, this);
14593     }
14594     
14595     
14596     this.addEvents({
14597         /**
14598          * @event keydown
14599          * Fires when a key is pressed
14600          * @param {Roo.BasicDialog} this
14601          * @param {Roo.EventObject} e
14602          */
14603         "keydown" : true,
14604         /**
14605          * @event move
14606          * Fires when this dialog is moved by the user.
14607          * @param {Roo.BasicDialog} this
14608          * @param {Number} x The new page X
14609          * @param {Number} y The new page Y
14610          */
14611         "move" : true,
14612         /**
14613          * @event resize
14614          * Fires when this dialog is resized by the user.
14615          * @param {Roo.BasicDialog} this
14616          * @param {Number} width The new width
14617          * @param {Number} height The new height
14618          */
14619         "resize" : true,
14620         /**
14621          * @event beforehide
14622          * Fires before this dialog is hidden.
14623          * @param {Roo.BasicDialog} this
14624          */
14625         "beforehide" : true,
14626         /**
14627          * @event hide
14628          * Fires when this dialog is hidden.
14629          * @param {Roo.BasicDialog} this
14630          */
14631         "hide" : true,
14632         /**
14633          * @event beforeshow
14634          * Fires before this dialog is shown.
14635          * @param {Roo.BasicDialog} this
14636          */
14637         "beforeshow" : true,
14638         /**
14639          * @event show
14640          * Fires when this dialog is shown.
14641          * @param {Roo.BasicDialog} this
14642          */
14643         "show" : true
14644     });
14645     el.on("keydown", this.onKeyDown, this);
14646     el.on("mousedown", this.toFront, this);
14647     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14648     this.el.hide();
14649     Roo.DialogManager.register(this);
14650     Roo.BasicDialog.superclass.constructor.call(this);
14651 };
14652
14653 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14654     shadowOffset: Roo.isIE ? 6 : 5,
14655     minHeight: 80,
14656     minWidth: 200,
14657     minButtonWidth: 75,
14658     defaultButton: null,
14659     buttonAlign: "right",
14660     tabTag: 'div',
14661     firstShow: true,
14662
14663     /**
14664      * Sets the dialog title text
14665      * @param {String} text The title text to display
14666      * @return {Roo.BasicDialog} this
14667      */
14668     setTitle : function(text){
14669         this.header.update(text);
14670         return this;
14671     },
14672
14673     // private
14674     closeClick : function(){
14675         this.hide();
14676     },
14677
14678     // private
14679     collapseClick : function(){
14680         this[this.collapsed ? "expand" : "collapse"]();
14681     },
14682
14683     /**
14684      * Collapses the dialog to its minimized state (only the title bar is visible).
14685      * Equivalent to the user clicking the collapse dialog button.
14686      */
14687     collapse : function(){
14688         if(!this.collapsed){
14689             this.collapsed = true;
14690             this.el.addClass("x-dlg-collapsed");
14691             this.restoreHeight = this.el.getHeight();
14692             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14693         }
14694     },
14695
14696     /**
14697      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14698      * clicking the expand dialog button.
14699      */
14700     expand : function(){
14701         if(this.collapsed){
14702             this.collapsed = false;
14703             this.el.removeClass("x-dlg-collapsed");
14704             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14705         }
14706     },
14707
14708     /**
14709      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14710      * @return {Roo.TabPanel} The tabs component
14711      */
14712     initTabs : function(){
14713         var tabs = this.getTabs();
14714         while(tabs.getTab(0)){
14715             tabs.removeTab(0);
14716         }
14717         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14718             var dom = el.dom;
14719             tabs.addTab(Roo.id(dom), dom.title);
14720             dom.title = "";
14721         });
14722         tabs.activate(0);
14723         return tabs;
14724     },
14725
14726     // private
14727     beforeResize : function(){
14728         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14729     },
14730
14731     // private
14732     onResize : function(){
14733         this.refreshSize();
14734         this.syncBodyHeight();
14735         this.adjustAssets();
14736         this.focus();
14737         this.fireEvent("resize", this, this.size.width, this.size.height);
14738     },
14739
14740     // private
14741     onKeyDown : function(e){
14742         if(this.isVisible()){
14743             this.fireEvent("keydown", this, e);
14744         }
14745     },
14746
14747     /**
14748      * Resizes the dialog.
14749      * @param {Number} width
14750      * @param {Number} height
14751      * @return {Roo.BasicDialog} this
14752      */
14753     resizeTo : function(width, height){
14754         this.el.setSize(width, height);
14755         this.size = {width: width, height: height};
14756         this.syncBodyHeight();
14757         if(this.fixedcenter){
14758             this.center();
14759         }
14760         if(this.isVisible()){
14761             this.constrainXY();
14762             this.adjustAssets();
14763         }
14764         this.fireEvent("resize", this, width, height);
14765         return this;
14766     },
14767
14768
14769     /**
14770      * Resizes the dialog to fit the specified content size.
14771      * @param {Number} width
14772      * @param {Number} height
14773      * @return {Roo.BasicDialog} this
14774      */
14775     setContentSize : function(w, h){
14776         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14777         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14778         //if(!this.el.isBorderBox()){
14779             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14780             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14781         //}
14782         if(this.tabs){
14783             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14784             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14785         }
14786         this.resizeTo(w, h);
14787         return this;
14788     },
14789
14790     /**
14791      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14792      * executed in response to a particular key being pressed while the dialog is active.
14793      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14794      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14795      * @param {Function} fn The function to call
14796      * @param {Object} scope (optional) The scope of the function
14797      * @return {Roo.BasicDialog} this
14798      */
14799     addKeyListener : function(key, fn, scope){
14800         var keyCode, shift, ctrl, alt;
14801         if(typeof key == "object" && !(key instanceof Array)){
14802             keyCode = key["key"];
14803             shift = key["shift"];
14804             ctrl = key["ctrl"];
14805             alt = key["alt"];
14806         }else{
14807             keyCode = key;
14808         }
14809         var handler = function(dlg, e){
14810             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14811                 var k = e.getKey();
14812                 if(keyCode instanceof Array){
14813                     for(var i = 0, len = keyCode.length; i < len; i++){
14814                         if(keyCode[i] == k){
14815                           fn.call(scope || window, dlg, k, e);
14816                           return;
14817                         }
14818                     }
14819                 }else{
14820                     if(k == keyCode){
14821                         fn.call(scope || window, dlg, k, e);
14822                     }
14823                 }
14824             }
14825         };
14826         this.on("keydown", handler);
14827         return this;
14828     },
14829
14830     /**
14831      * Returns the TabPanel component (creates it if it doesn't exist).
14832      * Note: If you wish to simply check for the existence of tabs without creating them,
14833      * check for a null 'tabs' property.
14834      * @return {Roo.TabPanel} The tabs component
14835      */
14836     getTabs : function(){
14837         if(!this.tabs){
14838             this.el.addClass("x-dlg-auto-tabs");
14839             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14840             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14841         }
14842         return this.tabs;
14843     },
14844
14845     /**
14846      * Adds a button to the footer section of the dialog.
14847      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14848      * object or a valid Roo.DomHelper element config
14849      * @param {Function} handler The function called when the button is clicked
14850      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14851      * @return {Roo.Button} The new button
14852      */
14853     addButton : function(config, handler, scope){
14854         var dh = Roo.DomHelper;
14855         if(!this.footer){
14856             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14857         }
14858         if(!this.btnContainer){
14859             var tb = this.footer.createChild({
14860
14861                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14862                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14863             }, null, true);
14864             this.btnContainer = tb.firstChild.firstChild.firstChild;
14865         }
14866         var bconfig = {
14867             handler: handler,
14868             scope: scope,
14869             minWidth: this.minButtonWidth,
14870             hideParent:true
14871         };
14872         if(typeof config == "string"){
14873             bconfig.text = config;
14874         }else{
14875             if(config.tag){
14876                 bconfig.dhconfig = config;
14877             }else{
14878                 Roo.apply(bconfig, config);
14879             }
14880         }
14881         var fc = false;
14882         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14883             bconfig.position = Math.max(0, bconfig.position);
14884             fc = this.btnContainer.childNodes[bconfig.position];
14885         }
14886          
14887         var btn = new Roo.Button(
14888             fc ? 
14889                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14890                 : this.btnContainer.appendChild(document.createElement("td")),
14891             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14892             bconfig
14893         );
14894         this.syncBodyHeight();
14895         if(!this.buttons){
14896             /**
14897              * Array of all the buttons that have been added to this dialog via addButton
14898              * @type Array
14899              */
14900             this.buttons = [];
14901         }
14902         this.buttons.push(btn);
14903         return btn;
14904     },
14905
14906     /**
14907      * Sets the default button to be focused when the dialog is displayed.
14908      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14909      * @return {Roo.BasicDialog} this
14910      */
14911     setDefaultButton : function(btn){
14912         this.defaultButton = btn;
14913         return this;
14914     },
14915
14916     // private
14917     getHeaderFooterHeight : function(safe){
14918         var height = 0;
14919         if(this.header){
14920            height += this.header.getHeight();
14921         }
14922         if(this.footer){
14923            var fm = this.footer.getMargins();
14924             height += (this.footer.getHeight()+fm.top+fm.bottom);
14925         }
14926         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14927         height += this.centerBg.getPadding("tb");
14928         return height;
14929     },
14930
14931     // private
14932     syncBodyHeight : function()
14933     {
14934         var bd = this.body, // the text
14935             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14936             bw = this.bwrap;
14937         var height = this.size.height - this.getHeaderFooterHeight(false);
14938         bd.setHeight(height-bd.getMargins("tb"));
14939         var hh = this.header.getHeight();
14940         var h = this.size.height-hh;
14941         cb.setHeight(h);
14942         
14943         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14944         bw.setHeight(h-cb.getPadding("tb"));
14945         
14946         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14947         bd.setWidth(bw.getWidth(true));
14948         if(this.tabs){
14949             this.tabs.syncHeight();
14950             if(Roo.isIE){
14951                 this.tabs.el.repaint();
14952             }
14953         }
14954     },
14955
14956     /**
14957      * Restores the previous state of the dialog if Roo.state is configured.
14958      * @return {Roo.BasicDialog} this
14959      */
14960     restoreState : function(){
14961         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14962         if(box && box.width){
14963             this.xy = [box.x, box.y];
14964             this.resizeTo(box.width, box.height);
14965         }
14966         return this;
14967     },
14968
14969     // private
14970     beforeShow : function(){
14971         this.expand();
14972         if(this.fixedcenter){
14973             this.xy = this.el.getCenterXY(true);
14974         }
14975         if(this.modal){
14976             Roo.get(document.body).addClass("x-body-masked");
14977             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14978             this.mask.show();
14979         }
14980         this.constrainXY();
14981     },
14982
14983     // private
14984     animShow : function(){
14985         var b = Roo.get(this.animateTarget).getBox();
14986         this.proxy.setSize(b.width, b.height);
14987         this.proxy.setLocation(b.x, b.y);
14988         this.proxy.show();
14989         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14990                     true, .35, this.showEl.createDelegate(this));
14991     },
14992
14993     /**
14994      * Shows the dialog.
14995      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14996      * @return {Roo.BasicDialog} this
14997      */
14998     show : function(animateTarget){
14999         if (this.fireEvent("beforeshow", this) === false){
15000             return;
15001         }
15002         if(this.syncHeightBeforeShow){
15003             this.syncBodyHeight();
15004         }else if(this.firstShow){
15005             this.firstShow = false;
15006             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
15007         }
15008         this.animateTarget = animateTarget || this.animateTarget;
15009         if(!this.el.isVisible()){
15010             this.beforeShow();
15011             if(this.animateTarget && Roo.get(this.animateTarget)){
15012                 this.animShow();
15013             }else{
15014                 this.showEl();
15015             }
15016         }
15017         return this;
15018     },
15019
15020     // private
15021     showEl : function(){
15022         this.proxy.hide();
15023         this.el.setXY(this.xy);
15024         this.el.show();
15025         this.adjustAssets(true);
15026         this.toFront();
15027         this.focus();
15028         // IE peekaboo bug - fix found by Dave Fenwick
15029         if(Roo.isIE){
15030             this.el.repaint();
15031         }
15032         this.fireEvent("show", this);
15033     },
15034
15035     /**
15036      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
15037      * dialog itself will receive focus.
15038      */
15039     focus : function(){
15040         if(this.defaultButton){
15041             this.defaultButton.focus();
15042         }else{
15043             this.focusEl.focus();
15044         }
15045     },
15046
15047     // private
15048     constrainXY : function(){
15049         if(this.constraintoviewport !== false){
15050             if(!this.viewSize){
15051                 if(this.container){
15052                     var s = this.container.getSize();
15053                     this.viewSize = [s.width, s.height];
15054                 }else{
15055                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15056                 }
15057             }
15058             var s = Roo.get(this.container||document).getScroll();
15059
15060             var x = this.xy[0], y = this.xy[1];
15061             var w = this.size.width, h = this.size.height;
15062             var vw = this.viewSize[0], vh = this.viewSize[1];
15063             // only move it if it needs it
15064             var moved = false;
15065             // first validate right/bottom
15066             if(x + w > vw+s.left){
15067                 x = vw - w;
15068                 moved = true;
15069             }
15070             if(y + h > vh+s.top){
15071                 y = vh - h;
15072                 moved = true;
15073             }
15074             // then make sure top/left isn't negative
15075             if(x < s.left){
15076                 x = s.left;
15077                 moved = true;
15078             }
15079             if(y < s.top){
15080                 y = s.top;
15081                 moved = true;
15082             }
15083             if(moved){
15084                 // cache xy
15085                 this.xy = [x, y];
15086                 if(this.isVisible()){
15087                     this.el.setLocation(x, y);
15088                     this.adjustAssets();
15089                 }
15090             }
15091         }
15092     },
15093
15094     // private
15095     onDrag : function(){
15096         if(!this.proxyDrag){
15097             this.xy = this.el.getXY();
15098             this.adjustAssets();
15099         }
15100     },
15101
15102     // private
15103     adjustAssets : function(doShow){
15104         var x = this.xy[0], y = this.xy[1];
15105         var w = this.size.width, h = this.size.height;
15106         if(doShow === true){
15107             if(this.shadow){
15108                 this.shadow.show(this.el);
15109             }
15110             if(this.shim){
15111                 this.shim.show();
15112             }
15113         }
15114         if(this.shadow && this.shadow.isVisible()){
15115             this.shadow.show(this.el);
15116         }
15117         if(this.shim && this.shim.isVisible()){
15118             this.shim.setBounds(x, y, w, h);
15119         }
15120     },
15121
15122     // private
15123     adjustViewport : function(w, h){
15124         if(!w || !h){
15125             w = Roo.lib.Dom.getViewWidth();
15126             h = Roo.lib.Dom.getViewHeight();
15127         }
15128         // cache the size
15129         this.viewSize = [w, h];
15130         if(this.modal && this.mask.isVisible()){
15131             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15132             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15133         }
15134         if(this.isVisible()){
15135             this.constrainXY();
15136         }
15137     },
15138
15139     /**
15140      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15141      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15142      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15143      */
15144     destroy : function(removeEl){
15145         if(this.isVisible()){
15146             this.animateTarget = null;
15147             this.hide();
15148         }
15149         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15150         if(this.tabs){
15151             this.tabs.destroy(removeEl);
15152         }
15153         Roo.destroy(
15154              this.shim,
15155              this.proxy,
15156              this.resizer,
15157              this.close,
15158              this.mask
15159         );
15160         if(this.dd){
15161             this.dd.unreg();
15162         }
15163         if(this.buttons){
15164            for(var i = 0, len = this.buttons.length; i < len; i++){
15165                this.buttons[i].destroy();
15166            }
15167         }
15168         this.el.removeAllListeners();
15169         if(removeEl === true){
15170             this.el.update("");
15171             this.el.remove();
15172         }
15173         Roo.DialogManager.unregister(this);
15174     },
15175
15176     // private
15177     startMove : function(){
15178         if(this.proxyDrag){
15179             this.proxy.show();
15180         }
15181         if(this.constraintoviewport !== false){
15182             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15183         }
15184     },
15185
15186     // private
15187     endMove : function(){
15188         if(!this.proxyDrag){
15189             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15190         }else{
15191             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15192             this.proxy.hide();
15193         }
15194         this.refreshSize();
15195         this.adjustAssets();
15196         this.focus();
15197         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15198     },
15199
15200     /**
15201      * Brings this dialog to the front of any other visible dialogs
15202      * @return {Roo.BasicDialog} this
15203      */
15204     toFront : function(){
15205         Roo.DialogManager.bringToFront(this);
15206         return this;
15207     },
15208
15209     /**
15210      * Sends this dialog to the back (under) of any other visible dialogs
15211      * @return {Roo.BasicDialog} this
15212      */
15213     toBack : function(){
15214         Roo.DialogManager.sendToBack(this);
15215         return this;
15216     },
15217
15218     /**
15219      * Centers this dialog in the viewport
15220      * @return {Roo.BasicDialog} this
15221      */
15222     center : function(){
15223         var xy = this.el.getCenterXY(true);
15224         this.moveTo(xy[0], xy[1]);
15225         return this;
15226     },
15227
15228     /**
15229      * Moves the dialog's top-left corner to the specified point
15230      * @param {Number} x
15231      * @param {Number} y
15232      * @return {Roo.BasicDialog} this
15233      */
15234     moveTo : function(x, y){
15235         this.xy = [x,y];
15236         if(this.isVisible()){
15237             this.el.setXY(this.xy);
15238             this.adjustAssets();
15239         }
15240         return this;
15241     },
15242
15243     /**
15244      * Aligns the dialog to the specified element
15245      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15246      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15247      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15248      * @return {Roo.BasicDialog} this
15249      */
15250     alignTo : function(element, position, offsets){
15251         this.xy = this.el.getAlignToXY(element, position, offsets);
15252         if(this.isVisible()){
15253             this.el.setXY(this.xy);
15254             this.adjustAssets();
15255         }
15256         return this;
15257     },
15258
15259     /**
15260      * Anchors an element to another element and realigns it when the window is resized.
15261      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15262      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15263      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15264      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15265      * is a number, it is used as the buffer delay (defaults to 50ms).
15266      * @return {Roo.BasicDialog} this
15267      */
15268     anchorTo : function(el, alignment, offsets, monitorScroll){
15269         var action = function(){
15270             this.alignTo(el, alignment, offsets);
15271         };
15272         Roo.EventManager.onWindowResize(action, this);
15273         var tm = typeof monitorScroll;
15274         if(tm != 'undefined'){
15275             Roo.EventManager.on(window, 'scroll', action, this,
15276                 {buffer: tm == 'number' ? monitorScroll : 50});
15277         }
15278         action.call(this);
15279         return this;
15280     },
15281
15282     /**
15283      * Returns true if the dialog is visible
15284      * @return {Boolean}
15285      */
15286     isVisible : function(){
15287         return this.el.isVisible();
15288     },
15289
15290     // private
15291     animHide : function(callback){
15292         var b = Roo.get(this.animateTarget).getBox();
15293         this.proxy.show();
15294         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15295         this.el.hide();
15296         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15297                     this.hideEl.createDelegate(this, [callback]));
15298     },
15299
15300     /**
15301      * Hides the dialog.
15302      * @param {Function} callback (optional) Function to call when the dialog is hidden
15303      * @return {Roo.BasicDialog} this
15304      */
15305     hide : function(callback){
15306         if (this.fireEvent("beforehide", this) === false){
15307             return;
15308         }
15309         if(this.shadow){
15310             this.shadow.hide();
15311         }
15312         if(this.shim) {
15313           this.shim.hide();
15314         }
15315         // sometimes animateTarget seems to get set.. causing problems...
15316         // this just double checks..
15317         if(this.animateTarget && Roo.get(this.animateTarget)) {
15318            this.animHide(callback);
15319         }else{
15320             this.el.hide();
15321             this.hideEl(callback);
15322         }
15323         return this;
15324     },
15325
15326     // private
15327     hideEl : function(callback){
15328         this.proxy.hide();
15329         if(this.modal){
15330             this.mask.hide();
15331             Roo.get(document.body).removeClass("x-body-masked");
15332         }
15333         this.fireEvent("hide", this);
15334         if(typeof callback == "function"){
15335             callback();
15336         }
15337     },
15338
15339     // private
15340     hideAction : function(){
15341         this.setLeft("-10000px");
15342         this.setTop("-10000px");
15343         this.setStyle("visibility", "hidden");
15344     },
15345
15346     // private
15347     refreshSize : function(){
15348         this.size = this.el.getSize();
15349         this.xy = this.el.getXY();
15350         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15351     },
15352
15353     // private
15354     // z-index is managed by the DialogManager and may be overwritten at any time
15355     setZIndex : function(index){
15356         if(this.modal){
15357             this.mask.setStyle("z-index", index);
15358         }
15359         if(this.shim){
15360             this.shim.setStyle("z-index", ++index);
15361         }
15362         if(this.shadow){
15363             this.shadow.setZIndex(++index);
15364         }
15365         this.el.setStyle("z-index", ++index);
15366         if(this.proxy){
15367             this.proxy.setStyle("z-index", ++index);
15368         }
15369         if(this.resizer){
15370             this.resizer.proxy.setStyle("z-index", ++index);
15371         }
15372
15373         this.lastZIndex = index;
15374     },
15375
15376     /**
15377      * Returns the element for this dialog
15378      * @return {Roo.Element} The underlying dialog Element
15379      */
15380     getEl : function(){
15381         return this.el;
15382     }
15383 });
15384
15385 /**
15386  * @class Roo.DialogManager
15387  * Provides global access to BasicDialogs that have been created and
15388  * support for z-indexing (layering) multiple open dialogs.
15389  */
15390 Roo.DialogManager = function(){
15391     var list = {};
15392     var accessList = [];
15393     var front = null;
15394
15395     // private
15396     var sortDialogs = function(d1, d2){
15397         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15398     };
15399
15400     // private
15401     var orderDialogs = function(){
15402         accessList.sort(sortDialogs);
15403         var seed = Roo.DialogManager.zseed;
15404         for(var i = 0, len = accessList.length; i < len; i++){
15405             var dlg = accessList[i];
15406             if(dlg){
15407                 dlg.setZIndex(seed + (i*10));
15408             }
15409         }
15410     };
15411
15412     return {
15413         /**
15414          * The starting z-index for BasicDialogs (defaults to 9000)
15415          * @type Number The z-index value
15416          */
15417         zseed : 9000,
15418
15419         // private
15420         register : function(dlg){
15421             list[dlg.id] = dlg;
15422             accessList.push(dlg);
15423         },
15424
15425         // private
15426         unregister : function(dlg){
15427             delete list[dlg.id];
15428             var i=0;
15429             var len=0;
15430             if(!accessList.indexOf){
15431                 for(  i = 0, len = accessList.length; i < len; i++){
15432                     if(accessList[i] == dlg){
15433                         accessList.splice(i, 1);
15434                         return;
15435                     }
15436                 }
15437             }else{
15438                  i = accessList.indexOf(dlg);
15439                 if(i != -1){
15440                     accessList.splice(i, 1);
15441                 }
15442             }
15443         },
15444
15445         /**
15446          * Gets a registered dialog by id
15447          * @param {String/Object} id The id of the dialog or a dialog
15448          * @return {Roo.BasicDialog} this
15449          */
15450         get : function(id){
15451             return typeof id == "object" ? id : list[id];
15452         },
15453
15454         /**
15455          * Brings the specified dialog to the front
15456          * @param {String/Object} dlg The id of the dialog or a dialog
15457          * @return {Roo.BasicDialog} this
15458          */
15459         bringToFront : function(dlg){
15460             dlg = this.get(dlg);
15461             if(dlg != front){
15462                 front = dlg;
15463                 dlg._lastAccess = new Date().getTime();
15464                 orderDialogs();
15465             }
15466             return dlg;
15467         },
15468
15469         /**
15470          * Sends the specified dialog to the back
15471          * @param {String/Object} dlg The id of the dialog or a dialog
15472          * @return {Roo.BasicDialog} this
15473          */
15474         sendToBack : function(dlg){
15475             dlg = this.get(dlg);
15476             dlg._lastAccess = -(new Date().getTime());
15477             orderDialogs();
15478             return dlg;
15479         },
15480
15481         /**
15482          * Hides all dialogs
15483          */
15484         hideAll : function(){
15485             for(var id in list){
15486                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15487                     list[id].hide();
15488                 }
15489             }
15490         }
15491     };
15492 }();
15493
15494 /**
15495  * @class Roo.LayoutDialog
15496  * @extends Roo.BasicDialog
15497  * Dialog which provides adjustments for working with a layout in a Dialog.
15498  * Add your necessary layout config options to the dialog's config.<br>
15499  * Example usage (including a nested layout):
15500  * <pre><code>
15501 if(!dialog){
15502     dialog = new Roo.LayoutDialog("download-dlg", {
15503         modal: true,
15504         width:600,
15505         height:450,
15506         shadow:true,
15507         minWidth:500,
15508         minHeight:350,
15509         autoTabs:true,
15510         proxyDrag:true,
15511         // layout config merges with the dialog config
15512         center:{
15513             tabPosition: "top",
15514             alwaysShowTabs: true
15515         }
15516     });
15517     dialog.addKeyListener(27, dialog.hide, dialog);
15518     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15519     dialog.addButton("Build It!", this.getDownload, this);
15520
15521     // we can even add nested layouts
15522     var innerLayout = new Roo.BorderLayout("dl-inner", {
15523         east: {
15524             initialSize: 200,
15525             autoScroll:true,
15526             split:true
15527         },
15528         center: {
15529             autoScroll:true
15530         }
15531     });
15532     innerLayout.beginUpdate();
15533     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15534     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15535     innerLayout.endUpdate(true);
15536
15537     var layout = dialog.getLayout();
15538     layout.beginUpdate();
15539     layout.add("center", new Roo.ContentPanel("standard-panel",
15540                         {title: "Download the Source", fitToFrame:true}));
15541     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15542                {title: "Build your own roo.js"}));
15543     layout.getRegion("center").showPanel(sp);
15544     layout.endUpdate();
15545 }
15546 </code></pre>
15547     * @constructor
15548     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15549     * @param {Object} config configuration options
15550   */
15551 Roo.LayoutDialog = function(el, cfg){
15552     
15553     var config=  cfg;
15554     if (typeof(cfg) == 'undefined') {
15555         config = Roo.apply({}, el);
15556         // not sure why we use documentElement here.. - it should always be body.
15557         // IE7 borks horribly if we use documentElement.
15558         // webkit also does not like documentElement - it creates a body element...
15559         el = Roo.get( document.body || document.documentElement ).createChild();
15560         //config.autoCreate = true;
15561     }
15562     
15563     
15564     config.autoTabs = false;
15565     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15566     this.body.setStyle({overflow:"hidden", position:"relative"});
15567     this.layout = new Roo.BorderLayout(this.body.dom, config);
15568     this.layout.monitorWindowResize = false;
15569     this.el.addClass("x-dlg-auto-layout");
15570     // fix case when center region overwrites center function
15571     this.center = Roo.BasicDialog.prototype.center;
15572     this.on("show", this.layout.layout, this.layout, true);
15573     if (config.items) {
15574         var xitems = config.items;
15575         delete config.items;
15576         Roo.each(xitems, this.addxtype, this);
15577     }
15578     
15579     
15580 };
15581 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15582     /**
15583      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15584      * @deprecated
15585      */
15586     endUpdate : function(){
15587         this.layout.endUpdate();
15588     },
15589
15590     /**
15591      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15592      *  @deprecated
15593      */
15594     beginUpdate : function(){
15595         this.layout.beginUpdate();
15596     },
15597
15598     /**
15599      * Get the BorderLayout for this dialog
15600      * @return {Roo.BorderLayout}
15601      */
15602     getLayout : function(){
15603         return this.layout;
15604     },
15605
15606     showEl : function(){
15607         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15608         if(Roo.isIE7){
15609             this.layout.layout();
15610         }
15611     },
15612
15613     // private
15614     // Use the syncHeightBeforeShow config option to control this automatically
15615     syncBodyHeight : function(){
15616         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15617         if(this.layout){this.layout.layout();}
15618     },
15619     
15620       /**
15621      * Add an xtype element (actually adds to the layout.)
15622      * @return {Object} xdata xtype object data.
15623      */
15624     
15625     addxtype : function(c) {
15626         return this.layout.addxtype(c);
15627     }
15628 });/*
15629  * Based on:
15630  * Ext JS Library 1.1.1
15631  * Copyright(c) 2006-2007, Ext JS, LLC.
15632  *
15633  * Originally Released Under LGPL - original licence link has changed is not relivant.
15634  *
15635  * Fork - LGPL
15636  * <script type="text/javascript">
15637  */
15638  
15639 /**
15640  * @class Roo.MessageBox
15641  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15642  * Example usage:
15643  *<pre><code>
15644 // Basic alert:
15645 Roo.Msg.alert('Status', 'Changes saved successfully.');
15646
15647 // Prompt for user data:
15648 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15649     if (btn == 'ok'){
15650         // process text value...
15651     }
15652 });
15653
15654 // Show a dialog using config options:
15655 Roo.Msg.show({
15656    title:'Save Changes?',
15657    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15658    buttons: Roo.Msg.YESNOCANCEL,
15659    fn: processResult,
15660    animEl: 'elId'
15661 });
15662 </code></pre>
15663  * @singleton
15664  */
15665 Roo.MessageBox = function(){
15666     var dlg, opt, mask, waitTimer;
15667     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15668     var buttons, activeTextEl, bwidth;
15669
15670     // private
15671     var handleButton = function(button){
15672         dlg.hide();
15673         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15674     };
15675
15676     // private
15677     var handleHide = function(){
15678         if(opt && opt.cls){
15679             dlg.el.removeClass(opt.cls);
15680         }
15681         if(waitTimer){
15682             Roo.TaskMgr.stop(waitTimer);
15683             waitTimer = null;
15684         }
15685     };
15686
15687     // private
15688     var updateButtons = function(b){
15689         var width = 0;
15690         if(!b){
15691             buttons["ok"].hide();
15692             buttons["cancel"].hide();
15693             buttons["yes"].hide();
15694             buttons["no"].hide();
15695             dlg.footer.dom.style.display = 'none';
15696             return width;
15697         }
15698         dlg.footer.dom.style.display = '';
15699         for(var k in buttons){
15700             if(typeof buttons[k] != "function"){
15701                 if(b[k]){
15702                     buttons[k].show();
15703                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15704                     width += buttons[k].el.getWidth()+15;
15705                 }else{
15706                     buttons[k].hide();
15707                 }
15708             }
15709         }
15710         return width;
15711     };
15712
15713     // private
15714     var handleEsc = function(d, k, e){
15715         if(opt && opt.closable !== false){
15716             dlg.hide();
15717         }
15718         if(e){
15719             e.stopEvent();
15720         }
15721     };
15722
15723     return {
15724         /**
15725          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15726          * @return {Roo.BasicDialog} The BasicDialog element
15727          */
15728         getDialog : function(){
15729            if(!dlg){
15730                 dlg = new Roo.BasicDialog("x-msg-box", {
15731                     autoCreate : true,
15732                     shadow: true,
15733                     draggable: true,
15734                     resizable:false,
15735                     constraintoviewport:false,
15736                     fixedcenter:true,
15737                     collapsible : false,
15738                     shim:true,
15739                     modal: true,
15740                     width:400, height:100,
15741                     buttonAlign:"center",
15742                     closeClick : function(){
15743                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15744                             handleButton("no");
15745                         }else{
15746                             handleButton("cancel");
15747                         }
15748                     }
15749                 });
15750                 dlg.on("hide", handleHide);
15751                 mask = dlg.mask;
15752                 dlg.addKeyListener(27, handleEsc);
15753                 buttons = {};
15754                 var bt = this.buttonText;
15755                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15756                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15757                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15758                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15759                 bodyEl = dlg.body.createChild({
15760
15761                     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>'
15762                 });
15763                 msgEl = bodyEl.dom.firstChild;
15764                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15765                 textboxEl.enableDisplayMode();
15766                 textboxEl.addKeyListener([10,13], function(){
15767                     if(dlg.isVisible() && opt && opt.buttons){
15768                         if(opt.buttons.ok){
15769                             handleButton("ok");
15770                         }else if(opt.buttons.yes){
15771                             handleButton("yes");
15772                         }
15773                     }
15774                 });
15775                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15776                 textareaEl.enableDisplayMode();
15777                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15778                 progressEl.enableDisplayMode();
15779                 var pf = progressEl.dom.firstChild;
15780                 if (pf) {
15781                     pp = Roo.get(pf.firstChild);
15782                     pp.setHeight(pf.offsetHeight);
15783                 }
15784                 
15785             }
15786             return dlg;
15787         },
15788
15789         /**
15790          * Updates the message box body text
15791          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15792          * the XHTML-compliant non-breaking space character '&amp;#160;')
15793          * @return {Roo.MessageBox} This message box
15794          */
15795         updateText : function(text){
15796             if(!dlg.isVisible() && !opt.width){
15797                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15798             }
15799             msgEl.innerHTML = text || '&#160;';
15800       
15801             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15802             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15803             var w = Math.max(
15804                     Math.min(opt.width || cw , this.maxWidth), 
15805                     Math.max(opt.minWidth || this.minWidth, bwidth)
15806             );
15807             if(opt.prompt){
15808                 activeTextEl.setWidth(w);
15809             }
15810             if(dlg.isVisible()){
15811                 dlg.fixedcenter = false;
15812             }
15813             // to big, make it scroll. = But as usual stupid IE does not support
15814             // !important..
15815             
15816             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15817                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15818                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15819             } else {
15820                 bodyEl.dom.style.height = '';
15821                 bodyEl.dom.style.overflowY = '';
15822             }
15823             if (cw > w) {
15824                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15825             } else {
15826                 bodyEl.dom.style.overflowX = '';
15827             }
15828             
15829             dlg.setContentSize(w, bodyEl.getHeight());
15830             if(dlg.isVisible()){
15831                 dlg.fixedcenter = true;
15832             }
15833             return this;
15834         },
15835
15836         /**
15837          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15838          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15839          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15840          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15841          * @return {Roo.MessageBox} This message box
15842          */
15843         updateProgress : function(value, text){
15844             if(text){
15845                 this.updateText(text);
15846             }
15847             if (pp) { // weird bug on my firefox - for some reason this is not defined
15848                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15849             }
15850             return this;
15851         },        
15852
15853         /**
15854          * Returns true if the message box is currently displayed
15855          * @return {Boolean} True if the message box is visible, else false
15856          */
15857         isVisible : function(){
15858             return dlg && dlg.isVisible();  
15859         },
15860
15861         /**
15862          * Hides the message box if it is displayed
15863          */
15864         hide : function(){
15865             if(this.isVisible()){
15866                 dlg.hide();
15867             }  
15868         },
15869
15870         /**
15871          * Displays a new message box, or reinitializes an existing message box, based on the config options
15872          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15873          * The following config object properties are supported:
15874          * <pre>
15875 Property    Type             Description
15876 ----------  ---------------  ------------------------------------------------------------------------------------
15877 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15878                                    closes (defaults to undefined)
15879 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15880                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15881 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15882                                    progress and wait dialogs will ignore this property and always hide the
15883                                    close button as they can only be closed programmatically.
15884 cls               String           A custom CSS class to apply to the message box element
15885 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15886                                    displayed (defaults to 75)
15887 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15888                                    function will be btn (the name of the button that was clicked, if applicable,
15889                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15890                                    Progress and wait dialogs will ignore this option since they do not respond to
15891                                    user actions and can only be closed programmatically, so any required function
15892                                    should be called by the same code after it closes the dialog.
15893 icon              String           A CSS class that provides a background image to be used as an icon for
15894                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15895 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15896 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15897 modal             Boolean          False to allow user interaction with the page while the message box is
15898                                    displayed (defaults to true)
15899 msg               String           A string that will replace the existing message box body text (defaults
15900                                    to the XHTML-compliant non-breaking space character '&#160;')
15901 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15902 progress          Boolean          True to display a progress bar (defaults to false)
15903 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15904 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15905 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15906 title             String           The title text
15907 value             String           The string value to set into the active textbox element if displayed
15908 wait              Boolean          True to display a progress bar (defaults to false)
15909 width             Number           The width of the dialog in pixels
15910 </pre>
15911          *
15912          * Example usage:
15913          * <pre><code>
15914 Roo.Msg.show({
15915    title: 'Address',
15916    msg: 'Please enter your address:',
15917    width: 300,
15918    buttons: Roo.MessageBox.OKCANCEL,
15919    multiline: true,
15920    fn: saveAddress,
15921    animEl: 'addAddressBtn'
15922 });
15923 </code></pre>
15924          * @param {Object} config Configuration options
15925          * @return {Roo.MessageBox} This message box
15926          */
15927         show : function(options)
15928         {
15929             
15930             // this causes nightmares if you show one dialog after another
15931             // especially on callbacks..
15932              
15933             if(this.isVisible()){
15934                 
15935                 this.hide();
15936                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15937                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15938                 Roo.log("New Dialog Message:" +  options.msg )
15939                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15940                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15941                 
15942             }
15943             var d = this.getDialog();
15944             opt = options;
15945             d.setTitle(opt.title || "&#160;");
15946             d.close.setDisplayed(opt.closable !== false);
15947             activeTextEl = textboxEl;
15948             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15949             if(opt.prompt){
15950                 if(opt.multiline){
15951                     textboxEl.hide();
15952                     textareaEl.show();
15953                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15954                         opt.multiline : this.defaultTextHeight);
15955                     activeTextEl = textareaEl;
15956                 }else{
15957                     textboxEl.show();
15958                     textareaEl.hide();
15959                 }
15960             }else{
15961                 textboxEl.hide();
15962                 textareaEl.hide();
15963             }
15964             progressEl.setDisplayed(opt.progress === true);
15965             this.updateProgress(0);
15966             activeTextEl.dom.value = opt.value || "";
15967             if(opt.prompt){
15968                 dlg.setDefaultButton(activeTextEl);
15969             }else{
15970                 var bs = opt.buttons;
15971                 var db = null;
15972                 if(bs && bs.ok){
15973                     db = buttons["ok"];
15974                 }else if(bs && bs.yes){
15975                     db = buttons["yes"];
15976                 }
15977                 dlg.setDefaultButton(db);
15978             }
15979             bwidth = updateButtons(opt.buttons);
15980             this.updateText(opt.msg);
15981             if(opt.cls){
15982                 d.el.addClass(opt.cls);
15983             }
15984             d.proxyDrag = opt.proxyDrag === true;
15985             d.modal = opt.modal !== false;
15986             d.mask = opt.modal !== false ? mask : false;
15987             if(!d.isVisible()){
15988                 // force it to the end of the z-index stack so it gets a cursor in FF
15989                 document.body.appendChild(dlg.el.dom);
15990                 d.animateTarget = null;
15991                 d.show(options.animEl);
15992             }
15993             return this;
15994         },
15995
15996         /**
15997          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15998          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15999          * and closing the message box when the process is complete.
16000          * @param {String} title The title bar text
16001          * @param {String} msg The message box body text
16002          * @return {Roo.MessageBox} This message box
16003          */
16004         progress : function(title, msg){
16005             this.show({
16006                 title : title,
16007                 msg : msg,
16008                 buttons: false,
16009                 progress:true,
16010                 closable:false,
16011                 minWidth: this.minProgressWidth,
16012                 modal : true
16013             });
16014             return this;
16015         },
16016
16017         /**
16018          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16019          * If a callback function is passed it will be called after the user clicks the button, and the
16020          * id of the button that was clicked will be passed as the only parameter to the callback
16021          * (could also be the top-right close button).
16022          * @param {String} title The title bar text
16023          * @param {String} msg The message box body text
16024          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16025          * @param {Object} scope (optional) The scope of the callback function
16026          * @return {Roo.MessageBox} This message box
16027          */
16028         alert : function(title, msg, fn, scope){
16029             this.show({
16030                 title : title,
16031                 msg : msg,
16032                 buttons: this.OK,
16033                 fn: fn,
16034                 scope : scope,
16035                 modal : true
16036             });
16037             return this;
16038         },
16039
16040         /**
16041          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
16042          * interaction while waiting for a long-running process to complete that does not have defined intervals.
16043          * You are responsible for closing the message box when the process is complete.
16044          * @param {String} msg The message box body text
16045          * @param {String} title (optional) The title bar text
16046          * @return {Roo.MessageBox} This message box
16047          */
16048         wait : function(msg, title){
16049             this.show({
16050                 title : title,
16051                 msg : msg,
16052                 buttons: false,
16053                 closable:false,
16054                 progress:true,
16055                 modal:true,
16056                 width:300,
16057                 wait:true
16058             });
16059             waitTimer = Roo.TaskMgr.start({
16060                 run: function(i){
16061                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16062                 },
16063                 interval: 1000
16064             });
16065             return this;
16066         },
16067
16068         /**
16069          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16070          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16071          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16072          * @param {String} title The title bar text
16073          * @param {String} msg The message box body text
16074          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16075          * @param {Object} scope (optional) The scope of the callback function
16076          * @return {Roo.MessageBox} This message box
16077          */
16078         confirm : function(title, msg, fn, scope){
16079             this.show({
16080                 title : title,
16081                 msg : msg,
16082                 buttons: this.YESNO,
16083                 fn: fn,
16084                 scope : scope,
16085                 modal : true
16086             });
16087             return this;
16088         },
16089
16090         /**
16091          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16092          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16093          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16094          * (could also be the top-right close button) and the text that was entered will be passed as the two
16095          * parameters to the callback.
16096          * @param {String} title The title bar text
16097          * @param {String} msg The message box body text
16098          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16099          * @param {Object} scope (optional) The scope of the callback function
16100          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16101          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16102          * @return {Roo.MessageBox} This message box
16103          */
16104         prompt : function(title, msg, fn, scope, multiline){
16105             this.show({
16106                 title : title,
16107                 msg : msg,
16108                 buttons: this.OKCANCEL,
16109                 fn: fn,
16110                 minWidth:250,
16111                 scope : scope,
16112                 prompt:true,
16113                 multiline: multiline,
16114                 modal : true
16115             });
16116             return this;
16117         },
16118
16119         /**
16120          * Button config that displays a single OK button
16121          * @type Object
16122          */
16123         OK : {ok:true},
16124         /**
16125          * Button config that displays Yes and No buttons
16126          * @type Object
16127          */
16128         YESNO : {yes:true, no:true},
16129         /**
16130          * Button config that displays OK and Cancel buttons
16131          * @type Object
16132          */
16133         OKCANCEL : {ok:true, cancel:true},
16134         /**
16135          * Button config that displays Yes, No and Cancel buttons
16136          * @type Object
16137          */
16138         YESNOCANCEL : {yes:true, no:true, cancel:true},
16139
16140         /**
16141          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16142          * @type Number
16143          */
16144         defaultTextHeight : 75,
16145         /**
16146          * The maximum width in pixels of the message box (defaults to 600)
16147          * @type Number
16148          */
16149         maxWidth : 600,
16150         /**
16151          * The minimum width in pixels of the message box (defaults to 100)
16152          * @type Number
16153          */
16154         minWidth : 100,
16155         /**
16156          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16157          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16158          * @type Number
16159          */
16160         minProgressWidth : 250,
16161         /**
16162          * An object containing the default button text strings that can be overriden for localized language support.
16163          * Supported properties are: ok, cancel, yes and no.
16164          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16165          * @type Object
16166          */
16167         buttonText : {
16168             ok : "OK",
16169             cancel : "Cancel",
16170             yes : "Yes",
16171             no : "No"
16172         }
16173     };
16174 }();
16175
16176 /**
16177  * Shorthand for {@link Roo.MessageBox}
16178  */
16179 Roo.Msg = Roo.MessageBox;/*
16180  * Based on:
16181  * Ext JS Library 1.1.1
16182  * Copyright(c) 2006-2007, Ext JS, LLC.
16183  *
16184  * Originally Released Under LGPL - original licence link has changed is not relivant.
16185  *
16186  * Fork - LGPL
16187  * <script type="text/javascript">
16188  */
16189 /**
16190  * @class Roo.QuickTips
16191  * Provides attractive and customizable tooltips for any element.
16192  * @singleton
16193  */
16194 Roo.QuickTips = function(){
16195     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16196     var ce, bd, xy, dd;
16197     var visible = false, disabled = true, inited = false;
16198     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16199     
16200     var onOver = function(e){
16201         if(disabled){
16202             return;
16203         }
16204         var t = e.getTarget();
16205         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16206             return;
16207         }
16208         if(ce && t == ce.el){
16209             clearTimeout(hideProc);
16210             return;
16211         }
16212         if(t && tagEls[t.id]){
16213             tagEls[t.id].el = t;
16214             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16215             return;
16216         }
16217         var ttp, et = Roo.fly(t);
16218         var ns = cfg.namespace;
16219         if(tm.interceptTitles && t.title){
16220             ttp = t.title;
16221             t.qtip = ttp;
16222             t.removeAttribute("title");
16223             e.preventDefault();
16224         }else{
16225             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16226         }
16227         if(ttp){
16228             showProc = show.defer(tm.showDelay, tm, [{
16229                 el: t, 
16230                 text: ttp, 
16231                 width: et.getAttributeNS(ns, cfg.width),
16232                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16233                 title: et.getAttributeNS(ns, cfg.title),
16234                     cls: et.getAttributeNS(ns, cfg.cls)
16235             }]);
16236         }
16237     };
16238     
16239     var onOut = function(e){
16240         clearTimeout(showProc);
16241         var t = e.getTarget();
16242         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16243             hideProc = setTimeout(hide, tm.hideDelay);
16244         }
16245     };
16246     
16247     var onMove = function(e){
16248         if(disabled){
16249             return;
16250         }
16251         xy = e.getXY();
16252         xy[1] += 18;
16253         if(tm.trackMouse && ce){
16254             el.setXY(xy);
16255         }
16256     };
16257     
16258     var onDown = function(e){
16259         clearTimeout(showProc);
16260         clearTimeout(hideProc);
16261         if(!e.within(el)){
16262             if(tm.hideOnClick){
16263                 hide();
16264                 tm.disable();
16265                 tm.enable.defer(100, tm);
16266             }
16267         }
16268     };
16269     
16270     var getPad = function(){
16271         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16272     };
16273
16274     var show = function(o){
16275         if(disabled){
16276             return;
16277         }
16278         clearTimeout(dismissProc);
16279         ce = o;
16280         if(removeCls){ // in case manually hidden
16281             el.removeClass(removeCls);
16282             removeCls = null;
16283         }
16284         if(ce.cls){
16285             el.addClass(ce.cls);
16286             removeCls = ce.cls;
16287         }
16288         if(ce.title){
16289             tipTitle.update(ce.title);
16290             tipTitle.show();
16291         }else{
16292             tipTitle.update('');
16293             tipTitle.hide();
16294         }
16295         el.dom.style.width  = tm.maxWidth+'px';
16296         //tipBody.dom.style.width = '';
16297         tipBodyText.update(o.text);
16298         var p = getPad(), w = ce.width;
16299         if(!w){
16300             var td = tipBodyText.dom;
16301             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16302             if(aw > tm.maxWidth){
16303                 w = tm.maxWidth;
16304             }else if(aw < tm.minWidth){
16305                 w = tm.minWidth;
16306             }else{
16307                 w = aw;
16308             }
16309         }
16310         //tipBody.setWidth(w);
16311         el.setWidth(parseInt(w, 10) + p);
16312         if(ce.autoHide === false){
16313             close.setDisplayed(true);
16314             if(dd){
16315                 dd.unlock();
16316             }
16317         }else{
16318             close.setDisplayed(false);
16319             if(dd){
16320                 dd.lock();
16321             }
16322         }
16323         if(xy){
16324             el.avoidY = xy[1]-18;
16325             el.setXY(xy);
16326         }
16327         if(tm.animate){
16328             el.setOpacity(.1);
16329             el.setStyle("visibility", "visible");
16330             el.fadeIn({callback: afterShow});
16331         }else{
16332             afterShow();
16333         }
16334     };
16335     
16336     var afterShow = function(){
16337         if(ce){
16338             el.show();
16339             esc.enable();
16340             if(tm.autoDismiss && ce.autoHide !== false){
16341                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16342             }
16343         }
16344     };
16345     
16346     var hide = function(noanim){
16347         clearTimeout(dismissProc);
16348         clearTimeout(hideProc);
16349         ce = null;
16350         if(el.isVisible()){
16351             esc.disable();
16352             if(noanim !== true && tm.animate){
16353                 el.fadeOut({callback: afterHide});
16354             }else{
16355                 afterHide();
16356             } 
16357         }
16358     };
16359     
16360     var afterHide = function(){
16361         el.hide();
16362         if(removeCls){
16363             el.removeClass(removeCls);
16364             removeCls = null;
16365         }
16366     };
16367     
16368     return {
16369         /**
16370         * @cfg {Number} minWidth
16371         * The minimum width of the quick tip (defaults to 40)
16372         */
16373        minWidth : 40,
16374         /**
16375         * @cfg {Number} maxWidth
16376         * The maximum width of the quick tip (defaults to 300)
16377         */
16378        maxWidth : 300,
16379         /**
16380         * @cfg {Boolean} interceptTitles
16381         * True to automatically use the element's DOM title value if available (defaults to false)
16382         */
16383        interceptTitles : false,
16384         /**
16385         * @cfg {Boolean} trackMouse
16386         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16387         */
16388        trackMouse : false,
16389         /**
16390         * @cfg {Boolean} hideOnClick
16391         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16392         */
16393        hideOnClick : true,
16394         /**
16395         * @cfg {Number} showDelay
16396         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16397         */
16398        showDelay : 500,
16399         /**
16400         * @cfg {Number} hideDelay
16401         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16402         */
16403        hideDelay : 200,
16404         /**
16405         * @cfg {Boolean} autoHide
16406         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16407         * Used in conjunction with hideDelay.
16408         */
16409        autoHide : true,
16410         /**
16411         * @cfg {Boolean}
16412         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16413         * (defaults to true).  Used in conjunction with autoDismissDelay.
16414         */
16415        autoDismiss : true,
16416         /**
16417         * @cfg {Number}
16418         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16419         */
16420        autoDismissDelay : 5000,
16421        /**
16422         * @cfg {Boolean} animate
16423         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16424         */
16425        animate : false,
16426
16427        /**
16428         * @cfg {String} title
16429         * Title text to display (defaults to '').  This can be any valid HTML markup.
16430         */
16431         title: '',
16432        /**
16433         * @cfg {String} text
16434         * Body text to display (defaults to '').  This can be any valid HTML markup.
16435         */
16436         text : '',
16437        /**
16438         * @cfg {String} cls
16439         * A CSS class to apply to the base quick tip element (defaults to '').
16440         */
16441         cls : '',
16442        /**
16443         * @cfg {Number} width
16444         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16445         * minWidth or maxWidth.
16446         */
16447         width : null,
16448
16449     /**
16450      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16451      * or display QuickTips in a page.
16452      */
16453        init : function(){
16454           tm = Roo.QuickTips;
16455           cfg = tm.tagConfig;
16456           if(!inited){
16457               if(!Roo.isReady){ // allow calling of init() before onReady
16458                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16459                   return;
16460               }
16461               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16462               el.fxDefaults = {stopFx: true};
16463               // maximum custom styling
16464               //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>');
16465               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>');              
16466               tipTitle = el.child('h3');
16467               tipTitle.enableDisplayMode("block");
16468               tipBody = el.child('div.x-tip-bd');
16469               tipBodyText = el.child('div.x-tip-bd-inner');
16470               //bdLeft = el.child('div.x-tip-bd-left');
16471               //bdRight = el.child('div.x-tip-bd-right');
16472               close = el.child('div.x-tip-close');
16473               close.enableDisplayMode("block");
16474               close.on("click", hide);
16475               var d = Roo.get(document);
16476               d.on("mousedown", onDown);
16477               d.on("mouseover", onOver);
16478               d.on("mouseout", onOut);
16479               d.on("mousemove", onMove);
16480               esc = d.addKeyListener(27, hide);
16481               esc.disable();
16482               if(Roo.dd.DD){
16483                   dd = el.initDD("default", null, {
16484                       onDrag : function(){
16485                           el.sync();  
16486                       }
16487                   });
16488                   dd.setHandleElId(tipTitle.id);
16489                   dd.lock();
16490               }
16491               inited = true;
16492           }
16493           this.enable(); 
16494        },
16495
16496     /**
16497      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16498      * are supported:
16499      * <pre>
16500 Property    Type                   Description
16501 ----------  ---------------------  ------------------------------------------------------------------------
16502 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16503      * </ul>
16504      * @param {Object} config The config object
16505      */
16506        register : function(config){
16507            var cs = config instanceof Array ? config : arguments;
16508            for(var i = 0, len = cs.length; i < len; i++) {
16509                var c = cs[i];
16510                var target = c.target;
16511                if(target){
16512                    if(target instanceof Array){
16513                        for(var j = 0, jlen = target.length; j < jlen; j++){
16514                            tagEls[target[j]] = c;
16515                        }
16516                    }else{
16517                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16518                    }
16519                }
16520            }
16521        },
16522
16523     /**
16524      * Removes this quick tip from its element and destroys it.
16525      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16526      */
16527        unregister : function(el){
16528            delete tagEls[Roo.id(el)];
16529        },
16530
16531     /**
16532      * Enable this quick tip.
16533      */
16534        enable : function(){
16535            if(inited && disabled){
16536                locks.pop();
16537                if(locks.length < 1){
16538                    disabled = false;
16539                }
16540            }
16541        },
16542
16543     /**
16544      * Disable this quick tip.
16545      */
16546        disable : function(){
16547           disabled = true;
16548           clearTimeout(showProc);
16549           clearTimeout(hideProc);
16550           clearTimeout(dismissProc);
16551           if(ce){
16552               hide(true);
16553           }
16554           locks.push(1);
16555        },
16556
16557     /**
16558      * Returns true if the quick tip is enabled, else false.
16559      */
16560        isEnabled : function(){
16561             return !disabled;
16562        },
16563
16564         // private
16565        tagConfig : {
16566            namespace : "ext",
16567            attribute : "qtip",
16568            width : "width",
16569            target : "target",
16570            title : "qtitle",
16571            hide : "hide",
16572            cls : "qclass"
16573        }
16574    };
16575 }();
16576
16577 // backwards compat
16578 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16579  * Based on:
16580  * Ext JS Library 1.1.1
16581  * Copyright(c) 2006-2007, Ext JS, LLC.
16582  *
16583  * Originally Released Under LGPL - original licence link has changed is not relivant.
16584  *
16585  * Fork - LGPL
16586  * <script type="text/javascript">
16587  */
16588  
16589
16590 /**
16591  * @class Roo.tree.TreePanel
16592  * @extends Roo.data.Tree
16593
16594  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16595  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16596  * @cfg {Boolean} enableDD true to enable drag and drop
16597  * @cfg {Boolean} enableDrag true to enable just drag
16598  * @cfg {Boolean} enableDrop true to enable just drop
16599  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16600  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16601  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16602  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16603  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16604  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16605  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16606  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16607  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16608  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16609  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16610  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16611  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16612  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16613  * @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>
16614  * @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>
16615  * 
16616  * @constructor
16617  * @param {String/HTMLElement/Element} el The container element
16618  * @param {Object} config
16619  */
16620 Roo.tree.TreePanel = function(el, config){
16621     var root = false;
16622     var loader = false;
16623     if (config.root) {
16624         root = config.root;
16625         delete config.root;
16626     }
16627     if (config.loader) {
16628         loader = config.loader;
16629         delete config.loader;
16630     }
16631     
16632     Roo.apply(this, config);
16633     Roo.tree.TreePanel.superclass.constructor.call(this);
16634     this.el = Roo.get(el);
16635     this.el.addClass('x-tree');
16636     //console.log(root);
16637     if (root) {
16638         this.setRootNode( Roo.factory(root, Roo.tree));
16639     }
16640     if (loader) {
16641         this.loader = Roo.factory(loader, Roo.tree);
16642     }
16643    /**
16644     * Read-only. The id of the container element becomes this TreePanel's id.
16645     */
16646     this.id = this.el.id;
16647     this.addEvents({
16648         /**
16649         * @event beforeload
16650         * Fires before a node is loaded, return false to cancel
16651         * @param {Node} node The node being loaded
16652         */
16653         "beforeload" : true,
16654         /**
16655         * @event load
16656         * Fires when a node is loaded
16657         * @param {Node} node The node that was loaded
16658         */
16659         "load" : true,
16660         /**
16661         * @event textchange
16662         * Fires when the text for a node is changed
16663         * @param {Node} node The node
16664         * @param {String} text The new text
16665         * @param {String} oldText The old text
16666         */
16667         "textchange" : true,
16668         /**
16669         * @event beforeexpand
16670         * Fires before a node is expanded, return false to cancel.
16671         * @param {Node} node The node
16672         * @param {Boolean} deep
16673         * @param {Boolean} anim
16674         */
16675         "beforeexpand" : true,
16676         /**
16677         * @event beforecollapse
16678         * Fires before a node is collapsed, return false to cancel.
16679         * @param {Node} node The node
16680         * @param {Boolean} deep
16681         * @param {Boolean} anim
16682         */
16683         "beforecollapse" : true,
16684         /**
16685         * @event expand
16686         * Fires when a node is expanded
16687         * @param {Node} node The node
16688         */
16689         "expand" : true,
16690         /**
16691         * @event disabledchange
16692         * Fires when the disabled status of a node changes
16693         * @param {Node} node The node
16694         * @param {Boolean} disabled
16695         */
16696         "disabledchange" : true,
16697         /**
16698         * @event collapse
16699         * Fires when a node is collapsed
16700         * @param {Node} node The node
16701         */
16702         "collapse" : true,
16703         /**
16704         * @event beforeclick
16705         * Fires before click processing on a node. Return false to cancel the default action.
16706         * @param {Node} node The node
16707         * @param {Roo.EventObject} e The event object
16708         */
16709         "beforeclick":true,
16710         /**
16711         * @event checkchange
16712         * Fires when a node with a checkbox's checked property changes
16713         * @param {Node} this This node
16714         * @param {Boolean} checked
16715         */
16716         "checkchange":true,
16717         /**
16718         * @event click
16719         * Fires when a node is clicked
16720         * @param {Node} node The node
16721         * @param {Roo.EventObject} e The event object
16722         */
16723         "click":true,
16724         /**
16725         * @event dblclick
16726         * Fires when a node is double clicked
16727         * @param {Node} node The node
16728         * @param {Roo.EventObject} e The event object
16729         */
16730         "dblclick":true,
16731         /**
16732         * @event contextmenu
16733         * Fires when a node is right clicked
16734         * @param {Node} node The node
16735         * @param {Roo.EventObject} e The event object
16736         */
16737         "contextmenu":true,
16738         /**
16739         * @event beforechildrenrendered
16740         * Fires right before the child nodes for a node are rendered
16741         * @param {Node} node The node
16742         */
16743         "beforechildrenrendered":true,
16744         /**
16745         * @event startdrag
16746         * Fires when a node starts being dragged
16747         * @param {Roo.tree.TreePanel} this
16748         * @param {Roo.tree.TreeNode} node
16749         * @param {event} e The raw browser event
16750         */ 
16751        "startdrag" : true,
16752        /**
16753         * @event enddrag
16754         * Fires when a drag operation is complete
16755         * @param {Roo.tree.TreePanel} this
16756         * @param {Roo.tree.TreeNode} node
16757         * @param {event} e The raw browser event
16758         */
16759        "enddrag" : true,
16760        /**
16761         * @event dragdrop
16762         * Fires when a dragged node is dropped on a valid DD target
16763         * @param {Roo.tree.TreePanel} this
16764         * @param {Roo.tree.TreeNode} node
16765         * @param {DD} dd The dd it was dropped on
16766         * @param {event} e The raw browser event
16767         */
16768        "dragdrop" : true,
16769        /**
16770         * @event beforenodedrop
16771         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16772         * passed to handlers has the following properties:<br />
16773         * <ul style="padding:5px;padding-left:16px;">
16774         * <li>tree - The TreePanel</li>
16775         * <li>target - The node being targeted for the drop</li>
16776         * <li>data - The drag data from the drag source</li>
16777         * <li>point - The point of the drop - append, above or below</li>
16778         * <li>source - The drag source</li>
16779         * <li>rawEvent - Raw mouse event</li>
16780         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16781         * to be inserted by setting them on this object.</li>
16782         * <li>cancel - Set this to true to cancel the drop.</li>
16783         * </ul>
16784         * @param {Object} dropEvent
16785         */
16786        "beforenodedrop" : true,
16787        /**
16788         * @event nodedrop
16789         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16790         * passed to handlers has the following properties:<br />
16791         * <ul style="padding:5px;padding-left:16px;">
16792         * <li>tree - The TreePanel</li>
16793         * <li>target - The node being targeted for the drop</li>
16794         * <li>data - The drag data from the drag source</li>
16795         * <li>point - The point of the drop - append, above or below</li>
16796         * <li>source - The drag source</li>
16797         * <li>rawEvent - Raw mouse event</li>
16798         * <li>dropNode - Dropped node(s).</li>
16799         * </ul>
16800         * @param {Object} dropEvent
16801         */
16802        "nodedrop" : true,
16803         /**
16804         * @event nodedragover
16805         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16806         * passed to handlers has the following properties:<br />
16807         * <ul style="padding:5px;padding-left:16px;">
16808         * <li>tree - The TreePanel</li>
16809         * <li>target - The node being targeted for the drop</li>
16810         * <li>data - The drag data from the drag source</li>
16811         * <li>point - The point of the drop - append, above or below</li>
16812         * <li>source - The drag source</li>
16813         * <li>rawEvent - Raw mouse event</li>
16814         * <li>dropNode - Drop node(s) provided by the source.</li>
16815         * <li>cancel - Set this to true to signal drop not allowed.</li>
16816         * </ul>
16817         * @param {Object} dragOverEvent
16818         */
16819        "nodedragover" : true
16820         
16821     });
16822     if(this.singleExpand){
16823        this.on("beforeexpand", this.restrictExpand, this);
16824     }
16825     if (this.editor) {
16826         this.editor.tree = this;
16827         this.editor = Roo.factory(this.editor, Roo.tree);
16828     }
16829     
16830     if (this.selModel) {
16831         this.selModel = Roo.factory(this.selModel, Roo.tree);
16832     }
16833    
16834 };
16835 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16836     rootVisible : true,
16837     animate: Roo.enableFx,
16838     lines : true,
16839     enableDD : false,
16840     hlDrop : Roo.enableFx,
16841   
16842     renderer: false,
16843     
16844     rendererTip: false,
16845     // private
16846     restrictExpand : function(node){
16847         var p = node.parentNode;
16848         if(p){
16849             if(p.expandedChild && p.expandedChild.parentNode == p){
16850                 p.expandedChild.collapse();
16851             }
16852             p.expandedChild = node;
16853         }
16854     },
16855
16856     // private override
16857     setRootNode : function(node){
16858         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16859         if(!this.rootVisible){
16860             node.ui = new Roo.tree.RootTreeNodeUI(node);
16861         }
16862         return node;
16863     },
16864
16865     /**
16866      * Returns the container element for this TreePanel
16867      */
16868     getEl : function(){
16869         return this.el;
16870     },
16871
16872     /**
16873      * Returns the default TreeLoader for this TreePanel
16874      */
16875     getLoader : function(){
16876         return this.loader;
16877     },
16878
16879     /**
16880      * Expand all nodes
16881      */
16882     expandAll : function(){
16883         this.root.expand(true);
16884     },
16885
16886     /**
16887      * Collapse all nodes
16888      */
16889     collapseAll : function(){
16890         this.root.collapse(true);
16891     },
16892
16893     /**
16894      * Returns the selection model used by this TreePanel
16895      */
16896     getSelectionModel : function(){
16897         if(!this.selModel){
16898             this.selModel = new Roo.tree.DefaultSelectionModel();
16899         }
16900         return this.selModel;
16901     },
16902
16903     /**
16904      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16905      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16906      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16907      * @return {Array}
16908      */
16909     getChecked : function(a, startNode){
16910         startNode = startNode || this.root;
16911         var r = [];
16912         var f = function(){
16913             if(this.attributes.checked){
16914                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16915             }
16916         }
16917         startNode.cascade(f);
16918         return r;
16919     },
16920
16921     /**
16922      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16923      * @param {String} path
16924      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16925      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16926      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16927      */
16928     expandPath : function(path, attr, callback){
16929         attr = attr || "id";
16930         var keys = path.split(this.pathSeparator);
16931         var curNode = this.root;
16932         if(curNode.attributes[attr] != keys[1]){ // invalid root
16933             if(callback){
16934                 callback(false, null);
16935             }
16936             return;
16937         }
16938         var index = 1;
16939         var f = function(){
16940             if(++index == keys.length){
16941                 if(callback){
16942                     callback(true, curNode);
16943                 }
16944                 return;
16945             }
16946             var c = curNode.findChild(attr, keys[index]);
16947             if(!c){
16948                 if(callback){
16949                     callback(false, curNode);
16950                 }
16951                 return;
16952             }
16953             curNode = c;
16954             c.expand(false, false, f);
16955         };
16956         curNode.expand(false, false, f);
16957     },
16958
16959     /**
16960      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16961      * @param {String} path
16962      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16963      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16964      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16965      */
16966     selectPath : function(path, attr, callback){
16967         attr = attr || "id";
16968         var keys = path.split(this.pathSeparator);
16969         var v = keys.pop();
16970         if(keys.length > 0){
16971             var f = function(success, node){
16972                 if(success && node){
16973                     var n = node.findChild(attr, v);
16974                     if(n){
16975                         n.select();
16976                         if(callback){
16977                             callback(true, n);
16978                         }
16979                     }else if(callback){
16980                         callback(false, n);
16981                     }
16982                 }else{
16983                     if(callback){
16984                         callback(false, n);
16985                     }
16986                 }
16987             };
16988             this.expandPath(keys.join(this.pathSeparator), attr, f);
16989         }else{
16990             this.root.select();
16991             if(callback){
16992                 callback(true, this.root);
16993             }
16994         }
16995     },
16996
16997     getTreeEl : function(){
16998         return this.el;
16999     },
17000
17001     /**
17002      * Trigger rendering of this TreePanel
17003      */
17004     render : function(){
17005         if (this.innerCt) {
17006             return this; // stop it rendering more than once!!
17007         }
17008         
17009         this.innerCt = this.el.createChild({tag:"ul",
17010                cls:"x-tree-root-ct " +
17011                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
17012
17013         if(this.containerScroll){
17014             Roo.dd.ScrollManager.register(this.el);
17015         }
17016         if((this.enableDD || this.enableDrop) && !this.dropZone){
17017            /**
17018             * The dropZone used by this tree if drop is enabled
17019             * @type Roo.tree.TreeDropZone
17020             */
17021              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17022                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17023            });
17024         }
17025         if((this.enableDD || this.enableDrag) && !this.dragZone){
17026            /**
17027             * The dragZone used by this tree if drag is enabled
17028             * @type Roo.tree.TreeDragZone
17029             */
17030             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17031                ddGroup: this.ddGroup || "TreeDD",
17032                scroll: this.ddScroll
17033            });
17034         }
17035         this.getSelectionModel().init(this);
17036         if (!this.root) {
17037             Roo.log("ROOT not set in tree");
17038             return this;
17039         }
17040         this.root.render();
17041         if(!this.rootVisible){
17042             this.root.renderChildren();
17043         }
17044         return this;
17045     }
17046 });/*
17047  * Based on:
17048  * Ext JS Library 1.1.1
17049  * Copyright(c) 2006-2007, Ext JS, LLC.
17050  *
17051  * Originally Released Under LGPL - original licence link has changed is not relivant.
17052  *
17053  * Fork - LGPL
17054  * <script type="text/javascript">
17055  */
17056  
17057
17058 /**
17059  * @class Roo.tree.DefaultSelectionModel
17060  * @extends Roo.util.Observable
17061  * The default single selection for a TreePanel.
17062  * @param {Object} cfg Configuration
17063  */
17064 Roo.tree.DefaultSelectionModel = function(cfg){
17065    this.selNode = null;
17066    
17067    
17068    
17069    this.addEvents({
17070        /**
17071         * @event selectionchange
17072         * Fires when the selected node changes
17073         * @param {DefaultSelectionModel} this
17074         * @param {TreeNode} node the new selection
17075         */
17076        "selectionchange" : true,
17077
17078        /**
17079         * @event beforeselect
17080         * Fires before the selected node changes, return false to cancel the change
17081         * @param {DefaultSelectionModel} this
17082         * @param {TreeNode} node the new selection
17083         * @param {TreeNode} node the old selection
17084         */
17085        "beforeselect" : true
17086    });
17087    
17088     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17089 };
17090
17091 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17092     init : function(tree){
17093         this.tree = tree;
17094         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17095         tree.on("click", this.onNodeClick, this);
17096     },
17097     
17098     onNodeClick : function(node, e){
17099         if (e.ctrlKey && this.selNode == node)  {
17100             this.unselect(node);
17101             return;
17102         }
17103         this.select(node);
17104     },
17105     
17106     /**
17107      * Select a node.
17108      * @param {TreeNode} node The node to select
17109      * @return {TreeNode} The selected node
17110      */
17111     select : function(node){
17112         var last = this.selNode;
17113         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17114             if(last){
17115                 last.ui.onSelectedChange(false);
17116             }
17117             this.selNode = node;
17118             node.ui.onSelectedChange(true);
17119             this.fireEvent("selectionchange", this, node, last);
17120         }
17121         return node;
17122     },
17123     
17124     /**
17125      * Deselect a node.
17126      * @param {TreeNode} node The node to unselect
17127      */
17128     unselect : function(node){
17129         if(this.selNode == node){
17130             this.clearSelections();
17131         }    
17132     },
17133     
17134     /**
17135      * Clear all selections
17136      */
17137     clearSelections : function(){
17138         var n = this.selNode;
17139         if(n){
17140             n.ui.onSelectedChange(false);
17141             this.selNode = null;
17142             this.fireEvent("selectionchange", this, null);
17143         }
17144         return n;
17145     },
17146     
17147     /**
17148      * Get the selected node
17149      * @return {TreeNode} The selected node
17150      */
17151     getSelectedNode : function(){
17152         return this.selNode;    
17153     },
17154     
17155     /**
17156      * Returns true if the node is selected
17157      * @param {TreeNode} node The node to check
17158      * @return {Boolean}
17159      */
17160     isSelected : function(node){
17161         return this.selNode == node;  
17162     },
17163
17164     /**
17165      * Selects the node above the selected node in the tree, intelligently walking the nodes
17166      * @return TreeNode The new selection
17167      */
17168     selectPrevious : function(){
17169         var s = this.selNode || this.lastSelNode;
17170         if(!s){
17171             return null;
17172         }
17173         var ps = s.previousSibling;
17174         if(ps){
17175             if(!ps.isExpanded() || ps.childNodes.length < 1){
17176                 return this.select(ps);
17177             } else{
17178                 var lc = ps.lastChild;
17179                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17180                     lc = lc.lastChild;
17181                 }
17182                 return this.select(lc);
17183             }
17184         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17185             return this.select(s.parentNode);
17186         }
17187         return null;
17188     },
17189
17190     /**
17191      * Selects the node above the selected node in the tree, intelligently walking the nodes
17192      * @return TreeNode The new selection
17193      */
17194     selectNext : function(){
17195         var s = this.selNode || this.lastSelNode;
17196         if(!s){
17197             return null;
17198         }
17199         if(s.firstChild && s.isExpanded()){
17200              return this.select(s.firstChild);
17201          }else if(s.nextSibling){
17202              return this.select(s.nextSibling);
17203          }else if(s.parentNode){
17204             var newS = null;
17205             s.parentNode.bubble(function(){
17206                 if(this.nextSibling){
17207                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17208                     return false;
17209                 }
17210             });
17211             return newS;
17212          }
17213         return null;
17214     },
17215
17216     onKeyDown : function(e){
17217         var s = this.selNode || this.lastSelNode;
17218         // undesirable, but required
17219         var sm = this;
17220         if(!s){
17221             return;
17222         }
17223         var k = e.getKey();
17224         switch(k){
17225              case e.DOWN:
17226                  e.stopEvent();
17227                  this.selectNext();
17228              break;
17229              case e.UP:
17230                  e.stopEvent();
17231                  this.selectPrevious();
17232              break;
17233              case e.RIGHT:
17234                  e.preventDefault();
17235                  if(s.hasChildNodes()){
17236                      if(!s.isExpanded()){
17237                          s.expand();
17238                      }else if(s.firstChild){
17239                          this.select(s.firstChild, e);
17240                      }
17241                  }
17242              break;
17243              case e.LEFT:
17244                  e.preventDefault();
17245                  if(s.hasChildNodes() && s.isExpanded()){
17246                      s.collapse();
17247                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17248                      this.select(s.parentNode, e);
17249                  }
17250              break;
17251         };
17252     }
17253 });
17254
17255 /**
17256  * @class Roo.tree.MultiSelectionModel
17257  * @extends Roo.util.Observable
17258  * Multi selection for a TreePanel.
17259  * @param {Object} cfg Configuration
17260  */
17261 Roo.tree.MultiSelectionModel = function(){
17262    this.selNodes = [];
17263    this.selMap = {};
17264    this.addEvents({
17265        /**
17266         * @event selectionchange
17267         * Fires when the selected nodes change
17268         * @param {MultiSelectionModel} this
17269         * @param {Array} nodes Array of the selected nodes
17270         */
17271        "selectionchange" : true
17272    });
17273    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17274    
17275 };
17276
17277 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17278     init : function(tree){
17279         this.tree = tree;
17280         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17281         tree.on("click", this.onNodeClick, this);
17282     },
17283     
17284     onNodeClick : function(node, e){
17285         this.select(node, e, e.ctrlKey);
17286     },
17287     
17288     /**
17289      * Select a node.
17290      * @param {TreeNode} node The node to select
17291      * @param {EventObject} e (optional) An event associated with the selection
17292      * @param {Boolean} keepExisting True to retain existing selections
17293      * @return {TreeNode} The selected node
17294      */
17295     select : function(node, e, keepExisting){
17296         if(keepExisting !== true){
17297             this.clearSelections(true);
17298         }
17299         if(this.isSelected(node)){
17300             this.lastSelNode = node;
17301             return node;
17302         }
17303         this.selNodes.push(node);
17304         this.selMap[node.id] = node;
17305         this.lastSelNode = node;
17306         node.ui.onSelectedChange(true);
17307         this.fireEvent("selectionchange", this, this.selNodes);
17308         return node;
17309     },
17310     
17311     /**
17312      * Deselect a node.
17313      * @param {TreeNode} node The node to unselect
17314      */
17315     unselect : function(node){
17316         if(this.selMap[node.id]){
17317             node.ui.onSelectedChange(false);
17318             var sn = this.selNodes;
17319             var index = -1;
17320             if(sn.indexOf){
17321                 index = sn.indexOf(node);
17322             }else{
17323                 for(var i = 0, len = sn.length; i < len; i++){
17324                     if(sn[i] == node){
17325                         index = i;
17326                         break;
17327                     }
17328                 }
17329             }
17330             if(index != -1){
17331                 this.selNodes.splice(index, 1);
17332             }
17333             delete this.selMap[node.id];
17334             this.fireEvent("selectionchange", this, this.selNodes);
17335         }
17336     },
17337     
17338     /**
17339      * Clear all selections
17340      */
17341     clearSelections : function(suppressEvent){
17342         var sn = this.selNodes;
17343         if(sn.length > 0){
17344             for(var i = 0, len = sn.length; i < len; i++){
17345                 sn[i].ui.onSelectedChange(false);
17346             }
17347             this.selNodes = [];
17348             this.selMap = {};
17349             if(suppressEvent !== true){
17350                 this.fireEvent("selectionchange", this, this.selNodes);
17351             }
17352         }
17353     },
17354     
17355     /**
17356      * Returns true if the node is selected
17357      * @param {TreeNode} node The node to check
17358      * @return {Boolean}
17359      */
17360     isSelected : function(node){
17361         return this.selMap[node.id] ? true : false;  
17362     },
17363     
17364     /**
17365      * Returns an array of the selected nodes
17366      * @return {Array}
17367      */
17368     getSelectedNodes : function(){
17369         return this.selNodes;    
17370     },
17371
17372     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17373
17374     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17375
17376     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17377 });/*
17378  * Based on:
17379  * Ext JS Library 1.1.1
17380  * Copyright(c) 2006-2007, Ext JS, LLC.
17381  *
17382  * Originally Released Under LGPL - original licence link has changed is not relivant.
17383  *
17384  * Fork - LGPL
17385  * <script type="text/javascript">
17386  */
17387  
17388 /**
17389  * @class Roo.tree.TreeNode
17390  * @extends Roo.data.Node
17391  * @cfg {String} text The text for this node
17392  * @cfg {Boolean} expanded true to start the node expanded
17393  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17394  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17395  * @cfg {Boolean} disabled true to start the node disabled
17396  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17397  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17398  * @cfg {String} cls A css class to be added to the node
17399  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17400  * @cfg {String} href URL of the link used for the node (defaults to #)
17401  * @cfg {String} hrefTarget target frame for the link
17402  * @cfg {String} qtip An Ext QuickTip for the node
17403  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17404  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17405  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17406  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17407  * (defaults to undefined with no checkbox rendered)
17408  * @constructor
17409  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17410  */
17411 Roo.tree.TreeNode = function(attributes){
17412     attributes = attributes || {};
17413     if(typeof attributes == "string"){
17414         attributes = {text: attributes};
17415     }
17416     this.childrenRendered = false;
17417     this.rendered = false;
17418     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17419     this.expanded = attributes.expanded === true;
17420     this.isTarget = attributes.isTarget !== false;
17421     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17422     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17423
17424     /**
17425      * Read-only. The text for this node. To change it use setText().
17426      * @type String
17427      */
17428     this.text = attributes.text;
17429     /**
17430      * True if this node is disabled.
17431      * @type Boolean
17432      */
17433     this.disabled = attributes.disabled === true;
17434
17435     this.addEvents({
17436         /**
17437         * @event textchange
17438         * Fires when the text for this node is changed
17439         * @param {Node} this This node
17440         * @param {String} text The new text
17441         * @param {String} oldText The old text
17442         */
17443         "textchange" : true,
17444         /**
17445         * @event beforeexpand
17446         * Fires before this node is expanded, return false to cancel.
17447         * @param {Node} this This node
17448         * @param {Boolean} deep
17449         * @param {Boolean} anim
17450         */
17451         "beforeexpand" : true,
17452         /**
17453         * @event beforecollapse
17454         * Fires before this node is collapsed, return false to cancel.
17455         * @param {Node} this This node
17456         * @param {Boolean} deep
17457         * @param {Boolean} anim
17458         */
17459         "beforecollapse" : true,
17460         /**
17461         * @event expand
17462         * Fires when this node is expanded
17463         * @param {Node} this This node
17464         */
17465         "expand" : true,
17466         /**
17467         * @event disabledchange
17468         * Fires when the disabled status of this node changes
17469         * @param {Node} this This node
17470         * @param {Boolean} disabled
17471         */
17472         "disabledchange" : true,
17473         /**
17474         * @event collapse
17475         * Fires when this node is collapsed
17476         * @param {Node} this This node
17477         */
17478         "collapse" : true,
17479         /**
17480         * @event beforeclick
17481         * Fires before click processing. Return false to cancel the default action.
17482         * @param {Node} this This node
17483         * @param {Roo.EventObject} e The event object
17484         */
17485         "beforeclick":true,
17486         /**
17487         * @event checkchange
17488         * Fires when a node with a checkbox's checked property changes
17489         * @param {Node} this This node
17490         * @param {Boolean} checked
17491         */
17492         "checkchange":true,
17493         /**
17494         * @event click
17495         * Fires when this node is clicked
17496         * @param {Node} this This node
17497         * @param {Roo.EventObject} e The event object
17498         */
17499         "click":true,
17500         /**
17501         * @event dblclick
17502         * Fires when this node is double clicked
17503         * @param {Node} this This node
17504         * @param {Roo.EventObject} e The event object
17505         */
17506         "dblclick":true,
17507         /**
17508         * @event contextmenu
17509         * Fires when this node is right clicked
17510         * @param {Node} this This node
17511         * @param {Roo.EventObject} e The event object
17512         */
17513         "contextmenu":true,
17514         /**
17515         * @event beforechildrenrendered
17516         * Fires right before the child nodes for this node are rendered
17517         * @param {Node} this This node
17518         */
17519         "beforechildrenrendered":true
17520     });
17521
17522     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17523
17524     /**
17525      * Read-only. The UI for this node
17526      * @type TreeNodeUI
17527      */
17528     this.ui = new uiClass(this);
17529     
17530     // finally support items[]
17531     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17532         return;
17533     }
17534     
17535     
17536     Roo.each(this.attributes.items, function(c) {
17537         this.appendChild(Roo.factory(c,Roo.Tree));
17538     }, this);
17539     delete this.attributes.items;
17540     
17541     
17542     
17543 };
17544 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17545     preventHScroll: true,
17546     /**
17547      * Returns true if this node is expanded
17548      * @return {Boolean}
17549      */
17550     isExpanded : function(){
17551         return this.expanded;
17552     },
17553
17554     /**
17555      * Returns the UI object for this node
17556      * @return {TreeNodeUI}
17557      */
17558     getUI : function(){
17559         return this.ui;
17560     },
17561
17562     // private override
17563     setFirstChild : function(node){
17564         var of = this.firstChild;
17565         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17566         if(this.childrenRendered && of && node != of){
17567             of.renderIndent(true, true);
17568         }
17569         if(this.rendered){
17570             this.renderIndent(true, true);
17571         }
17572     },
17573
17574     // private override
17575     setLastChild : function(node){
17576         var ol = this.lastChild;
17577         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17578         if(this.childrenRendered && ol && node != ol){
17579             ol.renderIndent(true, true);
17580         }
17581         if(this.rendered){
17582             this.renderIndent(true, true);
17583         }
17584     },
17585
17586     // these methods are overridden to provide lazy rendering support
17587     // private override
17588     appendChild : function()
17589     {
17590         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17591         if(node && this.childrenRendered){
17592             node.render();
17593         }
17594         this.ui.updateExpandIcon();
17595         return node;
17596     },
17597
17598     // private override
17599     removeChild : function(node){
17600         this.ownerTree.getSelectionModel().unselect(node);
17601         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17602         // if it's been rendered remove dom node
17603         if(this.childrenRendered){
17604             node.ui.remove();
17605         }
17606         if(this.childNodes.length < 1){
17607             this.collapse(false, false);
17608         }else{
17609             this.ui.updateExpandIcon();
17610         }
17611         if(!this.firstChild) {
17612             this.childrenRendered = false;
17613         }
17614         return node;
17615     },
17616
17617     // private override
17618     insertBefore : function(node, refNode){
17619         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17620         if(newNode && refNode && this.childrenRendered){
17621             node.render();
17622         }
17623         this.ui.updateExpandIcon();
17624         return newNode;
17625     },
17626
17627     /**
17628      * Sets the text for this node
17629      * @param {String} text
17630      */
17631     setText : function(text){
17632         var oldText = this.text;
17633         this.text = text;
17634         this.attributes.text = text;
17635         if(this.rendered){ // event without subscribing
17636             this.ui.onTextChange(this, text, oldText);
17637         }
17638         this.fireEvent("textchange", this, text, oldText);
17639     },
17640
17641     /**
17642      * Triggers selection of this node
17643      */
17644     select : function(){
17645         this.getOwnerTree().getSelectionModel().select(this);
17646     },
17647
17648     /**
17649      * Triggers deselection of this node
17650      */
17651     unselect : function(){
17652         this.getOwnerTree().getSelectionModel().unselect(this);
17653     },
17654
17655     /**
17656      * Returns true if this node is selected
17657      * @return {Boolean}
17658      */
17659     isSelected : function(){
17660         return this.getOwnerTree().getSelectionModel().isSelected(this);
17661     },
17662
17663     /**
17664      * Expand this node.
17665      * @param {Boolean} deep (optional) True to expand all children as well
17666      * @param {Boolean} anim (optional) false to cancel the default animation
17667      * @param {Function} callback (optional) A callback to be called when
17668      * expanding this node completes (does not wait for deep expand to complete).
17669      * Called with 1 parameter, this node.
17670      */
17671     expand : function(deep, anim, callback){
17672         if(!this.expanded){
17673             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17674                 return;
17675             }
17676             if(!this.childrenRendered){
17677                 this.renderChildren();
17678             }
17679             this.expanded = true;
17680             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17681                 this.ui.animExpand(function(){
17682                     this.fireEvent("expand", this);
17683                     if(typeof callback == "function"){
17684                         callback(this);
17685                     }
17686                     if(deep === true){
17687                         this.expandChildNodes(true);
17688                     }
17689                 }.createDelegate(this));
17690                 return;
17691             }else{
17692                 this.ui.expand();
17693                 this.fireEvent("expand", this);
17694                 if(typeof callback == "function"){
17695                     callback(this);
17696                 }
17697             }
17698         }else{
17699            if(typeof callback == "function"){
17700                callback(this);
17701            }
17702         }
17703         if(deep === true){
17704             this.expandChildNodes(true);
17705         }
17706     },
17707
17708     isHiddenRoot : function(){
17709         return this.isRoot && !this.getOwnerTree().rootVisible;
17710     },
17711
17712     /**
17713      * Collapse this node.
17714      * @param {Boolean} deep (optional) True to collapse all children as well
17715      * @param {Boolean} anim (optional) false to cancel the default animation
17716      */
17717     collapse : function(deep, anim){
17718         if(this.expanded && !this.isHiddenRoot()){
17719             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17720                 return;
17721             }
17722             this.expanded = false;
17723             if((this.getOwnerTree().animate && anim !== false) || anim){
17724                 this.ui.animCollapse(function(){
17725                     this.fireEvent("collapse", this);
17726                     if(deep === true){
17727                         this.collapseChildNodes(true);
17728                     }
17729                 }.createDelegate(this));
17730                 return;
17731             }else{
17732                 this.ui.collapse();
17733                 this.fireEvent("collapse", this);
17734             }
17735         }
17736         if(deep === true){
17737             var cs = this.childNodes;
17738             for(var i = 0, len = cs.length; i < len; i++) {
17739                 cs[i].collapse(true, false);
17740             }
17741         }
17742     },
17743
17744     // private
17745     delayedExpand : function(delay){
17746         if(!this.expandProcId){
17747             this.expandProcId = this.expand.defer(delay, this);
17748         }
17749     },
17750
17751     // private
17752     cancelExpand : function(){
17753         if(this.expandProcId){
17754             clearTimeout(this.expandProcId);
17755         }
17756         this.expandProcId = false;
17757     },
17758
17759     /**
17760      * Toggles expanded/collapsed state of the node
17761      */
17762     toggle : function(){
17763         if(this.expanded){
17764             this.collapse();
17765         }else{
17766             this.expand();
17767         }
17768     },
17769
17770     /**
17771      * Ensures all parent nodes are expanded
17772      */
17773     ensureVisible : function(callback){
17774         var tree = this.getOwnerTree();
17775         tree.expandPath(this.parentNode.getPath(), false, function(){
17776             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17777             Roo.callback(callback);
17778         }.createDelegate(this));
17779     },
17780
17781     /**
17782      * Expand all child nodes
17783      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17784      */
17785     expandChildNodes : function(deep){
17786         var cs = this.childNodes;
17787         for(var i = 0, len = cs.length; i < len; i++) {
17788                 cs[i].expand(deep);
17789         }
17790     },
17791
17792     /**
17793      * Collapse all child nodes
17794      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17795      */
17796     collapseChildNodes : function(deep){
17797         var cs = this.childNodes;
17798         for(var i = 0, len = cs.length; i < len; i++) {
17799                 cs[i].collapse(deep);
17800         }
17801     },
17802
17803     /**
17804      * Disables this node
17805      */
17806     disable : function(){
17807         this.disabled = true;
17808         this.unselect();
17809         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17810             this.ui.onDisableChange(this, true);
17811         }
17812         this.fireEvent("disabledchange", this, true);
17813     },
17814
17815     /**
17816      * Enables this node
17817      */
17818     enable : function(){
17819         this.disabled = false;
17820         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17821             this.ui.onDisableChange(this, false);
17822         }
17823         this.fireEvent("disabledchange", this, false);
17824     },
17825
17826     // private
17827     renderChildren : function(suppressEvent){
17828         if(suppressEvent !== false){
17829             this.fireEvent("beforechildrenrendered", this);
17830         }
17831         var cs = this.childNodes;
17832         for(var i = 0, len = cs.length; i < len; i++){
17833             cs[i].render(true);
17834         }
17835         this.childrenRendered = true;
17836     },
17837
17838     // private
17839     sort : function(fn, scope){
17840         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17841         if(this.childrenRendered){
17842             var cs = this.childNodes;
17843             for(var i = 0, len = cs.length; i < len; i++){
17844                 cs[i].render(true);
17845             }
17846         }
17847     },
17848
17849     // private
17850     render : function(bulkRender){
17851         this.ui.render(bulkRender);
17852         if(!this.rendered){
17853             this.rendered = true;
17854             if(this.expanded){
17855                 this.expanded = false;
17856                 this.expand(false, false);
17857             }
17858         }
17859     },
17860
17861     // private
17862     renderIndent : function(deep, refresh){
17863         if(refresh){
17864             this.ui.childIndent = null;
17865         }
17866         this.ui.renderIndent();
17867         if(deep === true && this.childrenRendered){
17868             var cs = this.childNodes;
17869             for(var i = 0, len = cs.length; i < len; i++){
17870                 cs[i].renderIndent(true, refresh);
17871             }
17872         }
17873     }
17874 });/*
17875  * Based on:
17876  * Ext JS Library 1.1.1
17877  * Copyright(c) 2006-2007, Ext JS, LLC.
17878  *
17879  * Originally Released Under LGPL - original licence link has changed is not relivant.
17880  *
17881  * Fork - LGPL
17882  * <script type="text/javascript">
17883  */
17884  
17885 /**
17886  * @class Roo.tree.AsyncTreeNode
17887  * @extends Roo.tree.TreeNode
17888  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17889  * @constructor
17890  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17891  */
17892  Roo.tree.AsyncTreeNode = function(config){
17893     this.loaded = false;
17894     this.loading = false;
17895     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17896     /**
17897     * @event beforeload
17898     * Fires before this node is loaded, return false to cancel
17899     * @param {Node} this This node
17900     */
17901     this.addEvents({'beforeload':true, 'load': true});
17902     /**
17903     * @event load
17904     * Fires when this node is loaded
17905     * @param {Node} this This node
17906     */
17907     /**
17908      * The loader used by this node (defaults to using the tree's defined loader)
17909      * @type TreeLoader
17910      * @property loader
17911      */
17912 };
17913 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17914     expand : function(deep, anim, callback){
17915         if(this.loading){ // if an async load is already running, waiting til it's done
17916             var timer;
17917             var f = function(){
17918                 if(!this.loading){ // done loading
17919                     clearInterval(timer);
17920                     this.expand(deep, anim, callback);
17921                 }
17922             }.createDelegate(this);
17923             timer = setInterval(f, 200);
17924             return;
17925         }
17926         if(!this.loaded){
17927             if(this.fireEvent("beforeload", this) === false){
17928                 return;
17929             }
17930             this.loading = true;
17931             this.ui.beforeLoad(this);
17932             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17933             if(loader){
17934                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17935                 return;
17936             }
17937         }
17938         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17939     },
17940     
17941     /**
17942      * Returns true if this node is currently loading
17943      * @return {Boolean}
17944      */
17945     isLoading : function(){
17946         return this.loading;  
17947     },
17948     
17949     loadComplete : function(deep, anim, callback){
17950         this.loading = false;
17951         this.loaded = true;
17952         this.ui.afterLoad(this);
17953         this.fireEvent("load", this);
17954         this.expand(deep, anim, callback);
17955     },
17956     
17957     /**
17958      * Returns true if this node has been loaded
17959      * @return {Boolean}
17960      */
17961     isLoaded : function(){
17962         return this.loaded;
17963     },
17964     
17965     hasChildNodes : function(){
17966         if(!this.isLeaf() && !this.loaded){
17967             return true;
17968         }else{
17969             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17970         }
17971     },
17972
17973     /**
17974      * Trigger a reload for this node
17975      * @param {Function} callback
17976      */
17977     reload : function(callback){
17978         this.collapse(false, false);
17979         while(this.firstChild){
17980             this.removeChild(this.firstChild);
17981         }
17982         this.childrenRendered = false;
17983         this.loaded = false;
17984         if(this.isHiddenRoot()){
17985             this.expanded = false;
17986         }
17987         this.expand(false, false, callback);
17988     }
17989 });/*
17990  * Based on:
17991  * Ext JS Library 1.1.1
17992  * Copyright(c) 2006-2007, Ext JS, LLC.
17993  *
17994  * Originally Released Under LGPL - original licence link has changed is not relivant.
17995  *
17996  * Fork - LGPL
17997  * <script type="text/javascript">
17998  */
17999  
18000 /**
18001  * @class Roo.tree.TreeNodeUI
18002  * @constructor
18003  * @param {Object} node The node to render
18004  * The TreeNode UI implementation is separate from the
18005  * tree implementation. Unless you are customizing the tree UI,
18006  * you should never have to use this directly.
18007  */
18008 Roo.tree.TreeNodeUI = function(node){
18009     this.node = node;
18010     this.rendered = false;
18011     this.animating = false;
18012     this.emptyIcon = Roo.BLANK_IMAGE_URL;
18013 };
18014
18015 Roo.tree.TreeNodeUI.prototype = {
18016     removeChild : function(node){
18017         if(this.rendered){
18018             this.ctNode.removeChild(node.ui.getEl());
18019         }
18020     },
18021
18022     beforeLoad : function(){
18023          this.addClass("x-tree-node-loading");
18024     },
18025
18026     afterLoad : function(){
18027          this.removeClass("x-tree-node-loading");
18028     },
18029
18030     onTextChange : function(node, text, oldText){
18031         if(this.rendered){
18032             this.textNode.innerHTML = text;
18033         }
18034     },
18035
18036     onDisableChange : function(node, state){
18037         this.disabled = state;
18038         if(state){
18039             this.addClass("x-tree-node-disabled");
18040         }else{
18041             this.removeClass("x-tree-node-disabled");
18042         }
18043     },
18044
18045     onSelectedChange : function(state){
18046         if(state){
18047             this.focus();
18048             this.addClass("x-tree-selected");
18049         }else{
18050             //this.blur();
18051             this.removeClass("x-tree-selected");
18052         }
18053     },
18054
18055     onMove : function(tree, node, oldParent, newParent, index, refNode){
18056         this.childIndent = null;
18057         if(this.rendered){
18058             var targetNode = newParent.ui.getContainer();
18059             if(!targetNode){//target not rendered
18060                 this.holder = document.createElement("div");
18061                 this.holder.appendChild(this.wrap);
18062                 return;
18063             }
18064             var insertBefore = refNode ? refNode.ui.getEl() : null;
18065             if(insertBefore){
18066                 targetNode.insertBefore(this.wrap, insertBefore);
18067             }else{
18068                 targetNode.appendChild(this.wrap);
18069             }
18070             this.node.renderIndent(true);
18071         }
18072     },
18073
18074     addClass : function(cls){
18075         if(this.elNode){
18076             Roo.fly(this.elNode).addClass(cls);
18077         }
18078     },
18079
18080     removeClass : function(cls){
18081         if(this.elNode){
18082             Roo.fly(this.elNode).removeClass(cls);
18083         }
18084     },
18085
18086     remove : function(){
18087         if(this.rendered){
18088             this.holder = document.createElement("div");
18089             this.holder.appendChild(this.wrap);
18090         }
18091     },
18092
18093     fireEvent : function(){
18094         return this.node.fireEvent.apply(this.node, arguments);
18095     },
18096
18097     initEvents : function(){
18098         this.node.on("move", this.onMove, this);
18099         var E = Roo.EventManager;
18100         var a = this.anchor;
18101
18102         var el = Roo.fly(a, '_treeui');
18103
18104         if(Roo.isOpera){ // opera render bug ignores the CSS
18105             el.setStyle("text-decoration", "none");
18106         }
18107
18108         el.on("click", this.onClick, this);
18109         el.on("dblclick", this.onDblClick, this);
18110
18111         if(this.checkbox){
18112             Roo.EventManager.on(this.checkbox,
18113                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18114         }
18115
18116         el.on("contextmenu", this.onContextMenu, this);
18117
18118         var icon = Roo.fly(this.iconNode);
18119         icon.on("click", this.onClick, this);
18120         icon.on("dblclick", this.onDblClick, this);
18121         icon.on("contextmenu", this.onContextMenu, this);
18122         E.on(this.ecNode, "click", this.ecClick, this, true);
18123
18124         if(this.node.disabled){
18125             this.addClass("x-tree-node-disabled");
18126         }
18127         if(this.node.hidden){
18128             this.addClass("x-tree-node-disabled");
18129         }
18130         var ot = this.node.getOwnerTree();
18131         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18132         if(dd && (!this.node.isRoot || ot.rootVisible)){
18133             Roo.dd.Registry.register(this.elNode, {
18134                 node: this.node,
18135                 handles: this.getDDHandles(),
18136                 isHandle: false
18137             });
18138         }
18139     },
18140
18141     getDDHandles : function(){
18142         return [this.iconNode, this.textNode];
18143     },
18144
18145     hide : function(){
18146         if(this.rendered){
18147             this.wrap.style.display = "none";
18148         }
18149     },
18150
18151     show : function(){
18152         if(this.rendered){
18153             this.wrap.style.display = "";
18154         }
18155     },
18156
18157     onContextMenu : function(e){
18158         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18159             e.preventDefault();
18160             this.focus();
18161             this.fireEvent("contextmenu", this.node, e);
18162         }
18163     },
18164
18165     onClick : function(e){
18166         if(this.dropping){
18167             e.stopEvent();
18168             return;
18169         }
18170         if(this.fireEvent("beforeclick", this.node, e) !== false){
18171             if(!this.disabled && this.node.attributes.href){
18172                 this.fireEvent("click", this.node, e);
18173                 return;
18174             }
18175             e.preventDefault();
18176             if(this.disabled){
18177                 return;
18178             }
18179
18180             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18181                 this.node.toggle();
18182             }
18183
18184             this.fireEvent("click", this.node, e);
18185         }else{
18186             e.stopEvent();
18187         }
18188     },
18189
18190     onDblClick : function(e){
18191         e.preventDefault();
18192         if(this.disabled){
18193             return;
18194         }
18195         if(this.checkbox){
18196             this.toggleCheck();
18197         }
18198         if(!this.animating && this.node.hasChildNodes()){
18199             this.node.toggle();
18200         }
18201         this.fireEvent("dblclick", this.node, e);
18202     },
18203
18204     onCheckChange : function(){
18205         var checked = this.checkbox.checked;
18206         this.node.attributes.checked = checked;
18207         this.fireEvent('checkchange', this.node, checked);
18208     },
18209
18210     ecClick : function(e){
18211         if(!this.animating && this.node.hasChildNodes()){
18212             this.node.toggle();
18213         }
18214     },
18215
18216     startDrop : function(){
18217         this.dropping = true;
18218     },
18219
18220     // delayed drop so the click event doesn't get fired on a drop
18221     endDrop : function(){
18222        setTimeout(function(){
18223            this.dropping = false;
18224        }.createDelegate(this), 50);
18225     },
18226
18227     expand : function(){
18228         this.updateExpandIcon();
18229         this.ctNode.style.display = "";
18230     },
18231
18232     focus : function(){
18233         if(!this.node.preventHScroll){
18234             try{this.anchor.focus();
18235             }catch(e){}
18236         }else if(!Roo.isIE){
18237             try{
18238                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18239                 var l = noscroll.scrollLeft;
18240                 this.anchor.focus();
18241                 noscroll.scrollLeft = l;
18242             }catch(e){}
18243         }
18244     },
18245
18246     toggleCheck : function(value){
18247         var cb = this.checkbox;
18248         if(cb){
18249             cb.checked = (value === undefined ? !cb.checked : value);
18250         }
18251     },
18252
18253     blur : function(){
18254         try{
18255             this.anchor.blur();
18256         }catch(e){}
18257     },
18258
18259     animExpand : function(callback){
18260         var ct = Roo.get(this.ctNode);
18261         ct.stopFx();
18262         if(!this.node.hasChildNodes()){
18263             this.updateExpandIcon();
18264             this.ctNode.style.display = "";
18265             Roo.callback(callback);
18266             return;
18267         }
18268         this.animating = true;
18269         this.updateExpandIcon();
18270
18271         ct.slideIn('t', {
18272            callback : function(){
18273                this.animating = false;
18274                Roo.callback(callback);
18275             },
18276             scope: this,
18277             duration: this.node.ownerTree.duration || .25
18278         });
18279     },
18280
18281     highlight : function(){
18282         var tree = this.node.getOwnerTree();
18283         Roo.fly(this.wrap).highlight(
18284             tree.hlColor || "C3DAF9",
18285             {endColor: tree.hlBaseColor}
18286         );
18287     },
18288
18289     collapse : function(){
18290         this.updateExpandIcon();
18291         this.ctNode.style.display = "none";
18292     },
18293
18294     animCollapse : function(callback){
18295         var ct = Roo.get(this.ctNode);
18296         ct.enableDisplayMode('block');
18297         ct.stopFx();
18298
18299         this.animating = true;
18300         this.updateExpandIcon();
18301
18302         ct.slideOut('t', {
18303             callback : function(){
18304                this.animating = false;
18305                Roo.callback(callback);
18306             },
18307             scope: this,
18308             duration: this.node.ownerTree.duration || .25
18309         });
18310     },
18311
18312     getContainer : function(){
18313         return this.ctNode;
18314     },
18315
18316     getEl : function(){
18317         return this.wrap;
18318     },
18319
18320     appendDDGhost : function(ghostNode){
18321         ghostNode.appendChild(this.elNode.cloneNode(true));
18322     },
18323
18324     getDDRepairXY : function(){
18325         return Roo.lib.Dom.getXY(this.iconNode);
18326     },
18327
18328     onRender : function(){
18329         this.render();
18330     },
18331
18332     render : function(bulkRender){
18333         var n = this.node, a = n.attributes;
18334         var targetNode = n.parentNode ?
18335               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18336
18337         if(!this.rendered){
18338             this.rendered = true;
18339
18340             this.renderElements(n, a, targetNode, bulkRender);
18341
18342             if(a.qtip){
18343                if(this.textNode.setAttributeNS){
18344                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18345                    if(a.qtipTitle){
18346                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18347                    }
18348                }else{
18349                    this.textNode.setAttribute("ext:qtip", a.qtip);
18350                    if(a.qtipTitle){
18351                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18352                    }
18353                }
18354             }else if(a.qtipCfg){
18355                 a.qtipCfg.target = Roo.id(this.textNode);
18356                 Roo.QuickTips.register(a.qtipCfg);
18357             }
18358             this.initEvents();
18359             if(!this.node.expanded){
18360                 this.updateExpandIcon();
18361             }
18362         }else{
18363             if(bulkRender === true) {
18364                 targetNode.appendChild(this.wrap);
18365             }
18366         }
18367     },
18368
18369     renderElements : function(n, a, targetNode, bulkRender)
18370     {
18371         // add some indent caching, this helps performance when rendering a large tree
18372         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18373         var t = n.getOwnerTree();
18374         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18375         if (typeof(n.attributes.html) != 'undefined') {
18376             txt = n.attributes.html;
18377         }
18378         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18379         var cb = typeof a.checked == 'boolean';
18380         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18381         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18382             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18383             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18384             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18385             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18386             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18387              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18388                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18389             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18390             "</li>"];
18391
18392         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18393             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18394                                 n.nextSibling.ui.getEl(), buf.join(""));
18395         }else{
18396             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18397         }
18398
18399         this.elNode = this.wrap.childNodes[0];
18400         this.ctNode = this.wrap.childNodes[1];
18401         var cs = this.elNode.childNodes;
18402         this.indentNode = cs[0];
18403         this.ecNode = cs[1];
18404         this.iconNode = cs[2];
18405         var index = 3;
18406         if(cb){
18407             this.checkbox = cs[3];
18408             index++;
18409         }
18410         this.anchor = cs[index];
18411         this.textNode = cs[index].firstChild;
18412     },
18413
18414     getAnchor : function(){
18415         return this.anchor;
18416     },
18417
18418     getTextEl : function(){
18419         return this.textNode;
18420     },
18421
18422     getIconEl : function(){
18423         return this.iconNode;
18424     },
18425
18426     isChecked : function(){
18427         return this.checkbox ? this.checkbox.checked : false;
18428     },
18429
18430     updateExpandIcon : function(){
18431         if(this.rendered){
18432             var n = this.node, c1, c2;
18433             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18434             var hasChild = n.hasChildNodes();
18435             if(hasChild){
18436                 if(n.expanded){
18437                     cls += "-minus";
18438                     c1 = "x-tree-node-collapsed";
18439                     c2 = "x-tree-node-expanded";
18440                 }else{
18441                     cls += "-plus";
18442                     c1 = "x-tree-node-expanded";
18443                     c2 = "x-tree-node-collapsed";
18444                 }
18445                 if(this.wasLeaf){
18446                     this.removeClass("x-tree-node-leaf");
18447                     this.wasLeaf = false;
18448                 }
18449                 if(this.c1 != c1 || this.c2 != c2){
18450                     Roo.fly(this.elNode).replaceClass(c1, c2);
18451                     this.c1 = c1; this.c2 = c2;
18452                 }
18453             }else{
18454                 // this changes non-leafs into leafs if they have no children.
18455                 // it's not very rational behaviour..
18456                 
18457                 if(!this.wasLeaf && this.node.leaf){
18458                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18459                     delete this.c1;
18460                     delete this.c2;
18461                     this.wasLeaf = true;
18462                 }
18463             }
18464             var ecc = "x-tree-ec-icon "+cls;
18465             if(this.ecc != ecc){
18466                 this.ecNode.className = ecc;
18467                 this.ecc = ecc;
18468             }
18469         }
18470     },
18471
18472     getChildIndent : function(){
18473         if(!this.childIndent){
18474             var buf = [];
18475             var p = this.node;
18476             while(p){
18477                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18478                     if(!p.isLast()) {
18479                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18480                     } else {
18481                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18482                     }
18483                 }
18484                 p = p.parentNode;
18485             }
18486             this.childIndent = buf.join("");
18487         }
18488         return this.childIndent;
18489     },
18490
18491     renderIndent : function(){
18492         if(this.rendered){
18493             var indent = "";
18494             var p = this.node.parentNode;
18495             if(p){
18496                 indent = p.ui.getChildIndent();
18497             }
18498             if(this.indentMarkup != indent){ // don't rerender if not required
18499                 this.indentNode.innerHTML = indent;
18500                 this.indentMarkup = indent;
18501             }
18502             this.updateExpandIcon();
18503         }
18504     }
18505 };
18506
18507 Roo.tree.RootTreeNodeUI = function(){
18508     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18509 };
18510 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18511     render : function(){
18512         if(!this.rendered){
18513             var targetNode = this.node.ownerTree.innerCt.dom;
18514             this.node.expanded = true;
18515             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18516             this.wrap = this.ctNode = targetNode.firstChild;
18517         }
18518     },
18519     collapse : function(){
18520     },
18521     expand : function(){
18522     }
18523 });/*
18524  * Based on:
18525  * Ext JS Library 1.1.1
18526  * Copyright(c) 2006-2007, Ext JS, LLC.
18527  *
18528  * Originally Released Under LGPL - original licence link has changed is not relivant.
18529  *
18530  * Fork - LGPL
18531  * <script type="text/javascript">
18532  */
18533 /**
18534  * @class Roo.tree.TreeLoader
18535  * @extends Roo.util.Observable
18536  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18537  * nodes from a specified URL. The response must be a javascript Array definition
18538  * who's elements are node definition objects. eg:
18539  * <pre><code>
18540 {  success : true,
18541    data :      [
18542    
18543     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18544     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18545     ]
18546 }
18547
18548
18549 </code></pre>
18550  * <br><br>
18551  * The old style respose with just an array is still supported, but not recommended.
18552  * <br><br>
18553  *
18554  * A server request is sent, and child nodes are loaded only when a node is expanded.
18555  * The loading node's id is passed to the server under the parameter name "node" to
18556  * enable the server to produce the correct child nodes.
18557  * <br><br>
18558  * To pass extra parameters, an event handler may be attached to the "beforeload"
18559  * event, and the parameters specified in the TreeLoader's baseParams property:
18560  * <pre><code>
18561     myTreeLoader.on("beforeload", function(treeLoader, node) {
18562         this.baseParams.category = node.attributes.category;
18563     }, this);
18564 </code></pre><
18565  * This would pass an HTTP parameter called "category" to the server containing
18566  * the value of the Node's "category" attribute.
18567  * @constructor
18568  * Creates a new Treeloader.
18569  * @param {Object} config A config object containing config properties.
18570  */
18571 Roo.tree.TreeLoader = function(config){
18572     this.baseParams = {};
18573     this.requestMethod = "POST";
18574     Roo.apply(this, config);
18575
18576     this.addEvents({
18577     
18578         /**
18579          * @event beforeload
18580          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18581          * @param {Object} This TreeLoader object.
18582          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18583          * @param {Object} callback The callback function specified in the {@link #load} call.
18584          */
18585         beforeload : true,
18586         /**
18587          * @event load
18588          * Fires when the node has been successfuly loaded.
18589          * @param {Object} This TreeLoader object.
18590          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18591          * @param {Object} response The response object containing the data from the server.
18592          */
18593         load : true,
18594         /**
18595          * @event loadexception
18596          * Fires if the network request failed.
18597          * @param {Object} This TreeLoader object.
18598          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18599          * @param {Object} response The response object containing the data from the server.
18600          */
18601         loadexception : true,
18602         /**
18603          * @event create
18604          * Fires before a node is created, enabling you to return custom Node types 
18605          * @param {Object} This TreeLoader object.
18606          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18607          */
18608         create : true
18609     });
18610
18611     Roo.tree.TreeLoader.superclass.constructor.call(this);
18612 };
18613
18614 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18615     /**
18616     * @cfg {String} dataUrl The URL from which to request a Json string which
18617     * specifies an array of node definition object representing the child nodes
18618     * to be loaded.
18619     */
18620     /**
18621     * @cfg {String} requestMethod either GET or POST
18622     * defaults to POST (due to BC)
18623     * to be loaded.
18624     */
18625     /**
18626     * @cfg {Object} baseParams (optional) An object containing properties which
18627     * specify HTTP parameters to be passed to each request for child nodes.
18628     */
18629     /**
18630     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18631     * created by this loader. If the attributes sent by the server have an attribute in this object,
18632     * they take priority.
18633     */
18634     /**
18635     * @cfg {Object} uiProviders (optional) An object containing properties which
18636     * 
18637     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18638     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18639     * <i>uiProvider</i> attribute of a returned child node is a string rather
18640     * than a reference to a TreeNodeUI implementation, this that string value
18641     * is used as a property name in the uiProviders object. You can define the provider named
18642     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18643     */
18644     uiProviders : {},
18645
18646     /**
18647     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18648     * child nodes before loading.
18649     */
18650     clearOnLoad : true,
18651
18652     /**
18653     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18654     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18655     * Grid query { data : [ .....] }
18656     */
18657     
18658     root : false,
18659      /**
18660     * @cfg {String} queryParam (optional) 
18661     * Name of the query as it will be passed on the querystring (defaults to 'node')
18662     * eg. the request will be ?node=[id]
18663     */
18664     
18665     
18666     queryParam: false,
18667     
18668     /**
18669      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18670      * This is called automatically when a node is expanded, but may be used to reload
18671      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18672      * @param {Roo.tree.TreeNode} node
18673      * @param {Function} callback
18674      */
18675     load : function(node, callback){
18676         if(this.clearOnLoad){
18677             while(node.firstChild){
18678                 node.removeChild(node.firstChild);
18679             }
18680         }
18681         if(node.attributes.children){ // preloaded json children
18682             var cs = node.attributes.children;
18683             for(var i = 0, len = cs.length; i < len; i++){
18684                 node.appendChild(this.createNode(cs[i]));
18685             }
18686             if(typeof callback == "function"){
18687                 callback();
18688             }
18689         }else if(this.dataUrl){
18690             this.requestData(node, callback);
18691         }
18692     },
18693
18694     getParams: function(node){
18695         var buf = [], bp = this.baseParams;
18696         for(var key in bp){
18697             if(typeof bp[key] != "function"){
18698                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18699             }
18700         }
18701         var n = this.queryParam === false ? 'node' : this.queryParam;
18702         buf.push(n + "=", encodeURIComponent(node.id));
18703         return buf.join("");
18704     },
18705
18706     requestData : function(node, callback){
18707         if(this.fireEvent("beforeload", this, node, callback) !== false){
18708             this.transId = Roo.Ajax.request({
18709                 method:this.requestMethod,
18710                 url: this.dataUrl||this.url,
18711                 success: this.handleResponse,
18712                 failure: this.handleFailure,
18713                 scope: this,
18714                 argument: {callback: callback, node: node},
18715                 params: this.getParams(node)
18716             });
18717         }else{
18718             // if the load is cancelled, make sure we notify
18719             // the node that we are done
18720             if(typeof callback == "function"){
18721                 callback();
18722             }
18723         }
18724     },
18725
18726     isLoading : function(){
18727         return this.transId ? true : false;
18728     },
18729
18730     abort : function(){
18731         if(this.isLoading()){
18732             Roo.Ajax.abort(this.transId);
18733         }
18734     },
18735
18736     // private
18737     createNode : function(attr)
18738     {
18739         // apply baseAttrs, nice idea Corey!
18740         if(this.baseAttrs){
18741             Roo.applyIf(attr, this.baseAttrs);
18742         }
18743         if(this.applyLoader !== false){
18744             attr.loader = this;
18745         }
18746         // uiProvider = depreciated..
18747         
18748         if(typeof(attr.uiProvider) == 'string'){
18749            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18750                 /**  eval:var:attr */ eval(attr.uiProvider);
18751         }
18752         if(typeof(this.uiProviders['default']) != 'undefined') {
18753             attr.uiProvider = this.uiProviders['default'];
18754         }
18755         
18756         this.fireEvent('create', this, attr);
18757         
18758         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18759         return(attr.leaf ?
18760                         new Roo.tree.TreeNode(attr) :
18761                         new Roo.tree.AsyncTreeNode(attr));
18762     },
18763
18764     processResponse : function(response, node, callback)
18765     {
18766         var json = response.responseText;
18767         try {
18768             
18769             var o = Roo.decode(json);
18770             
18771             if (this.root === false && typeof(o.success) != undefined) {
18772                 this.root = 'data'; // the default behaviour for list like data..
18773                 }
18774                 
18775             if (this.root !== false &&  !o.success) {
18776                 // it's a failure condition.
18777                 var a = response.argument;
18778                 this.fireEvent("loadexception", this, a.node, response);
18779                 Roo.log("Load failed - should have a handler really");
18780                 return;
18781             }
18782             
18783             
18784             
18785             if (this.root !== false) {
18786                  o = o[this.root];
18787             }
18788             
18789             for(var i = 0, len = o.length; i < len; i++){
18790                 var n = this.createNode(o[i]);
18791                 if(n){
18792                     node.appendChild(n);
18793                 }
18794             }
18795             if(typeof callback == "function"){
18796                 callback(this, node);
18797             }
18798         }catch(e){
18799             this.handleFailure(response);
18800         }
18801     },
18802
18803     handleResponse : function(response){
18804         this.transId = false;
18805         var a = response.argument;
18806         this.processResponse(response, a.node, a.callback);
18807         this.fireEvent("load", this, a.node, response);
18808     },
18809
18810     handleFailure : function(response)
18811     {
18812         // should handle failure better..
18813         this.transId = false;
18814         var a = response.argument;
18815         this.fireEvent("loadexception", this, a.node, response);
18816         if(typeof a.callback == "function"){
18817             a.callback(this, a.node);
18818         }
18819     }
18820 });/*
18821  * Based on:
18822  * Ext JS Library 1.1.1
18823  * Copyright(c) 2006-2007, Ext JS, LLC.
18824  *
18825  * Originally Released Under LGPL - original licence link has changed is not relivant.
18826  *
18827  * Fork - LGPL
18828  * <script type="text/javascript">
18829  */
18830
18831 /**
18832 * @class Roo.tree.TreeFilter
18833 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18834 * @param {TreePanel} tree
18835 * @param {Object} config (optional)
18836  */
18837 Roo.tree.TreeFilter = function(tree, config){
18838     this.tree = tree;
18839     this.filtered = {};
18840     Roo.apply(this, config);
18841 };
18842
18843 Roo.tree.TreeFilter.prototype = {
18844     clearBlank:false,
18845     reverse:false,
18846     autoClear:false,
18847     remove:false,
18848
18849      /**
18850      * Filter the data by a specific attribute.
18851      * @param {String/RegExp} value Either string that the attribute value
18852      * should start with or a RegExp to test against the attribute
18853      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18854      * @param {TreeNode} startNode (optional) The node to start the filter at.
18855      */
18856     filter : function(value, attr, startNode){
18857         attr = attr || "text";
18858         var f;
18859         if(typeof value == "string"){
18860             var vlen = value.length;
18861             // auto clear empty filter
18862             if(vlen == 0 && this.clearBlank){
18863                 this.clear();
18864                 return;
18865             }
18866             value = value.toLowerCase();
18867             f = function(n){
18868                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18869             };
18870         }else if(value.exec){ // regex?
18871             f = function(n){
18872                 return value.test(n.attributes[attr]);
18873             };
18874         }else{
18875             throw 'Illegal filter type, must be string or regex';
18876         }
18877         this.filterBy(f, null, startNode);
18878         },
18879
18880     /**
18881      * Filter by a function. The passed function will be called with each
18882      * node in the tree (or from the startNode). If the function returns true, the node is kept
18883      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18884      * @param {Function} fn The filter function
18885      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18886      */
18887     filterBy : function(fn, scope, startNode){
18888         startNode = startNode || this.tree.root;
18889         if(this.autoClear){
18890             this.clear();
18891         }
18892         var af = this.filtered, rv = this.reverse;
18893         var f = function(n){
18894             if(n == startNode){
18895                 return true;
18896             }
18897             if(af[n.id]){
18898                 return false;
18899             }
18900             var m = fn.call(scope || n, n);
18901             if(!m || rv){
18902                 af[n.id] = n;
18903                 n.ui.hide();
18904                 return false;
18905             }
18906             return true;
18907         };
18908         startNode.cascade(f);
18909         if(this.remove){
18910            for(var id in af){
18911                if(typeof id != "function"){
18912                    var n = af[id];
18913                    if(n && n.parentNode){
18914                        n.parentNode.removeChild(n);
18915                    }
18916                }
18917            }
18918         }
18919     },
18920
18921     /**
18922      * Clears the current filter. Note: with the "remove" option
18923      * set a filter cannot be cleared.
18924      */
18925     clear : function(){
18926         var t = this.tree;
18927         var af = this.filtered;
18928         for(var id in af){
18929             if(typeof id != "function"){
18930                 var n = af[id];
18931                 if(n){
18932                     n.ui.show();
18933                 }
18934             }
18935         }
18936         this.filtered = {};
18937     }
18938 };
18939 /*
18940  * Based on:
18941  * Ext JS Library 1.1.1
18942  * Copyright(c) 2006-2007, Ext JS, LLC.
18943  *
18944  * Originally Released Under LGPL - original licence link has changed is not relivant.
18945  *
18946  * Fork - LGPL
18947  * <script type="text/javascript">
18948  */
18949  
18950
18951 /**
18952  * @class Roo.tree.TreeSorter
18953  * Provides sorting of nodes in a TreePanel
18954  * 
18955  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18956  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18957  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18958  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18959  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18960  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18961  * @constructor
18962  * @param {TreePanel} tree
18963  * @param {Object} config
18964  */
18965 Roo.tree.TreeSorter = function(tree, config){
18966     Roo.apply(this, config);
18967     tree.on("beforechildrenrendered", this.doSort, this);
18968     tree.on("append", this.updateSort, this);
18969     tree.on("insert", this.updateSort, this);
18970     
18971     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18972     var p = this.property || "text";
18973     var sortType = this.sortType;
18974     var fs = this.folderSort;
18975     var cs = this.caseSensitive === true;
18976     var leafAttr = this.leafAttr || 'leaf';
18977
18978     this.sortFn = function(n1, n2){
18979         if(fs){
18980             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18981                 return 1;
18982             }
18983             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18984                 return -1;
18985             }
18986         }
18987         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18988         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18989         if(v1 < v2){
18990                         return dsc ? +1 : -1;
18991                 }else if(v1 > v2){
18992                         return dsc ? -1 : +1;
18993         }else{
18994                 return 0;
18995         }
18996     };
18997 };
18998
18999 Roo.tree.TreeSorter.prototype = {
19000     doSort : function(node){
19001         node.sort(this.sortFn);
19002     },
19003     
19004     compareNodes : function(n1, n2){
19005         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
19006     },
19007     
19008     updateSort : function(tree, node){
19009         if(node.childrenRendered){
19010             this.doSort.defer(1, this, [node]);
19011         }
19012     }
19013 };/*
19014  * Based on:
19015  * Ext JS Library 1.1.1
19016  * Copyright(c) 2006-2007, Ext JS, LLC.
19017  *
19018  * Originally Released Under LGPL - original licence link has changed is not relivant.
19019  *
19020  * Fork - LGPL
19021  * <script type="text/javascript">
19022  */
19023
19024 if(Roo.dd.DropZone){
19025     
19026 Roo.tree.TreeDropZone = function(tree, config){
19027     this.allowParentInsert = false;
19028     this.allowContainerDrop = false;
19029     this.appendOnly = false;
19030     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19031     this.tree = tree;
19032     this.lastInsertClass = "x-tree-no-status";
19033     this.dragOverData = {};
19034 };
19035
19036 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19037     ddGroup : "TreeDD",
19038     scroll:  true,
19039     
19040     expandDelay : 1000,
19041     
19042     expandNode : function(node){
19043         if(node.hasChildNodes() && !node.isExpanded()){
19044             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19045         }
19046     },
19047     
19048     queueExpand : function(node){
19049         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19050     },
19051     
19052     cancelExpand : function(){
19053         if(this.expandProcId){
19054             clearTimeout(this.expandProcId);
19055             this.expandProcId = false;
19056         }
19057     },
19058     
19059     isValidDropPoint : function(n, pt, dd, e, data){
19060         if(!n || !data){ return false; }
19061         var targetNode = n.node;
19062         var dropNode = data.node;
19063         // default drop rules
19064         if(!(targetNode && targetNode.isTarget && pt)){
19065             return false;
19066         }
19067         if(pt == "append" && targetNode.allowChildren === false){
19068             return false;
19069         }
19070         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19071             return false;
19072         }
19073         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19074             return false;
19075         }
19076         // reuse the object
19077         var overEvent = this.dragOverData;
19078         overEvent.tree = this.tree;
19079         overEvent.target = targetNode;
19080         overEvent.data = data;
19081         overEvent.point = pt;
19082         overEvent.source = dd;
19083         overEvent.rawEvent = e;
19084         overEvent.dropNode = dropNode;
19085         overEvent.cancel = false;  
19086         var result = this.tree.fireEvent("nodedragover", overEvent);
19087         return overEvent.cancel === false && result !== false;
19088     },
19089     
19090     getDropPoint : function(e, n, dd)
19091     {
19092         var tn = n.node;
19093         if(tn.isRoot){
19094             return tn.allowChildren !== false ? "append" : false; // always append for root
19095         }
19096         var dragEl = n.ddel;
19097         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19098         var y = Roo.lib.Event.getPageY(e);
19099         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19100         
19101         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19102         var noAppend = tn.allowChildren === false;
19103         if(this.appendOnly || tn.parentNode.allowChildren === false){
19104             return noAppend ? false : "append";
19105         }
19106         var noBelow = false;
19107         if(!this.allowParentInsert){
19108             noBelow = tn.hasChildNodes() && tn.isExpanded();
19109         }
19110         var q = (b - t) / (noAppend ? 2 : 3);
19111         if(y >= t && y < (t + q)){
19112             return "above";
19113         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19114             return "below";
19115         }else{
19116             return "append";
19117         }
19118     },
19119     
19120     onNodeEnter : function(n, dd, e, data)
19121     {
19122         this.cancelExpand();
19123     },
19124     
19125     onNodeOver : function(n, dd, e, data)
19126     {
19127        
19128         var pt = this.getDropPoint(e, n, dd);
19129         var node = n.node;
19130         
19131         // auto node expand check
19132         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19133             this.queueExpand(node);
19134         }else if(pt != "append"){
19135             this.cancelExpand();
19136         }
19137         
19138         // set the insert point style on the target node
19139         var returnCls = this.dropNotAllowed;
19140         if(this.isValidDropPoint(n, pt, dd, e, data)){
19141            if(pt){
19142                var el = n.ddel;
19143                var cls;
19144                if(pt == "above"){
19145                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19146                    cls = "x-tree-drag-insert-above";
19147                }else if(pt == "below"){
19148                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19149                    cls = "x-tree-drag-insert-below";
19150                }else{
19151                    returnCls = "x-tree-drop-ok-append";
19152                    cls = "x-tree-drag-append";
19153                }
19154                if(this.lastInsertClass != cls){
19155                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19156                    this.lastInsertClass = cls;
19157                }
19158            }
19159        }
19160        return returnCls;
19161     },
19162     
19163     onNodeOut : function(n, dd, e, data){
19164         
19165         this.cancelExpand();
19166         this.removeDropIndicators(n);
19167     },
19168     
19169     onNodeDrop : function(n, dd, e, data){
19170         var point = this.getDropPoint(e, n, dd);
19171         var targetNode = n.node;
19172         targetNode.ui.startDrop();
19173         if(!this.isValidDropPoint(n, point, dd, e, data)){
19174             targetNode.ui.endDrop();
19175             return false;
19176         }
19177         // first try to find the drop node
19178         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19179         var dropEvent = {
19180             tree : this.tree,
19181             target: targetNode,
19182             data: data,
19183             point: point,
19184             source: dd,
19185             rawEvent: e,
19186             dropNode: dropNode,
19187             cancel: !dropNode   
19188         };
19189         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19190         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19191             targetNode.ui.endDrop();
19192             return false;
19193         }
19194         // allow target changing
19195         targetNode = dropEvent.target;
19196         if(point == "append" && !targetNode.isExpanded()){
19197             targetNode.expand(false, null, function(){
19198                 this.completeDrop(dropEvent);
19199             }.createDelegate(this));
19200         }else{
19201             this.completeDrop(dropEvent);
19202         }
19203         return true;
19204     },
19205     
19206     completeDrop : function(de){
19207         var ns = de.dropNode, p = de.point, t = de.target;
19208         if(!(ns instanceof Array)){
19209             ns = [ns];
19210         }
19211         var n;
19212         for(var i = 0, len = ns.length; i < len; i++){
19213             n = ns[i];
19214             if(p == "above"){
19215                 t.parentNode.insertBefore(n, t);
19216             }else if(p == "below"){
19217                 t.parentNode.insertBefore(n, t.nextSibling);
19218             }else{
19219                 t.appendChild(n);
19220             }
19221         }
19222         n.ui.focus();
19223         if(this.tree.hlDrop){
19224             n.ui.highlight();
19225         }
19226         t.ui.endDrop();
19227         this.tree.fireEvent("nodedrop", de);
19228     },
19229     
19230     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19231         if(this.tree.hlDrop){
19232             dropNode.ui.focus();
19233             dropNode.ui.highlight();
19234         }
19235         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19236     },
19237     
19238     getTree : function(){
19239         return this.tree;
19240     },
19241     
19242     removeDropIndicators : function(n){
19243         if(n && n.ddel){
19244             var el = n.ddel;
19245             Roo.fly(el).removeClass([
19246                     "x-tree-drag-insert-above",
19247                     "x-tree-drag-insert-below",
19248                     "x-tree-drag-append"]);
19249             this.lastInsertClass = "_noclass";
19250         }
19251     },
19252     
19253     beforeDragDrop : function(target, e, id){
19254         this.cancelExpand();
19255         return true;
19256     },
19257     
19258     afterRepair : function(data){
19259         if(data && Roo.enableFx){
19260             data.node.ui.highlight();
19261         }
19262         this.hideProxy();
19263     } 
19264     
19265 });
19266
19267 }
19268 /*
19269  * Based on:
19270  * Ext JS Library 1.1.1
19271  * Copyright(c) 2006-2007, Ext JS, LLC.
19272  *
19273  * Originally Released Under LGPL - original licence link has changed is not relivant.
19274  *
19275  * Fork - LGPL
19276  * <script type="text/javascript">
19277  */
19278  
19279
19280 if(Roo.dd.DragZone){
19281 Roo.tree.TreeDragZone = function(tree, config){
19282     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19283     this.tree = tree;
19284 };
19285
19286 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19287     ddGroup : "TreeDD",
19288    
19289     onBeforeDrag : function(data, e){
19290         var n = data.node;
19291         return n && n.draggable && !n.disabled;
19292     },
19293      
19294     
19295     onInitDrag : function(e){
19296         var data = this.dragData;
19297         this.tree.getSelectionModel().select(data.node);
19298         this.proxy.update("");
19299         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19300         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19301     },
19302     
19303     getRepairXY : function(e, data){
19304         return data.node.ui.getDDRepairXY();
19305     },
19306     
19307     onEndDrag : function(data, e){
19308         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19309         
19310         
19311     },
19312     
19313     onValidDrop : function(dd, e, id){
19314         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19315         this.hideProxy();
19316     },
19317     
19318     beforeInvalidDrop : function(e, id){
19319         // this scrolls the original position back into view
19320         var sm = this.tree.getSelectionModel();
19321         sm.clearSelections();
19322         sm.select(this.dragData.node);
19323     }
19324 });
19325 }/*
19326  * Based on:
19327  * Ext JS Library 1.1.1
19328  * Copyright(c) 2006-2007, Ext JS, LLC.
19329  *
19330  * Originally Released Under LGPL - original licence link has changed is not relivant.
19331  *
19332  * Fork - LGPL
19333  * <script type="text/javascript">
19334  */
19335 /**
19336  * @class Roo.tree.TreeEditor
19337  * @extends Roo.Editor
19338  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19339  * as the editor field.
19340  * @constructor
19341  * @param {Object} config (used to be the tree panel.)
19342  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19343  * 
19344  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19345  * @cfg {Roo.form.TextField|Object} field The field configuration
19346  *
19347  * 
19348  */
19349 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19350     var tree = config;
19351     var field;
19352     if (oldconfig) { // old style..
19353         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19354     } else {
19355         // new style..
19356         tree = config.tree;
19357         config.field = config.field  || {};
19358         config.field.xtype = 'TextField';
19359         field = Roo.factory(config.field, Roo.form);
19360     }
19361     config = config || {};
19362     
19363     
19364     this.addEvents({
19365         /**
19366          * @event beforenodeedit
19367          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19368          * false from the handler of this event.
19369          * @param {Editor} this
19370          * @param {Roo.tree.Node} node 
19371          */
19372         "beforenodeedit" : true
19373     });
19374     
19375     //Roo.log(config);
19376     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19377
19378     this.tree = tree;
19379
19380     tree.on('beforeclick', this.beforeNodeClick, this);
19381     tree.getTreeEl().on('mousedown', this.hide, this);
19382     this.on('complete', this.updateNode, this);
19383     this.on('beforestartedit', this.fitToTree, this);
19384     this.on('startedit', this.bindScroll, this, {delay:10});
19385     this.on('specialkey', this.onSpecialKey, this);
19386 };
19387
19388 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19389     /**
19390      * @cfg {String} alignment
19391      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19392      */
19393     alignment: "l-l",
19394     // inherit
19395     autoSize: false,
19396     /**
19397      * @cfg {Boolean} hideEl
19398      * True to hide the bound element while the editor is displayed (defaults to false)
19399      */
19400     hideEl : false,
19401     /**
19402      * @cfg {String} cls
19403      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19404      */
19405     cls: "x-small-editor x-tree-editor",
19406     /**
19407      * @cfg {Boolean} shim
19408      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19409      */
19410     shim:false,
19411     // inherit
19412     shadow:"frame",
19413     /**
19414      * @cfg {Number} maxWidth
19415      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19416      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19417      * scroll and client offsets into account prior to each edit.
19418      */
19419     maxWidth: 250,
19420
19421     editDelay : 350,
19422
19423     // private
19424     fitToTree : function(ed, el){
19425         var td = this.tree.getTreeEl().dom, nd = el.dom;
19426         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19427             td.scrollLeft = nd.offsetLeft;
19428         }
19429         var w = Math.min(
19430                 this.maxWidth,
19431                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19432         this.setSize(w, '');
19433         
19434         return this.fireEvent('beforenodeedit', this, this.editNode);
19435         
19436     },
19437
19438     // private
19439     triggerEdit : function(node){
19440         this.completeEdit();
19441         this.editNode = node;
19442         this.startEdit(node.ui.textNode, node.text);
19443     },
19444
19445     // private
19446     bindScroll : function(){
19447         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19448     },
19449
19450     // private
19451     beforeNodeClick : function(node, e){
19452         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19453         this.lastClick = new Date();
19454         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19455             e.stopEvent();
19456             this.triggerEdit(node);
19457             return false;
19458         }
19459         return true;
19460     },
19461
19462     // private
19463     updateNode : function(ed, value){
19464         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19465         this.editNode.setText(value);
19466     },
19467
19468     // private
19469     onHide : function(){
19470         Roo.tree.TreeEditor.superclass.onHide.call(this);
19471         if(this.editNode){
19472             this.editNode.ui.focus();
19473         }
19474     },
19475
19476     // private
19477     onSpecialKey : function(field, e){
19478         var k = e.getKey();
19479         if(k == e.ESC){
19480             e.stopEvent();
19481             this.cancelEdit();
19482         }else if(k == e.ENTER && !e.hasModifier()){
19483             e.stopEvent();
19484             this.completeEdit();
19485         }
19486     }
19487 });//<Script type="text/javascript">
19488 /*
19489  * Based on:
19490  * Ext JS Library 1.1.1
19491  * Copyright(c) 2006-2007, Ext JS, LLC.
19492  *
19493  * Originally Released Under LGPL - original licence link has changed is not relivant.
19494  *
19495  * Fork - LGPL
19496  * <script type="text/javascript">
19497  */
19498  
19499 /**
19500  * Not documented??? - probably should be...
19501  */
19502
19503 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19504     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19505     
19506     renderElements : function(n, a, targetNode, bulkRender){
19507         //consel.log("renderElements?");
19508         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19509
19510         var t = n.getOwnerTree();
19511         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19512         
19513         var cols = t.columns;
19514         var bw = t.borderWidth;
19515         var c = cols[0];
19516         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19517          var cb = typeof a.checked == "boolean";
19518         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19519         var colcls = 'x-t-' + tid + '-c0';
19520         var buf = [
19521             '<li class="x-tree-node">',
19522             
19523                 
19524                 '<div class="x-tree-node-el ', a.cls,'">',
19525                     // extran...
19526                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19527                 
19528                 
19529                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19530                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19531                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19532                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19533                            (a.iconCls ? ' '+a.iconCls : ''),
19534                            '" unselectable="on" />',
19535                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19536                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19537                              
19538                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19539                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19540                             '<span unselectable="on" qtip="' + tx + '">',
19541                              tx,
19542                              '</span></a>' ,
19543                     '</div>',
19544                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19545                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19546                  ];
19547         for(var i = 1, len = cols.length; i < len; i++){
19548             c = cols[i];
19549             colcls = 'x-t-' + tid + '-c' +i;
19550             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19551             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19552                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19553                       "</div>");
19554          }
19555          
19556          buf.push(
19557             '</a>',
19558             '<div class="x-clear"></div></div>',
19559             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19560             "</li>");
19561         
19562         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19563             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19564                                 n.nextSibling.ui.getEl(), buf.join(""));
19565         }else{
19566             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19567         }
19568         var el = this.wrap.firstChild;
19569         this.elRow = el;
19570         this.elNode = el.firstChild;
19571         this.ranchor = el.childNodes[1];
19572         this.ctNode = this.wrap.childNodes[1];
19573         var cs = el.firstChild.childNodes;
19574         this.indentNode = cs[0];
19575         this.ecNode = cs[1];
19576         this.iconNode = cs[2];
19577         var index = 3;
19578         if(cb){
19579             this.checkbox = cs[3];
19580             index++;
19581         }
19582         this.anchor = cs[index];
19583         
19584         this.textNode = cs[index].firstChild;
19585         
19586         //el.on("click", this.onClick, this);
19587         //el.on("dblclick", this.onDblClick, this);
19588         
19589         
19590        // console.log(this);
19591     },
19592     initEvents : function(){
19593         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19594         
19595             
19596         var a = this.ranchor;
19597
19598         var el = Roo.get(a);
19599
19600         if(Roo.isOpera){ // opera render bug ignores the CSS
19601             el.setStyle("text-decoration", "none");
19602         }
19603
19604         el.on("click", this.onClick, this);
19605         el.on("dblclick", this.onDblClick, this);
19606         el.on("contextmenu", this.onContextMenu, this);
19607         
19608     },
19609     
19610     /*onSelectedChange : function(state){
19611         if(state){
19612             this.focus();
19613             this.addClass("x-tree-selected");
19614         }else{
19615             //this.blur();
19616             this.removeClass("x-tree-selected");
19617         }
19618     },*/
19619     addClass : function(cls){
19620         if(this.elRow){
19621             Roo.fly(this.elRow).addClass(cls);
19622         }
19623         
19624     },
19625     
19626     
19627     removeClass : function(cls){
19628         if(this.elRow){
19629             Roo.fly(this.elRow).removeClass(cls);
19630         }
19631     }
19632
19633     
19634     
19635 });//<Script type="text/javascript">
19636
19637 /*
19638  * Based on:
19639  * Ext JS Library 1.1.1
19640  * Copyright(c) 2006-2007, Ext JS, LLC.
19641  *
19642  * Originally Released Under LGPL - original licence link has changed is not relivant.
19643  *
19644  * Fork - LGPL
19645  * <script type="text/javascript">
19646  */
19647  
19648
19649 /**
19650  * @class Roo.tree.ColumnTree
19651  * @extends Roo.data.TreePanel
19652  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19653  * @cfg {int} borderWidth  compined right/left border allowance
19654  * @constructor
19655  * @param {String/HTMLElement/Element} el The container element
19656  * @param {Object} config
19657  */
19658 Roo.tree.ColumnTree =  function(el, config)
19659 {
19660    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19661    this.addEvents({
19662         /**
19663         * @event resize
19664         * Fire this event on a container when it resizes
19665         * @param {int} w Width
19666         * @param {int} h Height
19667         */
19668        "resize" : true
19669     });
19670     this.on('resize', this.onResize, this);
19671 };
19672
19673 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19674     //lines:false,
19675     
19676     
19677     borderWidth: Roo.isBorderBox ? 0 : 2, 
19678     headEls : false,
19679     
19680     render : function(){
19681         // add the header.....
19682        
19683         Roo.tree.ColumnTree.superclass.render.apply(this);
19684         
19685         this.el.addClass('x-column-tree');
19686         
19687         this.headers = this.el.createChild(
19688             {cls:'x-tree-headers'},this.innerCt.dom);
19689    
19690         var cols = this.columns, c;
19691         var totalWidth = 0;
19692         this.headEls = [];
19693         var  len = cols.length;
19694         for(var i = 0; i < len; i++){
19695              c = cols[i];
19696              totalWidth += c.width;
19697             this.headEls.push(this.headers.createChild({
19698                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19699                  cn: {
19700                      cls:'x-tree-hd-text',
19701                      html: c.header
19702                  },
19703                  style:'width:'+(c.width-this.borderWidth)+'px;'
19704              }));
19705         }
19706         this.headers.createChild({cls:'x-clear'});
19707         // prevent floats from wrapping when clipped
19708         this.headers.setWidth(totalWidth);
19709         //this.innerCt.setWidth(totalWidth);
19710         this.innerCt.setStyle({ overflow: 'auto' });
19711         this.onResize(this.width, this.height);
19712              
19713         
19714     },
19715     onResize : function(w,h)
19716     {
19717         this.height = h;
19718         this.width = w;
19719         // resize cols..
19720         this.innerCt.setWidth(this.width);
19721         this.innerCt.setHeight(this.height-20);
19722         
19723         // headers...
19724         var cols = this.columns, c;
19725         var totalWidth = 0;
19726         var expEl = false;
19727         var len = cols.length;
19728         for(var i = 0; i < len; i++){
19729             c = cols[i];
19730             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19731                 // it's the expander..
19732                 expEl  = this.headEls[i];
19733                 continue;
19734             }
19735             totalWidth += c.width;
19736             
19737         }
19738         if (expEl) {
19739             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19740         }
19741         this.headers.setWidth(w-20);
19742
19743         
19744         
19745         
19746     }
19747 });
19748 /*
19749  * Based on:
19750  * Ext JS Library 1.1.1
19751  * Copyright(c) 2006-2007, Ext JS, LLC.
19752  *
19753  * Originally Released Under LGPL - original licence link has changed is not relivant.
19754  *
19755  * Fork - LGPL
19756  * <script type="text/javascript">
19757  */
19758  
19759 /**
19760  * @class Roo.menu.Menu
19761  * @extends Roo.util.Observable
19762  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19763  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19764  * @constructor
19765  * Creates a new Menu
19766  * @param {Object} config Configuration options
19767  */
19768 Roo.menu.Menu = function(config){
19769     Roo.apply(this, config);
19770     this.id = this.id || Roo.id();
19771     this.addEvents({
19772         /**
19773          * @event beforeshow
19774          * Fires before this menu is displayed
19775          * @param {Roo.menu.Menu} this
19776          */
19777         beforeshow : true,
19778         /**
19779          * @event beforehide
19780          * Fires before this menu is hidden
19781          * @param {Roo.menu.Menu} this
19782          */
19783         beforehide : true,
19784         /**
19785          * @event show
19786          * Fires after this menu is displayed
19787          * @param {Roo.menu.Menu} this
19788          */
19789         show : true,
19790         /**
19791          * @event hide
19792          * Fires after this menu is hidden
19793          * @param {Roo.menu.Menu} this
19794          */
19795         hide : true,
19796         /**
19797          * @event click
19798          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19799          * @param {Roo.menu.Menu} this
19800          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19801          * @param {Roo.EventObject} e
19802          */
19803         click : true,
19804         /**
19805          * @event mouseover
19806          * Fires when the mouse is hovering over this menu
19807          * @param {Roo.menu.Menu} this
19808          * @param {Roo.EventObject} e
19809          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19810          */
19811         mouseover : true,
19812         /**
19813          * @event mouseout
19814          * Fires when the mouse exits this menu
19815          * @param {Roo.menu.Menu} this
19816          * @param {Roo.EventObject} e
19817          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19818          */
19819         mouseout : true,
19820         /**
19821          * @event itemclick
19822          * Fires when a menu item contained in this menu is clicked
19823          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19824          * @param {Roo.EventObject} e
19825          */
19826         itemclick: true
19827     });
19828     if (this.registerMenu) {
19829         Roo.menu.MenuMgr.register(this);
19830     }
19831     
19832     var mis = this.items;
19833     this.items = new Roo.util.MixedCollection();
19834     if(mis){
19835         this.add.apply(this, mis);
19836     }
19837 };
19838
19839 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19840     /**
19841      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19842      */
19843     minWidth : 120,
19844     /**
19845      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19846      * for bottom-right shadow (defaults to "sides")
19847      */
19848     shadow : "sides",
19849     /**
19850      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19851      * this menu (defaults to "tl-tr?")
19852      */
19853     subMenuAlign : "tl-tr?",
19854     /**
19855      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19856      * relative to its element of origin (defaults to "tl-bl?")
19857      */
19858     defaultAlign : "tl-bl?",
19859     /**
19860      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19861      */
19862     allowOtherMenus : false,
19863     /**
19864      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19865      */
19866     registerMenu : true,
19867
19868     hidden:true,
19869
19870     // private
19871     render : function(){
19872         if(this.el){
19873             return;
19874         }
19875         var el = this.el = new Roo.Layer({
19876             cls: "x-menu",
19877             shadow:this.shadow,
19878             constrain: false,
19879             parentEl: this.parentEl || document.body,
19880             zindex:15000
19881         });
19882
19883         this.keyNav = new Roo.menu.MenuNav(this);
19884
19885         if(this.plain){
19886             el.addClass("x-menu-plain");
19887         }
19888         if(this.cls){
19889             el.addClass(this.cls);
19890         }
19891         // generic focus element
19892         this.focusEl = el.createChild({
19893             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19894         });
19895         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19896         ul.on("click", this.onClick, this);
19897         ul.on("mouseover", this.onMouseOver, this);
19898         ul.on("mouseout", this.onMouseOut, this);
19899         this.items.each(function(item){
19900             if (item.hidden) {
19901                 return;
19902             }
19903             
19904             var li = document.createElement("li");
19905             li.className = "x-menu-list-item";
19906             ul.dom.appendChild(li);
19907             item.render(li, this);
19908         }, this);
19909         this.ul = ul;
19910         this.autoWidth();
19911     },
19912
19913     // private
19914     autoWidth : function(){
19915         var el = this.el, ul = this.ul;
19916         if(!el){
19917             return;
19918         }
19919         var w = this.width;
19920         if(w){
19921             el.setWidth(w);
19922         }else if(Roo.isIE){
19923             el.setWidth(this.minWidth);
19924             var t = el.dom.offsetWidth; // force recalc
19925             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19926         }
19927     },
19928
19929     // private
19930     delayAutoWidth : function(){
19931         if(this.rendered){
19932             if(!this.awTask){
19933                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19934             }
19935             this.awTask.delay(20);
19936         }
19937     },
19938
19939     // private
19940     findTargetItem : function(e){
19941         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19942         if(t && t.menuItemId){
19943             return this.items.get(t.menuItemId);
19944         }
19945     },
19946
19947     // private
19948     onClick : function(e){
19949         var t;
19950         if(t = this.findTargetItem(e)){
19951             t.onClick(e);
19952             this.fireEvent("click", this, t, e);
19953         }
19954     },
19955
19956     // private
19957     setActiveItem : function(item, autoExpand){
19958         if(item != this.activeItem){
19959             if(this.activeItem){
19960                 this.activeItem.deactivate();
19961             }
19962             this.activeItem = item;
19963             item.activate(autoExpand);
19964         }else if(autoExpand){
19965             item.expandMenu();
19966         }
19967     },
19968
19969     // private
19970     tryActivate : function(start, step){
19971         var items = this.items;
19972         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19973             var item = items.get(i);
19974             if(!item.disabled && item.canActivate){
19975                 this.setActiveItem(item, false);
19976                 return item;
19977             }
19978         }
19979         return false;
19980     },
19981
19982     // private
19983     onMouseOver : function(e){
19984         var t;
19985         if(t = this.findTargetItem(e)){
19986             if(t.canActivate && !t.disabled){
19987                 this.setActiveItem(t, true);
19988             }
19989         }
19990         this.fireEvent("mouseover", this, e, t);
19991     },
19992
19993     // private
19994     onMouseOut : function(e){
19995         var t;
19996         if(t = this.findTargetItem(e)){
19997             if(t == this.activeItem && t.shouldDeactivate(e)){
19998                 this.activeItem.deactivate();
19999                 delete this.activeItem;
20000             }
20001         }
20002         this.fireEvent("mouseout", this, e, t);
20003     },
20004
20005     /**
20006      * Read-only.  Returns true if the menu is currently displayed, else false.
20007      * @type Boolean
20008      */
20009     isVisible : function(){
20010         return this.el && !this.hidden;
20011     },
20012
20013     /**
20014      * Displays this menu relative to another element
20015      * @param {String/HTMLElement/Roo.Element} element The element to align to
20016      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20017      * the element (defaults to this.defaultAlign)
20018      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20019      */
20020     show : function(el, pos, parentMenu){
20021         this.parentMenu = parentMenu;
20022         if(!this.el){
20023             this.render();
20024         }
20025         this.fireEvent("beforeshow", this);
20026         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20027     },
20028
20029     /**
20030      * Displays this menu at a specific xy position
20031      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20032      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20033      */
20034     showAt : function(xy, parentMenu, /* private: */_e){
20035         this.parentMenu = parentMenu;
20036         if(!this.el){
20037             this.render();
20038         }
20039         if(_e !== false){
20040             this.fireEvent("beforeshow", this);
20041             xy = this.el.adjustForConstraints(xy);
20042         }
20043         this.el.setXY(xy);
20044         this.el.show();
20045         this.hidden = false;
20046         this.focus();
20047         this.fireEvent("show", this);
20048     },
20049
20050     focus : function(){
20051         if(!this.hidden){
20052             this.doFocus.defer(50, this);
20053         }
20054     },
20055
20056     doFocus : function(){
20057         if(!this.hidden){
20058             this.focusEl.focus();
20059         }
20060     },
20061
20062     /**
20063      * Hides this menu and optionally all parent menus
20064      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20065      */
20066     hide : function(deep){
20067         if(this.el && this.isVisible()){
20068             this.fireEvent("beforehide", this);
20069             if(this.activeItem){
20070                 this.activeItem.deactivate();
20071                 this.activeItem = null;
20072             }
20073             this.el.hide();
20074             this.hidden = true;
20075             this.fireEvent("hide", this);
20076         }
20077         if(deep === true && this.parentMenu){
20078             this.parentMenu.hide(true);
20079         }
20080     },
20081
20082     /**
20083      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20084      * Any of the following are valid:
20085      * <ul>
20086      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20087      * <li>An HTMLElement object which will be converted to a menu item</li>
20088      * <li>A menu item config object that will be created as a new menu item</li>
20089      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20090      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20091      * </ul>
20092      * Usage:
20093      * <pre><code>
20094 // Create the menu
20095 var menu = new Roo.menu.Menu();
20096
20097 // Create a menu item to add by reference
20098 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20099
20100 // Add a bunch of items at once using different methods.
20101 // Only the last item added will be returned.
20102 var item = menu.add(
20103     menuItem,                // add existing item by ref
20104     'Dynamic Item',          // new TextItem
20105     '-',                     // new separator
20106     { text: 'Config Item' }  // new item by config
20107 );
20108 </code></pre>
20109      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20110      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20111      */
20112     add : function(){
20113         var a = arguments, l = a.length, item;
20114         for(var i = 0; i < l; i++){
20115             var el = a[i];
20116             if ((typeof(el) == "object") && el.xtype && el.xns) {
20117                 el = Roo.factory(el, Roo.menu);
20118             }
20119             
20120             if(el.render){ // some kind of Item
20121                 item = this.addItem(el);
20122             }else if(typeof el == "string"){ // string
20123                 if(el == "separator" || el == "-"){
20124                     item = this.addSeparator();
20125                 }else{
20126                     item = this.addText(el);
20127                 }
20128             }else if(el.tagName || el.el){ // element
20129                 item = this.addElement(el);
20130             }else if(typeof el == "object"){ // must be menu item config?
20131                 item = this.addMenuItem(el);
20132             }
20133         }
20134         return item;
20135     },
20136
20137     /**
20138      * Returns this menu's underlying {@link Roo.Element} object
20139      * @return {Roo.Element} The element
20140      */
20141     getEl : function(){
20142         if(!this.el){
20143             this.render();
20144         }
20145         return this.el;
20146     },
20147
20148     /**
20149      * Adds a separator bar to the menu
20150      * @return {Roo.menu.Item} The menu item that was added
20151      */
20152     addSeparator : function(){
20153         return this.addItem(new Roo.menu.Separator());
20154     },
20155
20156     /**
20157      * Adds an {@link Roo.Element} object to the menu
20158      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20159      * @return {Roo.menu.Item} The menu item that was added
20160      */
20161     addElement : function(el){
20162         return this.addItem(new Roo.menu.BaseItem(el));
20163     },
20164
20165     /**
20166      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20167      * @param {Roo.menu.Item} item The menu item to add
20168      * @return {Roo.menu.Item} The menu item that was added
20169      */
20170     addItem : function(item){
20171         this.items.add(item);
20172         if(this.ul){
20173             var li = document.createElement("li");
20174             li.className = "x-menu-list-item";
20175             this.ul.dom.appendChild(li);
20176             item.render(li, this);
20177             this.delayAutoWidth();
20178         }
20179         return item;
20180     },
20181
20182     /**
20183      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20184      * @param {Object} config A MenuItem config object
20185      * @return {Roo.menu.Item} The menu item that was added
20186      */
20187     addMenuItem : function(config){
20188         if(!(config instanceof Roo.menu.Item)){
20189             if(typeof config.checked == "boolean"){ // must be check menu item config?
20190                 config = new Roo.menu.CheckItem(config);
20191             }else{
20192                 config = new Roo.menu.Item(config);
20193             }
20194         }
20195         return this.addItem(config);
20196     },
20197
20198     /**
20199      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20200      * @param {String} text The text to display in the menu item
20201      * @return {Roo.menu.Item} The menu item that was added
20202      */
20203     addText : function(text){
20204         return this.addItem(new Roo.menu.TextItem({ text : text }));
20205     },
20206
20207     /**
20208      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20209      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20210      * @param {Roo.menu.Item} item The menu item to add
20211      * @return {Roo.menu.Item} The menu item that was added
20212      */
20213     insert : function(index, item){
20214         this.items.insert(index, item);
20215         if(this.ul){
20216             var li = document.createElement("li");
20217             li.className = "x-menu-list-item";
20218             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20219             item.render(li, this);
20220             this.delayAutoWidth();
20221         }
20222         return item;
20223     },
20224
20225     /**
20226      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20227      * @param {Roo.menu.Item} item The menu item to remove
20228      */
20229     remove : function(item){
20230         this.items.removeKey(item.id);
20231         item.destroy();
20232     },
20233
20234     /**
20235      * Removes and destroys all items in the menu
20236      */
20237     removeAll : function(){
20238         var f;
20239         while(f = this.items.first()){
20240             this.remove(f);
20241         }
20242     }
20243 });
20244
20245 // MenuNav is a private utility class used internally by the Menu
20246 Roo.menu.MenuNav = function(menu){
20247     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20248     this.scope = this.menu = menu;
20249 };
20250
20251 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20252     doRelay : function(e, h){
20253         var k = e.getKey();
20254         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20255             this.menu.tryActivate(0, 1);
20256             return false;
20257         }
20258         return h.call(this.scope || this, e, this.menu);
20259     },
20260
20261     up : function(e, m){
20262         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20263             m.tryActivate(m.items.length-1, -1);
20264         }
20265     },
20266
20267     down : function(e, m){
20268         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20269             m.tryActivate(0, 1);
20270         }
20271     },
20272
20273     right : function(e, m){
20274         if(m.activeItem){
20275             m.activeItem.expandMenu(true);
20276         }
20277     },
20278
20279     left : function(e, m){
20280         m.hide();
20281         if(m.parentMenu && m.parentMenu.activeItem){
20282             m.parentMenu.activeItem.activate();
20283         }
20284     },
20285
20286     enter : function(e, m){
20287         if(m.activeItem){
20288             e.stopPropagation();
20289             m.activeItem.onClick(e);
20290             m.fireEvent("click", this, m.activeItem);
20291             return true;
20292         }
20293     }
20294 });/*
20295  * Based on:
20296  * Ext JS Library 1.1.1
20297  * Copyright(c) 2006-2007, Ext JS, LLC.
20298  *
20299  * Originally Released Under LGPL - original licence link has changed is not relivant.
20300  *
20301  * Fork - LGPL
20302  * <script type="text/javascript">
20303  */
20304  
20305 /**
20306  * @class Roo.menu.MenuMgr
20307  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20308  * @singleton
20309  */
20310 Roo.menu.MenuMgr = function(){
20311    var menus, active, groups = {}, attached = false, lastShow = new Date();
20312
20313    // private - called when first menu is created
20314    function init(){
20315        menus = {};
20316        active = new Roo.util.MixedCollection();
20317        Roo.get(document).addKeyListener(27, function(){
20318            if(active.length > 0){
20319                hideAll();
20320            }
20321        });
20322    }
20323
20324    // private
20325    function hideAll(){
20326        if(active && active.length > 0){
20327            var c = active.clone();
20328            c.each(function(m){
20329                m.hide();
20330            });
20331        }
20332    }
20333
20334    // private
20335    function onHide(m){
20336        active.remove(m);
20337        if(active.length < 1){
20338            Roo.get(document).un("mousedown", onMouseDown);
20339            attached = false;
20340        }
20341    }
20342
20343    // private
20344    function onShow(m){
20345        var last = active.last();
20346        lastShow = new Date();
20347        active.add(m);
20348        if(!attached){
20349            Roo.get(document).on("mousedown", onMouseDown);
20350            attached = true;
20351        }
20352        if(m.parentMenu){
20353           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20354           m.parentMenu.activeChild = m;
20355        }else if(last && last.isVisible()){
20356           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20357        }
20358    }
20359
20360    // private
20361    function onBeforeHide(m){
20362        if(m.activeChild){
20363            m.activeChild.hide();
20364        }
20365        if(m.autoHideTimer){
20366            clearTimeout(m.autoHideTimer);
20367            delete m.autoHideTimer;
20368        }
20369    }
20370
20371    // private
20372    function onBeforeShow(m){
20373        var pm = m.parentMenu;
20374        if(!pm && !m.allowOtherMenus){
20375            hideAll();
20376        }else if(pm && pm.activeChild && active != m){
20377            pm.activeChild.hide();
20378        }
20379    }
20380
20381    // private
20382    function onMouseDown(e){
20383        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20384            hideAll();
20385        }
20386    }
20387
20388    // private
20389    function onBeforeCheck(mi, state){
20390        if(state){
20391            var g = groups[mi.group];
20392            for(var i = 0, l = g.length; i < l; i++){
20393                if(g[i] != mi){
20394                    g[i].setChecked(false);
20395                }
20396            }
20397        }
20398    }
20399
20400    return {
20401
20402        /**
20403         * Hides all menus that are currently visible
20404         */
20405        hideAll : function(){
20406             hideAll();  
20407        },
20408
20409        // private
20410        register : function(menu){
20411            if(!menus){
20412                init();
20413            }
20414            menus[menu.id] = menu;
20415            menu.on("beforehide", onBeforeHide);
20416            menu.on("hide", onHide);
20417            menu.on("beforeshow", onBeforeShow);
20418            menu.on("show", onShow);
20419            var g = menu.group;
20420            if(g && menu.events["checkchange"]){
20421                if(!groups[g]){
20422                    groups[g] = [];
20423                }
20424                groups[g].push(menu);
20425                menu.on("checkchange", onCheck);
20426            }
20427        },
20428
20429         /**
20430          * Returns a {@link Roo.menu.Menu} object
20431          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20432          * be used to generate and return a new Menu instance.
20433          */
20434        get : function(menu){
20435            if(typeof menu == "string"){ // menu id
20436                return menus[menu];
20437            }else if(menu.events){  // menu instance
20438                return menu;
20439            }else if(typeof menu.length == 'number'){ // array of menu items?
20440                return new Roo.menu.Menu({items:menu});
20441            }else{ // otherwise, must be a config
20442                return new Roo.menu.Menu(menu);
20443            }
20444        },
20445
20446        // private
20447        unregister : function(menu){
20448            delete menus[menu.id];
20449            menu.un("beforehide", onBeforeHide);
20450            menu.un("hide", onHide);
20451            menu.un("beforeshow", onBeforeShow);
20452            menu.un("show", onShow);
20453            var g = menu.group;
20454            if(g && menu.events["checkchange"]){
20455                groups[g].remove(menu);
20456                menu.un("checkchange", onCheck);
20457            }
20458        },
20459
20460        // private
20461        registerCheckable : function(menuItem){
20462            var g = menuItem.group;
20463            if(g){
20464                if(!groups[g]){
20465                    groups[g] = [];
20466                }
20467                groups[g].push(menuItem);
20468                menuItem.on("beforecheckchange", onBeforeCheck);
20469            }
20470        },
20471
20472        // private
20473        unregisterCheckable : function(menuItem){
20474            var g = menuItem.group;
20475            if(g){
20476                groups[g].remove(menuItem);
20477                menuItem.un("beforecheckchange", onBeforeCheck);
20478            }
20479        }
20480    };
20481 }();/*
20482  * Based on:
20483  * Ext JS Library 1.1.1
20484  * Copyright(c) 2006-2007, Ext JS, LLC.
20485  *
20486  * Originally Released Under LGPL - original licence link has changed is not relivant.
20487  *
20488  * Fork - LGPL
20489  * <script type="text/javascript">
20490  */
20491  
20492
20493 /**
20494  * @class Roo.menu.BaseItem
20495  * @extends Roo.Component
20496  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20497  * management and base configuration options shared by all menu components.
20498  * @constructor
20499  * Creates a new BaseItem
20500  * @param {Object} config Configuration options
20501  */
20502 Roo.menu.BaseItem = function(config){
20503     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20504
20505     this.addEvents({
20506         /**
20507          * @event click
20508          * Fires when this item is clicked
20509          * @param {Roo.menu.BaseItem} this
20510          * @param {Roo.EventObject} e
20511          */
20512         click: true,
20513         /**
20514          * @event activate
20515          * Fires when this item is activated
20516          * @param {Roo.menu.BaseItem} this
20517          */
20518         activate : true,
20519         /**
20520          * @event deactivate
20521          * Fires when this item is deactivated
20522          * @param {Roo.menu.BaseItem} this
20523          */
20524         deactivate : true
20525     });
20526
20527     if(this.handler){
20528         this.on("click", this.handler, this.scope, true);
20529     }
20530 };
20531
20532 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20533     /**
20534      * @cfg {Function} handler
20535      * A function that will handle the click event of this menu item (defaults to undefined)
20536      */
20537     /**
20538      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20539      */
20540     canActivate : false,
20541     
20542      /**
20543      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20544      */
20545     hidden: false,
20546     
20547     /**
20548      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20549      */
20550     activeClass : "x-menu-item-active",
20551     /**
20552      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20553      */
20554     hideOnClick : true,
20555     /**
20556      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20557      */
20558     hideDelay : 100,
20559
20560     // private
20561     ctype: "Roo.menu.BaseItem",
20562
20563     // private
20564     actionMode : "container",
20565
20566     // private
20567     render : function(container, parentMenu){
20568         this.parentMenu = parentMenu;
20569         Roo.menu.BaseItem.superclass.render.call(this, container);
20570         this.container.menuItemId = this.id;
20571     },
20572
20573     // private
20574     onRender : function(container, position){
20575         this.el = Roo.get(this.el);
20576         container.dom.appendChild(this.el.dom);
20577     },
20578
20579     // private
20580     onClick : function(e){
20581         if(!this.disabled && this.fireEvent("click", this, e) !== false
20582                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20583             this.handleClick(e);
20584         }else{
20585             e.stopEvent();
20586         }
20587     },
20588
20589     // private
20590     activate : function(){
20591         if(this.disabled){
20592             return false;
20593         }
20594         var li = this.container;
20595         li.addClass(this.activeClass);
20596         this.region = li.getRegion().adjust(2, 2, -2, -2);
20597         this.fireEvent("activate", this);
20598         return true;
20599     },
20600
20601     // private
20602     deactivate : function(){
20603         this.container.removeClass(this.activeClass);
20604         this.fireEvent("deactivate", this);
20605     },
20606
20607     // private
20608     shouldDeactivate : function(e){
20609         return !this.region || !this.region.contains(e.getPoint());
20610     },
20611
20612     // private
20613     handleClick : function(e){
20614         if(this.hideOnClick){
20615             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20616         }
20617     },
20618
20619     // private
20620     expandMenu : function(autoActivate){
20621         // do nothing
20622     },
20623
20624     // private
20625     hideMenu : function(){
20626         // do nothing
20627     }
20628 });/*
20629  * Based on:
20630  * Ext JS Library 1.1.1
20631  * Copyright(c) 2006-2007, Ext JS, LLC.
20632  *
20633  * Originally Released Under LGPL - original licence link has changed is not relivant.
20634  *
20635  * Fork - LGPL
20636  * <script type="text/javascript">
20637  */
20638  
20639 /**
20640  * @class Roo.menu.Adapter
20641  * @extends Roo.menu.BaseItem
20642  * 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.
20643  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20644  * @constructor
20645  * Creates a new Adapter
20646  * @param {Object} config Configuration options
20647  */
20648 Roo.menu.Adapter = function(component, config){
20649     Roo.menu.Adapter.superclass.constructor.call(this, config);
20650     this.component = component;
20651 };
20652 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20653     // private
20654     canActivate : true,
20655
20656     // private
20657     onRender : function(container, position){
20658         this.component.render(container);
20659         this.el = this.component.getEl();
20660     },
20661
20662     // private
20663     activate : function(){
20664         if(this.disabled){
20665             return false;
20666         }
20667         this.component.focus();
20668         this.fireEvent("activate", this);
20669         return true;
20670     },
20671
20672     // private
20673     deactivate : function(){
20674         this.fireEvent("deactivate", this);
20675     },
20676
20677     // private
20678     disable : function(){
20679         this.component.disable();
20680         Roo.menu.Adapter.superclass.disable.call(this);
20681     },
20682
20683     // private
20684     enable : function(){
20685         this.component.enable();
20686         Roo.menu.Adapter.superclass.enable.call(this);
20687     }
20688 });/*
20689  * Based on:
20690  * Ext JS Library 1.1.1
20691  * Copyright(c) 2006-2007, Ext JS, LLC.
20692  *
20693  * Originally Released Under LGPL - original licence link has changed is not relivant.
20694  *
20695  * Fork - LGPL
20696  * <script type="text/javascript">
20697  */
20698
20699 /**
20700  * @class Roo.menu.TextItem
20701  * @extends Roo.menu.BaseItem
20702  * Adds a static text string to a menu, usually used as either a heading or group separator.
20703  * Note: old style constructor with text is still supported.
20704  * 
20705  * @constructor
20706  * Creates a new TextItem
20707  * @param {Object} cfg Configuration
20708  */
20709 Roo.menu.TextItem = function(cfg){
20710     if (typeof(cfg) == 'string') {
20711         this.text = cfg;
20712     } else {
20713         Roo.apply(this,cfg);
20714     }
20715     
20716     Roo.menu.TextItem.superclass.constructor.call(this);
20717 };
20718
20719 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20720     /**
20721      * @cfg {Boolean} text Text to show on item.
20722      */
20723     text : '',
20724     
20725     /**
20726      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20727      */
20728     hideOnClick : false,
20729     /**
20730      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20731      */
20732     itemCls : "x-menu-text",
20733
20734     // private
20735     onRender : function(){
20736         var s = document.createElement("span");
20737         s.className = this.itemCls;
20738         s.innerHTML = this.text;
20739         this.el = s;
20740         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20741     }
20742 });/*
20743  * Based on:
20744  * Ext JS Library 1.1.1
20745  * Copyright(c) 2006-2007, Ext JS, LLC.
20746  *
20747  * Originally Released Under LGPL - original licence link has changed is not relivant.
20748  *
20749  * Fork - LGPL
20750  * <script type="text/javascript">
20751  */
20752
20753 /**
20754  * @class Roo.menu.Separator
20755  * @extends Roo.menu.BaseItem
20756  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20757  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20758  * @constructor
20759  * @param {Object} config Configuration options
20760  */
20761 Roo.menu.Separator = function(config){
20762     Roo.menu.Separator.superclass.constructor.call(this, config);
20763 };
20764
20765 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20766     /**
20767      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20768      */
20769     itemCls : "x-menu-sep",
20770     /**
20771      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20772      */
20773     hideOnClick : false,
20774
20775     // private
20776     onRender : function(li){
20777         var s = document.createElement("span");
20778         s.className = this.itemCls;
20779         s.innerHTML = "&#160;";
20780         this.el = s;
20781         li.addClass("x-menu-sep-li");
20782         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20783     }
20784 });/*
20785  * Based on:
20786  * Ext JS Library 1.1.1
20787  * Copyright(c) 2006-2007, Ext JS, LLC.
20788  *
20789  * Originally Released Under LGPL - original licence link has changed is not relivant.
20790  *
20791  * Fork - LGPL
20792  * <script type="text/javascript">
20793  */
20794 /**
20795  * @class Roo.menu.Item
20796  * @extends Roo.menu.BaseItem
20797  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20798  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20799  * activation and click handling.
20800  * @constructor
20801  * Creates a new Item
20802  * @param {Object} config Configuration options
20803  */
20804 Roo.menu.Item = function(config){
20805     Roo.menu.Item.superclass.constructor.call(this, config);
20806     if(this.menu){
20807         this.menu = Roo.menu.MenuMgr.get(this.menu);
20808     }
20809 };
20810 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20811     
20812     /**
20813      * @cfg {String} text
20814      * The text to show on the menu item.
20815      */
20816     text: '',
20817      /**
20818      * @cfg {String} HTML to render in menu
20819      * The text to show on the menu item (HTML version).
20820      */
20821     html: '',
20822     /**
20823      * @cfg {String} icon
20824      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20825      */
20826     icon: undefined,
20827     /**
20828      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20829      */
20830     itemCls : "x-menu-item",
20831     /**
20832      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20833      */
20834     canActivate : true,
20835     /**
20836      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20837      */
20838     showDelay: 200,
20839     // doc'd in BaseItem
20840     hideDelay: 200,
20841
20842     // private
20843     ctype: "Roo.menu.Item",
20844     
20845     // private
20846     onRender : function(container, position){
20847         var el = document.createElement("a");
20848         el.hideFocus = true;
20849         el.unselectable = "on";
20850         el.href = this.href || "#";
20851         if(this.hrefTarget){
20852             el.target = this.hrefTarget;
20853         }
20854         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20855         
20856         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20857         
20858         el.innerHTML = String.format(
20859                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20860                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20861         this.el = el;
20862         Roo.menu.Item.superclass.onRender.call(this, container, position);
20863     },
20864
20865     /**
20866      * Sets the text to display in this menu item
20867      * @param {String} text The text to display
20868      * @param {Boolean} isHTML true to indicate text is pure html.
20869      */
20870     setText : function(text, isHTML){
20871         if (isHTML) {
20872             this.html = text;
20873         } else {
20874             this.text = text;
20875             this.html = '';
20876         }
20877         if(this.rendered){
20878             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20879      
20880             this.el.update(String.format(
20881                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20882                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20883             this.parentMenu.autoWidth();
20884         }
20885     },
20886
20887     // private
20888     handleClick : function(e){
20889         if(!this.href){ // if no link defined, stop the event automatically
20890             e.stopEvent();
20891         }
20892         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20893     },
20894
20895     // private
20896     activate : function(autoExpand){
20897         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20898             this.focus();
20899             if(autoExpand){
20900                 this.expandMenu();
20901             }
20902         }
20903         return true;
20904     },
20905
20906     // private
20907     shouldDeactivate : function(e){
20908         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20909             if(this.menu && this.menu.isVisible()){
20910                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20911             }
20912             return true;
20913         }
20914         return false;
20915     },
20916
20917     // private
20918     deactivate : function(){
20919         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20920         this.hideMenu();
20921     },
20922
20923     // private
20924     expandMenu : function(autoActivate){
20925         if(!this.disabled && this.menu){
20926             clearTimeout(this.hideTimer);
20927             delete this.hideTimer;
20928             if(!this.menu.isVisible() && !this.showTimer){
20929                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20930             }else if (this.menu.isVisible() && autoActivate){
20931                 this.menu.tryActivate(0, 1);
20932             }
20933         }
20934     },
20935
20936     // private
20937     deferExpand : function(autoActivate){
20938         delete this.showTimer;
20939         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20940         if(autoActivate){
20941             this.menu.tryActivate(0, 1);
20942         }
20943     },
20944
20945     // private
20946     hideMenu : function(){
20947         clearTimeout(this.showTimer);
20948         delete this.showTimer;
20949         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20950             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20951         }
20952     },
20953
20954     // private
20955     deferHide : function(){
20956         delete this.hideTimer;
20957         this.menu.hide();
20958     }
20959 });/*
20960  * Based on:
20961  * Ext JS Library 1.1.1
20962  * Copyright(c) 2006-2007, Ext JS, LLC.
20963  *
20964  * Originally Released Under LGPL - original licence link has changed is not relivant.
20965  *
20966  * Fork - LGPL
20967  * <script type="text/javascript">
20968  */
20969  
20970 /**
20971  * @class Roo.menu.CheckItem
20972  * @extends Roo.menu.Item
20973  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20974  * @constructor
20975  * Creates a new CheckItem
20976  * @param {Object} config Configuration options
20977  */
20978 Roo.menu.CheckItem = function(config){
20979     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20980     this.addEvents({
20981         /**
20982          * @event beforecheckchange
20983          * Fires before the checked value is set, providing an opportunity to cancel if needed
20984          * @param {Roo.menu.CheckItem} this
20985          * @param {Boolean} checked The new checked value that will be set
20986          */
20987         "beforecheckchange" : true,
20988         /**
20989          * @event checkchange
20990          * Fires after the checked value has been set
20991          * @param {Roo.menu.CheckItem} this
20992          * @param {Boolean} checked The checked value that was set
20993          */
20994         "checkchange" : true
20995     });
20996     if(this.checkHandler){
20997         this.on('checkchange', this.checkHandler, this.scope);
20998     }
20999 };
21000 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
21001     /**
21002      * @cfg {String} group
21003      * All check items with the same group name will automatically be grouped into a single-select
21004      * radio button group (defaults to '')
21005      */
21006     /**
21007      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
21008      */
21009     itemCls : "x-menu-item x-menu-check-item",
21010     /**
21011      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
21012      */
21013     groupClass : "x-menu-group-item",
21014
21015     /**
21016      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
21017      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21018      * initialized with checked = true will be rendered as checked.
21019      */
21020     checked: false,
21021
21022     // private
21023     ctype: "Roo.menu.CheckItem",
21024
21025     // private
21026     onRender : function(c){
21027         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21028         if(this.group){
21029             this.el.addClass(this.groupClass);
21030         }
21031         Roo.menu.MenuMgr.registerCheckable(this);
21032         if(this.checked){
21033             this.checked = false;
21034             this.setChecked(true, true);
21035         }
21036     },
21037
21038     // private
21039     destroy : function(){
21040         if(this.rendered){
21041             Roo.menu.MenuMgr.unregisterCheckable(this);
21042         }
21043         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21044     },
21045
21046     /**
21047      * Set the checked state of this item
21048      * @param {Boolean} checked The new checked value
21049      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21050      */
21051     setChecked : function(state, suppressEvent){
21052         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21053             if(this.container){
21054                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21055             }
21056             this.checked = state;
21057             if(suppressEvent !== true){
21058                 this.fireEvent("checkchange", this, state);
21059             }
21060         }
21061     },
21062
21063     // private
21064     handleClick : function(e){
21065        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21066            this.setChecked(!this.checked);
21067        }
21068        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21069     }
21070 });/*
21071  * Based on:
21072  * Ext JS Library 1.1.1
21073  * Copyright(c) 2006-2007, Ext JS, LLC.
21074  *
21075  * Originally Released Under LGPL - original licence link has changed is not relivant.
21076  *
21077  * Fork - LGPL
21078  * <script type="text/javascript">
21079  */
21080  
21081 /**
21082  * @class Roo.menu.DateItem
21083  * @extends Roo.menu.Adapter
21084  * A menu item that wraps the {@link Roo.DatPicker} component.
21085  * @constructor
21086  * Creates a new DateItem
21087  * @param {Object} config Configuration options
21088  */
21089 Roo.menu.DateItem = function(config){
21090     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21091     /** The Roo.DatePicker object @type Roo.DatePicker */
21092     this.picker = this.component;
21093     this.addEvents({select: true});
21094     
21095     this.picker.on("render", function(picker){
21096         picker.getEl().swallowEvent("click");
21097         picker.container.addClass("x-menu-date-item");
21098     });
21099
21100     this.picker.on("select", this.onSelect, this);
21101 };
21102
21103 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21104     // private
21105     onSelect : function(picker, date){
21106         this.fireEvent("select", this, date, picker);
21107         Roo.menu.DateItem.superclass.handleClick.call(this);
21108     }
21109 });/*
21110  * Based on:
21111  * Ext JS Library 1.1.1
21112  * Copyright(c) 2006-2007, Ext JS, LLC.
21113  *
21114  * Originally Released Under LGPL - original licence link has changed is not relivant.
21115  *
21116  * Fork - LGPL
21117  * <script type="text/javascript">
21118  */
21119  
21120 /**
21121  * @class Roo.menu.ColorItem
21122  * @extends Roo.menu.Adapter
21123  * A menu item that wraps the {@link Roo.ColorPalette} component.
21124  * @constructor
21125  * Creates a new ColorItem
21126  * @param {Object} config Configuration options
21127  */
21128 Roo.menu.ColorItem = function(config){
21129     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21130     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21131     this.palette = this.component;
21132     this.relayEvents(this.palette, ["select"]);
21133     if(this.selectHandler){
21134         this.on('select', this.selectHandler, this.scope);
21135     }
21136 };
21137 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21138  * Based on:
21139  * Ext JS Library 1.1.1
21140  * Copyright(c) 2006-2007, Ext JS, LLC.
21141  *
21142  * Originally Released Under LGPL - original licence link has changed is not relivant.
21143  *
21144  * Fork - LGPL
21145  * <script type="text/javascript">
21146  */
21147  
21148
21149 /**
21150  * @class Roo.menu.DateMenu
21151  * @extends Roo.menu.Menu
21152  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21153  * @constructor
21154  * Creates a new DateMenu
21155  * @param {Object} config Configuration options
21156  */
21157 Roo.menu.DateMenu = function(config){
21158     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21159     this.plain = true;
21160     var di = new Roo.menu.DateItem(config);
21161     this.add(di);
21162     /**
21163      * The {@link Roo.DatePicker} instance for this DateMenu
21164      * @type DatePicker
21165      */
21166     this.picker = di.picker;
21167     /**
21168      * @event select
21169      * @param {DatePicker} picker
21170      * @param {Date} date
21171      */
21172     this.relayEvents(di, ["select"]);
21173     this.on('beforeshow', function(){
21174         if(this.picker){
21175             this.picker.hideMonthPicker(false);
21176         }
21177     }, this);
21178 };
21179 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21180     cls:'x-date-menu'
21181 });/*
21182  * Based on:
21183  * Ext JS Library 1.1.1
21184  * Copyright(c) 2006-2007, Ext JS, LLC.
21185  *
21186  * Originally Released Under LGPL - original licence link has changed is not relivant.
21187  *
21188  * Fork - LGPL
21189  * <script type="text/javascript">
21190  */
21191  
21192
21193 /**
21194  * @class Roo.menu.ColorMenu
21195  * @extends Roo.menu.Menu
21196  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21197  * @constructor
21198  * Creates a new ColorMenu
21199  * @param {Object} config Configuration options
21200  */
21201 Roo.menu.ColorMenu = function(config){
21202     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21203     this.plain = true;
21204     var ci = new Roo.menu.ColorItem(config);
21205     this.add(ci);
21206     /**
21207      * The {@link Roo.ColorPalette} instance for this ColorMenu
21208      * @type ColorPalette
21209      */
21210     this.palette = ci.palette;
21211     /**
21212      * @event select
21213      * @param {ColorPalette} palette
21214      * @param {String} color
21215      */
21216     this.relayEvents(ci, ["select"]);
21217 };
21218 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21219  * Based on:
21220  * Ext JS Library 1.1.1
21221  * Copyright(c) 2006-2007, Ext JS, LLC.
21222  *
21223  * Originally Released Under LGPL - original licence link has changed is not relivant.
21224  *
21225  * Fork - LGPL
21226  * <script type="text/javascript">
21227  */
21228  
21229 /**
21230  * @class Roo.form.Field
21231  * @extends Roo.BoxComponent
21232  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21233  * @constructor
21234  * Creates a new Field
21235  * @param {Object} config Configuration options
21236  */
21237 Roo.form.Field = function(config){
21238     Roo.form.Field.superclass.constructor.call(this, config);
21239 };
21240
21241 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21242     /**
21243      * @cfg {String} fieldLabel Label to use when rendering a form.
21244      */
21245        /**
21246      * @cfg {String} qtip Mouse over tip
21247      */
21248      
21249     /**
21250      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21251      */
21252     invalidClass : "x-form-invalid",
21253     /**
21254      * @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")
21255      */
21256     invalidText : "The value in this field is invalid",
21257     /**
21258      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21259      */
21260     focusClass : "x-form-focus",
21261     /**
21262      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21263       automatic validation (defaults to "keyup").
21264      */
21265     validationEvent : "keyup",
21266     /**
21267      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21268      */
21269     validateOnBlur : true,
21270     /**
21271      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21272      */
21273     validationDelay : 250,
21274     /**
21275      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21276      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21277      */
21278     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21279     /**
21280      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21281      */
21282     fieldClass : "x-form-field",
21283     /**
21284      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21285      *<pre>
21286 Value         Description
21287 -----------   ----------------------------------------------------------------------
21288 qtip          Display a quick tip when the user hovers over the field
21289 title         Display a default browser title attribute popup
21290 under         Add a block div beneath the field containing the error text
21291 side          Add an error icon to the right of the field with a popup on hover
21292 [element id]  Add the error text directly to the innerHTML of the specified element
21293 </pre>
21294      */
21295     msgTarget : 'qtip',
21296     /**
21297      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21298      */
21299     msgFx : 'normal',
21300
21301     /**
21302      * @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.
21303      */
21304     readOnly : false,
21305
21306     /**
21307      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21308      */
21309     disabled : false,
21310
21311     /**
21312      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21313      */
21314     inputType : undefined,
21315     
21316     /**
21317      * @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).
21318          */
21319         tabIndex : undefined,
21320         
21321     // private
21322     isFormField : true,
21323
21324     // private
21325     hasFocus : false,
21326     /**
21327      * @property {Roo.Element} fieldEl
21328      * Element Containing the rendered Field (with label etc.)
21329      */
21330     /**
21331      * @cfg {Mixed} value A value to initialize this field with.
21332      */
21333     value : undefined,
21334
21335     /**
21336      * @cfg {String} name The field's HTML name attribute.
21337      */
21338     /**
21339      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21340      */
21341
21342         // private ??
21343         initComponent : function(){
21344         Roo.form.Field.superclass.initComponent.call(this);
21345         this.addEvents({
21346             /**
21347              * @event focus
21348              * Fires when this field receives input focus.
21349              * @param {Roo.form.Field} this
21350              */
21351             focus : true,
21352             /**
21353              * @event blur
21354              * Fires when this field loses input focus.
21355              * @param {Roo.form.Field} this
21356              */
21357             blur : true,
21358             /**
21359              * @event specialkey
21360              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21361              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21362              * @param {Roo.form.Field} this
21363              * @param {Roo.EventObject} e The event object
21364              */
21365             specialkey : true,
21366             /**
21367              * @event change
21368              * Fires just before the field blurs if the field value has changed.
21369              * @param {Roo.form.Field} this
21370              * @param {Mixed} newValue The new value
21371              * @param {Mixed} oldValue The original value
21372              */
21373             change : true,
21374             /**
21375              * @event invalid
21376              * Fires after the field has been marked as invalid.
21377              * @param {Roo.form.Field} this
21378              * @param {String} msg The validation message
21379              */
21380             invalid : true,
21381             /**
21382              * @event valid
21383              * Fires after the field has been validated with no errors.
21384              * @param {Roo.form.Field} this
21385              */
21386             valid : true,
21387              /**
21388              * @event keyup
21389              * Fires after the key up
21390              * @param {Roo.form.Field} this
21391              * @param {Roo.EventObject}  e The event Object
21392              */
21393             keyup : true
21394         });
21395     },
21396
21397     /**
21398      * Returns the name attribute of the field if available
21399      * @return {String} name The field name
21400      */
21401     getName: function(){
21402          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21403     },
21404
21405     // private
21406     onRender : function(ct, position){
21407         Roo.form.Field.superclass.onRender.call(this, ct, position);
21408         if(!this.el){
21409             var cfg = this.getAutoCreate();
21410             if(!cfg.name){
21411                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21412             }
21413             if (!cfg.name.length) {
21414                 delete cfg.name;
21415             }
21416             if(this.inputType){
21417                 cfg.type = this.inputType;
21418             }
21419             this.el = ct.createChild(cfg, position);
21420         }
21421         var type = this.el.dom.type;
21422         if(type){
21423             if(type == 'password'){
21424                 type = 'text';
21425             }
21426             this.el.addClass('x-form-'+type);
21427         }
21428         if(this.readOnly){
21429             this.el.dom.readOnly = true;
21430         }
21431         if(this.tabIndex !== undefined){
21432             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21433         }
21434
21435         this.el.addClass([this.fieldClass, this.cls]);
21436         this.initValue();
21437     },
21438
21439     /**
21440      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21441      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21442      * @return {Roo.form.Field} this
21443      */
21444     applyTo : function(target){
21445         this.allowDomMove = false;
21446         this.el = Roo.get(target);
21447         this.render(this.el.dom.parentNode);
21448         return this;
21449     },
21450
21451     // private
21452     initValue : function(){
21453         if(this.value !== undefined){
21454             this.setValue(this.value);
21455         }else if(this.el.dom.value.length > 0){
21456             this.setValue(this.el.dom.value);
21457         }
21458     },
21459
21460     /**
21461      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21462      */
21463     isDirty : function() {
21464         if(this.disabled) {
21465             return false;
21466         }
21467         return String(this.getValue()) !== String(this.originalValue);
21468     },
21469
21470     // private
21471     afterRender : function(){
21472         Roo.form.Field.superclass.afterRender.call(this);
21473         this.initEvents();
21474     },
21475
21476     // private
21477     fireKey : function(e){
21478         //Roo.log('field ' + e.getKey());
21479         if(e.isNavKeyPress()){
21480             this.fireEvent("specialkey", this, e);
21481         }
21482     },
21483
21484     /**
21485      * Resets the current field value to the originally loaded value and clears any validation messages
21486      */
21487     reset : function(){
21488         this.setValue(this.originalValue);
21489         this.clearInvalid();
21490     },
21491
21492     // private
21493     initEvents : function(){
21494         // safari killled keypress - so keydown is now used..
21495         this.el.on("keydown" , this.fireKey,  this);
21496         this.el.on("focus", this.onFocus,  this);
21497         this.el.on("blur", this.onBlur,  this);
21498         this.el.relayEvent('keyup', this);
21499
21500         // reference to original value for reset
21501         this.originalValue = this.getValue();
21502     },
21503
21504     // private
21505     onFocus : function(){
21506         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21507             this.el.addClass(this.focusClass);
21508         }
21509         if(!this.hasFocus){
21510             this.hasFocus = true;
21511             this.startValue = this.getValue();
21512             this.fireEvent("focus", this);
21513         }
21514     },
21515
21516     beforeBlur : Roo.emptyFn,
21517
21518     // private
21519     onBlur : function(){
21520         this.beforeBlur();
21521         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21522             this.el.removeClass(this.focusClass);
21523         }
21524         this.hasFocus = false;
21525         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21526             this.validate();
21527         }
21528         var v = this.getValue();
21529         if(String(v) !== String(this.startValue)){
21530             this.fireEvent('change', this, v, this.startValue);
21531         }
21532         this.fireEvent("blur", this);
21533     },
21534
21535     /**
21536      * Returns whether or not the field value is currently valid
21537      * @param {Boolean} preventMark True to disable marking the field invalid
21538      * @return {Boolean} True if the value is valid, else false
21539      */
21540     isValid : function(preventMark){
21541         if(this.disabled){
21542             return true;
21543         }
21544         var restore = this.preventMark;
21545         this.preventMark = preventMark === true;
21546         var v = this.validateValue(this.processValue(this.getRawValue()));
21547         this.preventMark = restore;
21548         return v;
21549     },
21550
21551     /**
21552      * Validates the field value
21553      * @return {Boolean} True if the value is valid, else false
21554      */
21555     validate : function(){
21556         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21557             this.clearInvalid();
21558             return true;
21559         }
21560         return false;
21561     },
21562
21563     processValue : function(value){
21564         return value;
21565     },
21566
21567     // private
21568     // Subclasses should provide the validation implementation by overriding this
21569     validateValue : function(value){
21570         return true;
21571     },
21572
21573     /**
21574      * Mark this field as invalid
21575      * @param {String} msg The validation message
21576      */
21577     markInvalid : function(msg){
21578         if(!this.rendered || this.preventMark){ // not rendered
21579             return;
21580         }
21581         this.el.addClass(this.invalidClass);
21582         msg = msg || this.invalidText;
21583         switch(this.msgTarget){
21584             case 'qtip':
21585                 this.el.dom.qtip = msg;
21586                 this.el.dom.qclass = 'x-form-invalid-tip';
21587                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21588                     Roo.QuickTips.enable();
21589                 }
21590                 break;
21591             case 'title':
21592                 this.el.dom.title = msg;
21593                 break;
21594             case 'under':
21595                 if(!this.errorEl){
21596                     var elp = this.el.findParent('.x-form-element', 5, true);
21597                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21598                     this.errorEl.setWidth(elp.getWidth(true)-20);
21599                 }
21600                 this.errorEl.update(msg);
21601                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21602                 break;
21603             case 'side':
21604                 if(!this.errorIcon){
21605                     var elp = this.el.findParent('.x-form-element', 5, true);
21606                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21607                 }
21608                 this.alignErrorIcon();
21609                 this.errorIcon.dom.qtip = msg;
21610                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21611                 this.errorIcon.show();
21612                 this.on('resize', this.alignErrorIcon, this);
21613                 break;
21614             default:
21615                 var t = Roo.getDom(this.msgTarget);
21616                 t.innerHTML = msg;
21617                 t.style.display = this.msgDisplay;
21618                 break;
21619         }
21620         this.fireEvent('invalid', this, msg);
21621     },
21622
21623     // private
21624     alignErrorIcon : function(){
21625         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21626     },
21627
21628     /**
21629      * Clear any invalid styles/messages for this field
21630      */
21631     clearInvalid : function(){
21632         if(!this.rendered || this.preventMark){ // not rendered
21633             return;
21634         }
21635         this.el.removeClass(this.invalidClass);
21636         switch(this.msgTarget){
21637             case 'qtip':
21638                 this.el.dom.qtip = '';
21639                 break;
21640             case 'title':
21641                 this.el.dom.title = '';
21642                 break;
21643             case 'under':
21644                 if(this.errorEl){
21645                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21646                 }
21647                 break;
21648             case 'side':
21649                 if(this.errorIcon){
21650                     this.errorIcon.dom.qtip = '';
21651                     this.errorIcon.hide();
21652                     this.un('resize', this.alignErrorIcon, this);
21653                 }
21654                 break;
21655             default:
21656                 var t = Roo.getDom(this.msgTarget);
21657                 t.innerHTML = '';
21658                 t.style.display = 'none';
21659                 break;
21660         }
21661         this.fireEvent('valid', this);
21662     },
21663
21664     /**
21665      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21666      * @return {Mixed} value The field value
21667      */
21668     getRawValue : function(){
21669         var v = this.el.getValue();
21670         
21671         return v;
21672     },
21673
21674     /**
21675      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21676      * @return {Mixed} value The field value
21677      */
21678     getValue : function(){
21679         var v = this.el.getValue();
21680          
21681         return v;
21682     },
21683
21684     /**
21685      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21686      * @param {Mixed} value The value to set
21687      */
21688     setRawValue : function(v){
21689         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21690     },
21691
21692     /**
21693      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21694      * @param {Mixed} value The value to set
21695      */
21696     setValue : function(v){
21697         this.value = v;
21698         if(this.rendered){
21699             this.el.dom.value = (v === null || v === undefined ? '' : v);
21700              this.validate();
21701         }
21702     },
21703
21704     adjustSize : function(w, h){
21705         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21706         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21707         return s;
21708     },
21709
21710     adjustWidth : function(tag, w){
21711         tag = tag.toLowerCase();
21712         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21713             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21714                 if(tag == 'input'){
21715                     return w + 2;
21716                 }
21717                 if(tag == 'textarea'){
21718                     return w-2;
21719                 }
21720             }else if(Roo.isOpera){
21721                 if(tag == 'input'){
21722                     return w + 2;
21723                 }
21724                 if(tag == 'textarea'){
21725                     return w-2;
21726                 }
21727             }
21728         }
21729         return w;
21730     }
21731 });
21732
21733
21734 // anything other than normal should be considered experimental
21735 Roo.form.Field.msgFx = {
21736     normal : {
21737         show: function(msgEl, f){
21738             msgEl.setDisplayed('block');
21739         },
21740
21741         hide : function(msgEl, f){
21742             msgEl.setDisplayed(false).update('');
21743         }
21744     },
21745
21746     slide : {
21747         show: function(msgEl, f){
21748             msgEl.slideIn('t', {stopFx:true});
21749         },
21750
21751         hide : function(msgEl, f){
21752             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21753         }
21754     },
21755
21756     slideRight : {
21757         show: function(msgEl, f){
21758             msgEl.fixDisplay();
21759             msgEl.alignTo(f.el, 'tl-tr');
21760             msgEl.slideIn('l', {stopFx:true});
21761         },
21762
21763         hide : function(msgEl, f){
21764             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21765         }
21766     }
21767 };/*
21768  * Based on:
21769  * Ext JS Library 1.1.1
21770  * Copyright(c) 2006-2007, Ext JS, LLC.
21771  *
21772  * Originally Released Under LGPL - original licence link has changed is not relivant.
21773  *
21774  * Fork - LGPL
21775  * <script type="text/javascript">
21776  */
21777  
21778
21779 /**
21780  * @class Roo.form.TextField
21781  * @extends Roo.form.Field
21782  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21783  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21784  * @constructor
21785  * Creates a new TextField
21786  * @param {Object} config Configuration options
21787  */
21788 Roo.form.TextField = function(config){
21789     Roo.form.TextField.superclass.constructor.call(this, config);
21790     this.addEvents({
21791         /**
21792          * @event autosize
21793          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21794          * according to the default logic, but this event provides a hook for the developer to apply additional
21795          * logic at runtime to resize the field if needed.
21796              * @param {Roo.form.Field} this This text field
21797              * @param {Number} width The new field width
21798              */
21799         autosize : true
21800     });
21801 };
21802
21803 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21804     /**
21805      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21806      */
21807     grow : false,
21808     /**
21809      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21810      */
21811     growMin : 30,
21812     /**
21813      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21814      */
21815     growMax : 800,
21816     /**
21817      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21818      */
21819     vtype : null,
21820     /**
21821      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21822      */
21823     maskRe : null,
21824     /**
21825      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21826      */
21827     disableKeyFilter : false,
21828     /**
21829      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21830      */
21831     allowBlank : true,
21832     /**
21833      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21834      */
21835     minLength : 0,
21836     /**
21837      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21838      */
21839     maxLength : Number.MAX_VALUE,
21840     /**
21841      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21842      */
21843     minLengthText : "The minimum length for this field is {0}",
21844     /**
21845      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21846      */
21847     maxLengthText : "The maximum length for this field is {0}",
21848     /**
21849      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21850      */
21851     selectOnFocus : false,
21852     /**
21853      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21854      */
21855     blankText : "This field is required",
21856     /**
21857      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21858      * If available, this function will be called only after the basic validators all return true, and will be passed the
21859      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21860      */
21861     validator : null,
21862     /**
21863      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21864      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21865      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21866      */
21867     regex : null,
21868     /**
21869      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21870      */
21871     regexText : "",
21872     /**
21873      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21874      */
21875     emptyText : null,
21876    
21877
21878     // private
21879     initEvents : function()
21880     {
21881         if (this.emptyText) {
21882             this.el.attr('placeholder', this.emptyText);
21883         }
21884         
21885         Roo.form.TextField.superclass.initEvents.call(this);
21886         if(this.validationEvent == 'keyup'){
21887             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21888             this.el.on('keyup', this.filterValidation, this);
21889         }
21890         else if(this.validationEvent !== false){
21891             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21892         }
21893         
21894         if(this.selectOnFocus){
21895             this.on("focus", this.preFocus, this);
21896             
21897         }
21898         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21899             this.el.on("keypress", this.filterKeys, this);
21900         }
21901         if(this.grow){
21902             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21903             this.el.on("click", this.autoSize,  this);
21904         }
21905         if(this.el.is('input[type=password]') && Roo.isSafari){
21906             this.el.on('keydown', this.SafariOnKeyDown, this);
21907         }
21908     },
21909
21910     processValue : function(value){
21911         if(this.stripCharsRe){
21912             var newValue = value.replace(this.stripCharsRe, '');
21913             if(newValue !== value){
21914                 this.setRawValue(newValue);
21915                 return newValue;
21916             }
21917         }
21918         return value;
21919     },
21920
21921     filterValidation : function(e){
21922         if(!e.isNavKeyPress()){
21923             this.validationTask.delay(this.validationDelay);
21924         }
21925     },
21926
21927     // private
21928     onKeyUp : function(e){
21929         if(!e.isNavKeyPress()){
21930             this.autoSize();
21931         }
21932     },
21933
21934     /**
21935      * Resets the current field value to the originally-loaded value and clears any validation messages.
21936      *  
21937      */
21938     reset : function(){
21939         Roo.form.TextField.superclass.reset.call(this);
21940        
21941     },
21942
21943     
21944     // private
21945     preFocus : function(){
21946         
21947         if(this.selectOnFocus){
21948             this.el.dom.select();
21949         }
21950     },
21951
21952     
21953     // private
21954     filterKeys : function(e){
21955         var k = e.getKey();
21956         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21957             return;
21958         }
21959         var c = e.getCharCode(), cc = String.fromCharCode(c);
21960         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21961             return;
21962         }
21963         if(!this.maskRe.test(cc)){
21964             e.stopEvent();
21965         }
21966     },
21967
21968     setValue : function(v){
21969         
21970         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21971         
21972         this.autoSize();
21973     },
21974
21975     /**
21976      * Validates a value according to the field's validation rules and marks the field as invalid
21977      * if the validation fails
21978      * @param {Mixed} value The value to validate
21979      * @return {Boolean} True if the value is valid, else false
21980      */
21981     validateValue : function(value){
21982         if(value.length < 1)  { // if it's blank
21983              if(this.allowBlank){
21984                 this.clearInvalid();
21985                 return true;
21986              }else{
21987                 this.markInvalid(this.blankText);
21988                 return false;
21989              }
21990         }
21991         if(value.length < this.minLength){
21992             this.markInvalid(String.format(this.minLengthText, this.minLength));
21993             return false;
21994         }
21995         if(value.length > this.maxLength){
21996             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21997             return false;
21998         }
21999         if(this.vtype){
22000             var vt = Roo.form.VTypes;
22001             if(!vt[this.vtype](value, this)){
22002                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
22003                 return false;
22004             }
22005         }
22006         if(typeof this.validator == "function"){
22007             var msg = this.validator(value);
22008             if(msg !== true){
22009                 this.markInvalid(msg);
22010                 return false;
22011             }
22012         }
22013         if(this.regex && !this.regex.test(value)){
22014             this.markInvalid(this.regexText);
22015             return false;
22016         }
22017         return true;
22018     },
22019
22020     /**
22021      * Selects text in this field
22022      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22023      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22024      */
22025     selectText : function(start, end){
22026         var v = this.getRawValue();
22027         if(v.length > 0){
22028             start = start === undefined ? 0 : start;
22029             end = end === undefined ? v.length : end;
22030             var d = this.el.dom;
22031             if(d.setSelectionRange){
22032                 d.setSelectionRange(start, end);
22033             }else if(d.createTextRange){
22034                 var range = d.createTextRange();
22035                 range.moveStart("character", start);
22036                 range.moveEnd("character", v.length-end);
22037                 range.select();
22038             }
22039         }
22040     },
22041
22042     /**
22043      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22044      * This only takes effect if grow = true, and fires the autosize event.
22045      */
22046     autoSize : function(){
22047         if(!this.grow || !this.rendered){
22048             return;
22049         }
22050         if(!this.metrics){
22051             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22052         }
22053         var el = this.el;
22054         var v = el.dom.value;
22055         var d = document.createElement('div');
22056         d.appendChild(document.createTextNode(v));
22057         v = d.innerHTML;
22058         d = null;
22059         v += "&#160;";
22060         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22061         this.el.setWidth(w);
22062         this.fireEvent("autosize", this, w);
22063     },
22064     
22065     // private
22066     SafariOnKeyDown : function(event)
22067     {
22068         // this is a workaround for a password hang bug on chrome/ webkit.
22069         
22070         var isSelectAll = false;
22071         
22072         if(this.el.dom.selectionEnd > 0){
22073             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22074         }
22075         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22076             event.preventDefault();
22077             this.setValue('');
22078             return;
22079         }
22080         
22081         if(isSelectAll){ // backspace and delete key
22082             
22083             event.preventDefault();
22084             // this is very hacky as keydown always get's upper case.
22085             //
22086             var cc = String.fromCharCode(event.getCharCode());
22087             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
22088             
22089         }
22090         
22091         
22092     }
22093 });/*
22094  * Based on:
22095  * Ext JS Library 1.1.1
22096  * Copyright(c) 2006-2007, Ext JS, LLC.
22097  *
22098  * Originally Released Under LGPL - original licence link has changed is not relivant.
22099  *
22100  * Fork - LGPL
22101  * <script type="text/javascript">
22102  */
22103  
22104 /**
22105  * @class Roo.form.Hidden
22106  * @extends Roo.form.TextField
22107  * Simple Hidden element used on forms 
22108  * 
22109  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22110  * 
22111  * @constructor
22112  * Creates a new Hidden form element.
22113  * @param {Object} config Configuration options
22114  */
22115
22116
22117
22118 // easy hidden field...
22119 Roo.form.Hidden = function(config){
22120     Roo.form.Hidden.superclass.constructor.call(this, config);
22121 };
22122   
22123 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22124     fieldLabel:      '',
22125     inputType:      'hidden',
22126     width:          50,
22127     allowBlank:     true,
22128     labelSeparator: '',
22129     hidden:         true,
22130     itemCls :       'x-form-item-display-none'
22131
22132
22133 });
22134
22135
22136 /*
22137  * Based on:
22138  * Ext JS Library 1.1.1
22139  * Copyright(c) 2006-2007, Ext JS, LLC.
22140  *
22141  * Originally Released Under LGPL - original licence link has changed is not relivant.
22142  *
22143  * Fork - LGPL
22144  * <script type="text/javascript">
22145  */
22146  
22147 /**
22148  * @class Roo.form.TriggerField
22149  * @extends Roo.form.TextField
22150  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22151  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22152  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22153  * for which you can provide a custom implementation.  For example:
22154  * <pre><code>
22155 var trigger = new Roo.form.TriggerField();
22156 trigger.onTriggerClick = myTriggerFn;
22157 trigger.applyTo('my-field');
22158 </code></pre>
22159  *
22160  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22161  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22162  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22163  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22164  * @constructor
22165  * Create a new TriggerField.
22166  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22167  * to the base TextField)
22168  */
22169 Roo.form.TriggerField = function(config){
22170     this.mimicing = false;
22171     Roo.form.TriggerField.superclass.constructor.call(this, config);
22172 };
22173
22174 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22175     /**
22176      * @cfg {String} triggerClass A CSS class to apply to the trigger
22177      */
22178     /**
22179      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22180      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22181      */
22182     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22183     /**
22184      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22185      */
22186     hideTrigger:false,
22187
22188     /** @cfg {Boolean} grow @hide */
22189     /** @cfg {Number} growMin @hide */
22190     /** @cfg {Number} growMax @hide */
22191
22192     /**
22193      * @hide 
22194      * @method
22195      */
22196     autoSize: Roo.emptyFn,
22197     // private
22198     monitorTab : true,
22199     // private
22200     deferHeight : true,
22201
22202     
22203     actionMode : 'wrap',
22204     // private
22205     onResize : function(w, h){
22206         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22207         if(typeof w == 'number'){
22208             var x = w - this.trigger.getWidth();
22209             this.el.setWidth(this.adjustWidth('input', x));
22210             this.trigger.setStyle('left', x+'px');
22211         }
22212     },
22213
22214     // private
22215     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22216
22217     // private
22218     getResizeEl : function(){
22219         return this.wrap;
22220     },
22221
22222     // private
22223     getPositionEl : function(){
22224         return this.wrap;
22225     },
22226
22227     // private
22228     alignErrorIcon : function(){
22229         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22230     },
22231
22232     // private
22233     onRender : function(ct, position){
22234         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22235         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22236         this.trigger = this.wrap.createChild(this.triggerConfig ||
22237                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22238         if(this.hideTrigger){
22239             this.trigger.setDisplayed(false);
22240         }
22241         this.initTrigger();
22242         if(!this.width){
22243             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22244         }
22245     },
22246
22247     // private
22248     initTrigger : function(){
22249         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22250         this.trigger.addClassOnOver('x-form-trigger-over');
22251         this.trigger.addClassOnClick('x-form-trigger-click');
22252     },
22253
22254     // private
22255     onDestroy : function(){
22256         if(this.trigger){
22257             this.trigger.removeAllListeners();
22258             this.trigger.remove();
22259         }
22260         if(this.wrap){
22261             this.wrap.remove();
22262         }
22263         Roo.form.TriggerField.superclass.onDestroy.call(this);
22264     },
22265
22266     // private
22267     onFocus : function(){
22268         Roo.form.TriggerField.superclass.onFocus.call(this);
22269         if(!this.mimicing){
22270             this.wrap.addClass('x-trigger-wrap-focus');
22271             this.mimicing = true;
22272             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22273             if(this.monitorTab){
22274                 this.el.on("keydown", this.checkTab, this);
22275             }
22276         }
22277     },
22278
22279     // private
22280     checkTab : function(e){
22281         if(e.getKey() == e.TAB){
22282             this.triggerBlur();
22283         }
22284     },
22285
22286     // private
22287     onBlur : function(){
22288         // do nothing
22289     },
22290
22291     // private
22292     mimicBlur : function(e, t){
22293         if(!this.wrap.contains(t) && this.validateBlur()){
22294             this.triggerBlur();
22295         }
22296     },
22297
22298     // private
22299     triggerBlur : function(){
22300         this.mimicing = false;
22301         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22302         if(this.monitorTab){
22303             this.el.un("keydown", this.checkTab, this);
22304         }
22305         this.wrap.removeClass('x-trigger-wrap-focus');
22306         Roo.form.TriggerField.superclass.onBlur.call(this);
22307     },
22308
22309     // private
22310     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22311     validateBlur : function(e, t){
22312         return true;
22313     },
22314
22315     // private
22316     onDisable : function(){
22317         Roo.form.TriggerField.superclass.onDisable.call(this);
22318         if(this.wrap){
22319             this.wrap.addClass('x-item-disabled');
22320         }
22321     },
22322
22323     // private
22324     onEnable : function(){
22325         Roo.form.TriggerField.superclass.onEnable.call(this);
22326         if(this.wrap){
22327             this.wrap.removeClass('x-item-disabled');
22328         }
22329     },
22330
22331     // private
22332     onShow : function(){
22333         var ae = this.getActionEl();
22334         
22335         if(ae){
22336             ae.dom.style.display = '';
22337             ae.dom.style.visibility = 'visible';
22338         }
22339     },
22340
22341     // private
22342     
22343     onHide : function(){
22344         var ae = this.getActionEl();
22345         ae.dom.style.display = 'none';
22346     },
22347
22348     /**
22349      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22350      * by an implementing function.
22351      * @method
22352      * @param {EventObject} e
22353      */
22354     onTriggerClick : Roo.emptyFn
22355 });
22356
22357 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22358 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22359 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22360 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22361     initComponent : function(){
22362         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22363
22364         this.triggerConfig = {
22365             tag:'span', cls:'x-form-twin-triggers', cn:[
22366             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22367             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22368         ]};
22369     },
22370
22371     getTrigger : function(index){
22372         return this.triggers[index];
22373     },
22374
22375     initTrigger : function(){
22376         var ts = this.trigger.select('.x-form-trigger', true);
22377         this.wrap.setStyle('overflow', 'hidden');
22378         var triggerField = this;
22379         ts.each(function(t, all, index){
22380             t.hide = function(){
22381                 var w = triggerField.wrap.getWidth();
22382                 this.dom.style.display = 'none';
22383                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22384             };
22385             t.show = function(){
22386                 var w = triggerField.wrap.getWidth();
22387                 this.dom.style.display = '';
22388                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22389             };
22390             var triggerIndex = 'Trigger'+(index+1);
22391
22392             if(this['hide'+triggerIndex]){
22393                 t.dom.style.display = 'none';
22394             }
22395             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22396             t.addClassOnOver('x-form-trigger-over');
22397             t.addClassOnClick('x-form-trigger-click');
22398         }, this);
22399         this.triggers = ts.elements;
22400     },
22401
22402     onTrigger1Click : Roo.emptyFn,
22403     onTrigger2Click : Roo.emptyFn
22404 });/*
22405  * Based on:
22406  * Ext JS Library 1.1.1
22407  * Copyright(c) 2006-2007, Ext JS, LLC.
22408  *
22409  * Originally Released Under LGPL - original licence link has changed is not relivant.
22410  *
22411  * Fork - LGPL
22412  * <script type="text/javascript">
22413  */
22414  
22415 /**
22416  * @class Roo.form.TextArea
22417  * @extends Roo.form.TextField
22418  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22419  * support for auto-sizing.
22420  * @constructor
22421  * Creates a new TextArea
22422  * @param {Object} config Configuration options
22423  */
22424 Roo.form.TextArea = function(config){
22425     Roo.form.TextArea.superclass.constructor.call(this, config);
22426     // these are provided exchanges for backwards compat
22427     // minHeight/maxHeight were replaced by growMin/growMax to be
22428     // compatible with TextField growing config values
22429     if(this.minHeight !== undefined){
22430         this.growMin = this.minHeight;
22431     }
22432     if(this.maxHeight !== undefined){
22433         this.growMax = this.maxHeight;
22434     }
22435 };
22436
22437 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22438     /**
22439      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22440      */
22441     growMin : 60,
22442     /**
22443      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22444      */
22445     growMax: 1000,
22446     /**
22447      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22448      * in the field (equivalent to setting overflow: hidden, defaults to false)
22449      */
22450     preventScrollbars: false,
22451     /**
22452      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22453      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22454      */
22455
22456     // private
22457     onRender : function(ct, position){
22458         if(!this.el){
22459             this.defaultAutoCreate = {
22460                 tag: "textarea",
22461                 style:"width:300px;height:60px;",
22462                 autocomplete: "off"
22463             };
22464         }
22465         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22466         if(this.grow){
22467             this.textSizeEl = Roo.DomHelper.append(document.body, {
22468                 tag: "pre", cls: "x-form-grow-sizer"
22469             });
22470             if(this.preventScrollbars){
22471                 this.el.setStyle("overflow", "hidden");
22472             }
22473             this.el.setHeight(this.growMin);
22474         }
22475     },
22476
22477     onDestroy : function(){
22478         if(this.textSizeEl){
22479             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22480         }
22481         Roo.form.TextArea.superclass.onDestroy.call(this);
22482     },
22483
22484     // private
22485     onKeyUp : function(e){
22486         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22487             this.autoSize();
22488         }
22489     },
22490
22491     /**
22492      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22493      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22494      */
22495     autoSize : function(){
22496         if(!this.grow || !this.textSizeEl){
22497             return;
22498         }
22499         var el = this.el;
22500         var v = el.dom.value;
22501         var ts = this.textSizeEl;
22502
22503         ts.innerHTML = '';
22504         ts.appendChild(document.createTextNode(v));
22505         v = ts.innerHTML;
22506
22507         Roo.fly(ts).setWidth(this.el.getWidth());
22508         if(v.length < 1){
22509             v = "&#160;&#160;";
22510         }else{
22511             if(Roo.isIE){
22512                 v = v.replace(/\n/g, '<p>&#160;</p>');
22513             }
22514             v += "&#160;\n&#160;";
22515         }
22516         ts.innerHTML = v;
22517         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22518         if(h != this.lastHeight){
22519             this.lastHeight = h;
22520             this.el.setHeight(h);
22521             this.fireEvent("autosize", this, h);
22522         }
22523     }
22524 });/*
22525  * Based on:
22526  * Ext JS Library 1.1.1
22527  * Copyright(c) 2006-2007, Ext JS, LLC.
22528  *
22529  * Originally Released Under LGPL - original licence link has changed is not relivant.
22530  *
22531  * Fork - LGPL
22532  * <script type="text/javascript">
22533  */
22534  
22535
22536 /**
22537  * @class Roo.form.NumberField
22538  * @extends Roo.form.TextField
22539  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22540  * @constructor
22541  * Creates a new NumberField
22542  * @param {Object} config Configuration options
22543  */
22544 Roo.form.NumberField = function(config){
22545     Roo.form.NumberField.superclass.constructor.call(this, config);
22546 };
22547
22548 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22549     /**
22550      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22551      */
22552     fieldClass: "x-form-field x-form-num-field",
22553     /**
22554      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22555      */
22556     allowDecimals : true,
22557     /**
22558      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22559      */
22560     decimalSeparator : ".",
22561     /**
22562      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22563      */
22564     decimalPrecision : 2,
22565     /**
22566      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22567      */
22568     allowNegative : true,
22569     /**
22570      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22571      */
22572     minValue : Number.NEGATIVE_INFINITY,
22573     /**
22574      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22575      */
22576     maxValue : Number.MAX_VALUE,
22577     /**
22578      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22579      */
22580     minText : "The minimum value for this field is {0}",
22581     /**
22582      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22583      */
22584     maxText : "The maximum value for this field is {0}",
22585     /**
22586      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22587      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22588      */
22589     nanText : "{0} is not a valid number",
22590
22591     // private
22592     initEvents : function(){
22593         Roo.form.NumberField.superclass.initEvents.call(this);
22594         var allowed = "0123456789";
22595         if(this.allowDecimals){
22596             allowed += this.decimalSeparator;
22597         }
22598         if(this.allowNegative){
22599             allowed += "-";
22600         }
22601         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22602         var keyPress = function(e){
22603             var k = e.getKey();
22604             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22605                 return;
22606             }
22607             var c = e.getCharCode();
22608             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22609                 e.stopEvent();
22610             }
22611         };
22612         this.el.on("keypress", keyPress, this);
22613     },
22614
22615     // private
22616     validateValue : function(value){
22617         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22618             return false;
22619         }
22620         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22621              return true;
22622         }
22623         var num = this.parseValue(value);
22624         if(isNaN(num)){
22625             this.markInvalid(String.format(this.nanText, value));
22626             return false;
22627         }
22628         if(num < this.minValue){
22629             this.markInvalid(String.format(this.minText, this.minValue));
22630             return false;
22631         }
22632         if(num > this.maxValue){
22633             this.markInvalid(String.format(this.maxText, this.maxValue));
22634             return false;
22635         }
22636         return true;
22637     },
22638
22639     getValue : function(){
22640         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22641     },
22642
22643     // private
22644     parseValue : function(value){
22645         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22646         return isNaN(value) ? '' : value;
22647     },
22648
22649     // private
22650     fixPrecision : function(value){
22651         var nan = isNaN(value);
22652         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22653             return nan ? '' : value;
22654         }
22655         return parseFloat(value).toFixed(this.decimalPrecision);
22656     },
22657
22658     setValue : function(v){
22659         v = this.fixPrecision(v);
22660         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22661     },
22662
22663     // private
22664     decimalPrecisionFcn : function(v){
22665         return Math.floor(v);
22666     },
22667
22668     beforeBlur : function(){
22669         var v = this.parseValue(this.getRawValue());
22670         if(v){
22671             this.setValue(v);
22672         }
22673     }
22674 });/*
22675  * Based on:
22676  * Ext JS Library 1.1.1
22677  * Copyright(c) 2006-2007, Ext JS, LLC.
22678  *
22679  * Originally Released Under LGPL - original licence link has changed is not relivant.
22680  *
22681  * Fork - LGPL
22682  * <script type="text/javascript">
22683  */
22684  
22685 /**
22686  * @class Roo.form.DateField
22687  * @extends Roo.form.TriggerField
22688  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22689 * @constructor
22690 * Create a new DateField
22691 * @param {Object} config
22692  */
22693 Roo.form.DateField = function(config){
22694     Roo.form.DateField.superclass.constructor.call(this, config);
22695     
22696       this.addEvents({
22697          
22698         /**
22699          * @event select
22700          * Fires when a date is selected
22701              * @param {Roo.form.DateField} combo This combo box
22702              * @param {Date} date The date selected
22703              */
22704         'select' : true
22705          
22706     });
22707     
22708     
22709     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22710     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22711     this.ddMatch = null;
22712     if(this.disabledDates){
22713         var dd = this.disabledDates;
22714         var re = "(?:";
22715         for(var i = 0; i < dd.length; i++){
22716             re += dd[i];
22717             if(i != dd.length-1) re += "|";
22718         }
22719         this.ddMatch = new RegExp(re + ")");
22720     }
22721 };
22722
22723 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22724     /**
22725      * @cfg {String} format
22726      * The default date format string which can be overriden for localization support.  The format must be
22727      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22728      */
22729     format : "m/d/y",
22730     /**
22731      * @cfg {String} altFormats
22732      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22733      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22734      */
22735     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22736     /**
22737      * @cfg {Array} disabledDays
22738      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22739      */
22740     disabledDays : null,
22741     /**
22742      * @cfg {String} disabledDaysText
22743      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22744      */
22745     disabledDaysText : "Disabled",
22746     /**
22747      * @cfg {Array} disabledDates
22748      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22749      * expression so they are very powerful. Some examples:
22750      * <ul>
22751      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22752      * <li>["03/08", "09/16"] would disable those days for every year</li>
22753      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22754      * <li>["03/../2006"] would disable every day in March 2006</li>
22755      * <li>["^03"] would disable every day in every March</li>
22756      * </ul>
22757      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22758      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22759      */
22760     disabledDates : null,
22761     /**
22762      * @cfg {String} disabledDatesText
22763      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22764      */
22765     disabledDatesText : "Disabled",
22766     /**
22767      * @cfg {Date/String} minValue
22768      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22769      * valid format (defaults to null).
22770      */
22771     minValue : null,
22772     /**
22773      * @cfg {Date/String} maxValue
22774      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22775      * valid format (defaults to null).
22776      */
22777     maxValue : null,
22778     /**
22779      * @cfg {String} minText
22780      * The error text to display when the date in the cell is before minValue (defaults to
22781      * 'The date in this field must be after {minValue}').
22782      */
22783     minText : "The date in this field must be equal to or after {0}",
22784     /**
22785      * @cfg {String} maxText
22786      * The error text to display when the date in the cell is after maxValue (defaults to
22787      * 'The date in this field must be before {maxValue}').
22788      */
22789     maxText : "The date in this field must be equal to or before {0}",
22790     /**
22791      * @cfg {String} invalidText
22792      * The error text to display when the date in the field is invalid (defaults to
22793      * '{value} is not a valid date - it must be in the format {format}').
22794      */
22795     invalidText : "{0} is not a valid date - it must be in the format {1}",
22796     /**
22797      * @cfg {String} triggerClass
22798      * An additional CSS class used to style the trigger button.  The trigger will always get the
22799      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22800      * which displays a calendar icon).
22801      */
22802     triggerClass : 'x-form-date-trigger',
22803     
22804
22805     /**
22806      * @cfg {Boolean} useIso
22807      * if enabled, then the date field will use a hidden field to store the 
22808      * real value as iso formated date. default (false)
22809      */ 
22810     useIso : false,
22811     /**
22812      * @cfg {String/Object} autoCreate
22813      * A DomHelper element spec, or true for a default element spec (defaults to
22814      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22815      */ 
22816     // private
22817     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22818     
22819     // private
22820     hiddenField: false,
22821     
22822     onRender : function(ct, position)
22823     {
22824         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22825         if (this.useIso) {
22826             //this.el.dom.removeAttribute('name'); 
22827             Roo.log("Changing name?");
22828             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22829             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22830                     'before', true);
22831             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22832             // prevent input submission
22833             this.hiddenName = this.name;
22834         }
22835             
22836             
22837     },
22838     
22839     // private
22840     validateValue : function(value)
22841     {
22842         value = this.formatDate(value);
22843         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22844             Roo.log('super failed');
22845             return false;
22846         }
22847         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22848              return true;
22849         }
22850         var svalue = value;
22851         value = this.parseDate(value);
22852         if(!value){
22853             Roo.log('parse date failed' + svalue);
22854             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22855             return false;
22856         }
22857         var time = value.getTime();
22858         if(this.minValue && time < this.minValue.getTime()){
22859             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22860             return false;
22861         }
22862         if(this.maxValue && time > this.maxValue.getTime()){
22863             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22864             return false;
22865         }
22866         if(this.disabledDays){
22867             var day = value.getDay();
22868             for(var i = 0; i < this.disabledDays.length; i++) {
22869                 if(day === this.disabledDays[i]){
22870                     this.markInvalid(this.disabledDaysText);
22871                     return false;
22872                 }
22873             }
22874         }
22875         var fvalue = this.formatDate(value);
22876         if(this.ddMatch && this.ddMatch.test(fvalue)){
22877             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22878             return false;
22879         }
22880         return true;
22881     },
22882
22883     // private
22884     // Provides logic to override the default TriggerField.validateBlur which just returns true
22885     validateBlur : function(){
22886         return !this.menu || !this.menu.isVisible();
22887     },
22888     
22889     getName: function()
22890     {
22891         // returns hidden if it's set..
22892         if (!this.rendered) {return ''};
22893         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22894         
22895     },
22896
22897     /**
22898      * Returns the current date value of the date field.
22899      * @return {Date} The date value
22900      */
22901     getValue : function(){
22902         
22903         return  this.hiddenField ?
22904                 this.hiddenField.value :
22905                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22906     },
22907
22908     /**
22909      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22910      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22911      * (the default format used is "m/d/y").
22912      * <br />Usage:
22913      * <pre><code>
22914 //All of these calls set the same date value (May 4, 2006)
22915
22916 //Pass a date object:
22917 var dt = new Date('5/4/06');
22918 dateField.setValue(dt);
22919
22920 //Pass a date string (default format):
22921 dateField.setValue('5/4/06');
22922
22923 //Pass a date string (custom format):
22924 dateField.format = 'Y-m-d';
22925 dateField.setValue('2006-5-4');
22926 </code></pre>
22927      * @param {String/Date} date The date or valid date string
22928      */
22929     setValue : function(date){
22930         if (this.hiddenField) {
22931             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22932         }
22933         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22934         // make sure the value field is always stored as a date..
22935         this.value = this.parseDate(date);
22936         
22937         
22938     },
22939
22940     // private
22941     parseDate : function(value){
22942         if(!value || value instanceof Date){
22943             return value;
22944         }
22945         var v = Date.parseDate(value, this.format);
22946          if (!v && this.useIso) {
22947             v = Date.parseDate(value, 'Y-m-d');
22948         }
22949         if(!v && this.altFormats){
22950             if(!this.altFormatsArray){
22951                 this.altFormatsArray = this.altFormats.split("|");
22952             }
22953             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22954                 v = Date.parseDate(value, this.altFormatsArray[i]);
22955             }
22956         }
22957         return v;
22958     },
22959
22960     // private
22961     formatDate : function(date, fmt){
22962         return (!date || !(date instanceof Date)) ?
22963                date : date.dateFormat(fmt || this.format);
22964     },
22965
22966     // private
22967     menuListeners : {
22968         select: function(m, d){
22969             
22970             this.setValue(d);
22971             this.fireEvent('select', this, d);
22972         },
22973         show : function(){ // retain focus styling
22974             this.onFocus();
22975         },
22976         hide : function(){
22977             this.focus.defer(10, this);
22978             var ml = this.menuListeners;
22979             this.menu.un("select", ml.select,  this);
22980             this.menu.un("show", ml.show,  this);
22981             this.menu.un("hide", ml.hide,  this);
22982         }
22983     },
22984
22985     // private
22986     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22987     onTriggerClick : function(){
22988         if(this.disabled){
22989             return;
22990         }
22991         if(this.menu == null){
22992             this.menu = new Roo.menu.DateMenu();
22993         }
22994         Roo.apply(this.menu.picker,  {
22995             showClear: this.allowBlank,
22996             minDate : this.minValue,
22997             maxDate : this.maxValue,
22998             disabledDatesRE : this.ddMatch,
22999             disabledDatesText : this.disabledDatesText,
23000             disabledDays : this.disabledDays,
23001             disabledDaysText : this.disabledDaysText,
23002             format : this.useIso ? 'Y-m-d' : this.format,
23003             minText : String.format(this.minText, this.formatDate(this.minValue)),
23004             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23005         });
23006         this.menu.on(Roo.apply({}, this.menuListeners, {
23007             scope:this
23008         }));
23009         this.menu.picker.setValue(this.getValue() || new Date());
23010         this.menu.show(this.el, "tl-bl?");
23011     },
23012
23013     beforeBlur : function(){
23014         var v = this.parseDate(this.getRawValue());
23015         if(v){
23016             this.setValue(v);
23017         }
23018     }
23019
23020     /** @cfg {Boolean} grow @hide */
23021     /** @cfg {Number} growMin @hide */
23022     /** @cfg {Number} growMax @hide */
23023     /**
23024      * @hide
23025      * @method autoSize
23026      */
23027 });/*
23028  * Based on:
23029  * Ext JS Library 1.1.1
23030  * Copyright(c) 2006-2007, Ext JS, LLC.
23031  *
23032  * Originally Released Under LGPL - original licence link has changed is not relivant.
23033  *
23034  * Fork - LGPL
23035  * <script type="text/javascript">
23036  */
23037  
23038 /**
23039  * @class Roo.form.MonthField
23040  * @extends Roo.form.TriggerField
23041  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23042 * @constructor
23043 * Create a new MonthField
23044 * @param {Object} config
23045  */
23046 Roo.form.MonthField = function(config){
23047     
23048     Roo.form.MonthField.superclass.constructor.call(this, config);
23049     
23050       this.addEvents({
23051          
23052         /**
23053          * @event select
23054          * Fires when a date is selected
23055              * @param {Roo.form.MonthFieeld} combo This combo box
23056              * @param {Date} date The date selected
23057              */
23058         'select' : true
23059          
23060     });
23061     
23062     
23063     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23064     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23065     this.ddMatch = null;
23066     if(this.disabledDates){
23067         var dd = this.disabledDates;
23068         var re = "(?:";
23069         for(var i = 0; i < dd.length; i++){
23070             re += dd[i];
23071             if(i != dd.length-1) re += "|";
23072         }
23073         this.ddMatch = new RegExp(re + ")");
23074     }
23075 };
23076
23077 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23078     /**
23079      * @cfg {String} format
23080      * The default date format string which can be overriden for localization support.  The format must be
23081      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23082      */
23083     format : "M Y",
23084     /**
23085      * @cfg {String} altFormats
23086      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23087      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23088      */
23089     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23090     /**
23091      * @cfg {Array} disabledDays
23092      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23093      */
23094     disabledDays : [0,1,2,3,4,5,6],
23095     /**
23096      * @cfg {String} disabledDaysText
23097      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23098      */
23099     disabledDaysText : "Disabled",
23100     /**
23101      * @cfg {Array} disabledDates
23102      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23103      * expression so they are very powerful. Some examples:
23104      * <ul>
23105      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23106      * <li>["03/08", "09/16"] would disable those days for every year</li>
23107      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23108      * <li>["03/../2006"] would disable every day in March 2006</li>
23109      * <li>["^03"] would disable every day in every March</li>
23110      * </ul>
23111      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23112      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23113      */
23114     disabledDates : null,
23115     /**
23116      * @cfg {String} disabledDatesText
23117      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23118      */
23119     disabledDatesText : "Disabled",
23120     /**
23121      * @cfg {Date/String} minValue
23122      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23123      * valid format (defaults to null).
23124      */
23125     minValue : null,
23126     /**
23127      * @cfg {Date/String} maxValue
23128      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23129      * valid format (defaults to null).
23130      */
23131     maxValue : null,
23132     /**
23133      * @cfg {String} minText
23134      * The error text to display when the date in the cell is before minValue (defaults to
23135      * 'The date in this field must be after {minValue}').
23136      */
23137     minText : "The date in this field must be equal to or after {0}",
23138     /**
23139      * @cfg {String} maxTextf
23140      * The error text to display when the date in the cell is after maxValue (defaults to
23141      * 'The date in this field must be before {maxValue}').
23142      */
23143     maxText : "The date in this field must be equal to or before {0}",
23144     /**
23145      * @cfg {String} invalidText
23146      * The error text to display when the date in the field is invalid (defaults to
23147      * '{value} is not a valid date - it must be in the format {format}').
23148      */
23149     invalidText : "{0} is not a valid date - it must be in the format {1}",
23150     /**
23151      * @cfg {String} triggerClass
23152      * An additional CSS class used to style the trigger button.  The trigger will always get the
23153      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23154      * which displays a calendar icon).
23155      */
23156     triggerClass : 'x-form-date-trigger',
23157     
23158
23159     /**
23160      * @cfg {Boolean} useIso
23161      * if enabled, then the date field will use a hidden field to store the 
23162      * real value as iso formated date. default (true)
23163      */ 
23164     useIso : true,
23165     /**
23166      * @cfg {String/Object} autoCreate
23167      * A DomHelper element spec, or true for a default element spec (defaults to
23168      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23169      */ 
23170     // private
23171     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23172     
23173     // private
23174     hiddenField: false,
23175     
23176     hideMonthPicker : false,
23177     
23178     onRender : function(ct, position)
23179     {
23180         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23181         if (this.useIso) {
23182             this.el.dom.removeAttribute('name'); 
23183             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23184                     'before', true);
23185             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23186             // prevent input submission
23187             this.hiddenName = this.name;
23188         }
23189             
23190             
23191     },
23192     
23193     // private
23194     validateValue : function(value)
23195     {
23196         value = this.formatDate(value);
23197         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23198             return false;
23199         }
23200         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23201              return true;
23202         }
23203         var svalue = value;
23204         value = this.parseDate(value);
23205         if(!value){
23206             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23207             return false;
23208         }
23209         var time = value.getTime();
23210         if(this.minValue && time < this.minValue.getTime()){
23211             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23212             return false;
23213         }
23214         if(this.maxValue && time > this.maxValue.getTime()){
23215             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23216             return false;
23217         }
23218         /*if(this.disabledDays){
23219             var day = value.getDay();
23220             for(var i = 0; i < this.disabledDays.length; i++) {
23221                 if(day === this.disabledDays[i]){
23222                     this.markInvalid(this.disabledDaysText);
23223                     return false;
23224                 }
23225             }
23226         }
23227         */
23228         var fvalue = this.formatDate(value);
23229         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23230             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23231             return false;
23232         }
23233         */
23234         return true;
23235     },
23236
23237     // private
23238     // Provides logic to override the default TriggerField.validateBlur which just returns true
23239     validateBlur : function(){
23240         return !this.menu || !this.menu.isVisible();
23241     },
23242
23243     /**
23244      * Returns the current date value of the date field.
23245      * @return {Date} The date value
23246      */
23247     getValue : function(){
23248         
23249         
23250         
23251         return  this.hiddenField ?
23252                 this.hiddenField.value :
23253                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23254     },
23255
23256     /**
23257      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23258      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23259      * (the default format used is "m/d/y").
23260      * <br />Usage:
23261      * <pre><code>
23262 //All of these calls set the same date value (May 4, 2006)
23263
23264 //Pass a date object:
23265 var dt = new Date('5/4/06');
23266 monthField.setValue(dt);
23267
23268 //Pass a date string (default format):
23269 monthField.setValue('5/4/06');
23270
23271 //Pass a date string (custom format):
23272 monthField.format = 'Y-m-d';
23273 monthField.setValue('2006-5-4');
23274 </code></pre>
23275      * @param {String/Date} date The date or valid date string
23276      */
23277     setValue : function(date){
23278         Roo.log('month setValue' + date);
23279         // can only be first of month..
23280         
23281         var val = this.parseDate(date);
23282         
23283         if (this.hiddenField) {
23284             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23285         }
23286         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23287         this.value = this.parseDate(date);
23288     },
23289
23290     // private
23291     parseDate : function(value){
23292         if(!value || value instanceof Date){
23293             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23294             return value;
23295         }
23296         var v = Date.parseDate(value, this.format);
23297         if (!v && this.useIso) {
23298             v = Date.parseDate(value, 'Y-m-d');
23299         }
23300         if (v) {
23301             // 
23302             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23303         }
23304         
23305         
23306         if(!v && this.altFormats){
23307             if(!this.altFormatsArray){
23308                 this.altFormatsArray = this.altFormats.split("|");
23309             }
23310             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23311                 v = Date.parseDate(value, this.altFormatsArray[i]);
23312             }
23313         }
23314         return v;
23315     },
23316
23317     // private
23318     formatDate : function(date, fmt){
23319         return (!date || !(date instanceof Date)) ?
23320                date : date.dateFormat(fmt || this.format);
23321     },
23322
23323     // private
23324     menuListeners : {
23325         select: function(m, d){
23326             this.setValue(d);
23327             this.fireEvent('select', this, d);
23328         },
23329         show : function(){ // retain focus styling
23330             this.onFocus();
23331         },
23332         hide : function(){
23333             this.focus.defer(10, this);
23334             var ml = this.menuListeners;
23335             this.menu.un("select", ml.select,  this);
23336             this.menu.un("show", ml.show,  this);
23337             this.menu.un("hide", ml.hide,  this);
23338         }
23339     },
23340     // private
23341     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23342     onTriggerClick : function(){
23343         if(this.disabled){
23344             return;
23345         }
23346         if(this.menu == null){
23347             this.menu = new Roo.menu.DateMenu();
23348            
23349         }
23350         
23351         Roo.apply(this.menu.picker,  {
23352             
23353             showClear: this.allowBlank,
23354             minDate : this.minValue,
23355             maxDate : this.maxValue,
23356             disabledDatesRE : this.ddMatch,
23357             disabledDatesText : this.disabledDatesText,
23358             
23359             format : this.useIso ? 'Y-m-d' : this.format,
23360             minText : String.format(this.minText, this.formatDate(this.minValue)),
23361             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23362             
23363         });
23364          this.menu.on(Roo.apply({}, this.menuListeners, {
23365             scope:this
23366         }));
23367        
23368         
23369         var m = this.menu;
23370         var p = m.picker;
23371         
23372         // hide month picker get's called when we called by 'before hide';
23373         
23374         var ignorehide = true;
23375         p.hideMonthPicker  = function(disableAnim){
23376             if (ignorehide) {
23377                 return;
23378             }
23379              if(this.monthPicker){
23380                 Roo.log("hideMonthPicker called");
23381                 if(disableAnim === true){
23382                     this.monthPicker.hide();
23383                 }else{
23384                     this.monthPicker.slideOut('t', {duration:.2});
23385                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23386                     p.fireEvent("select", this, this.value);
23387                     m.hide();
23388                 }
23389             }
23390         }
23391         
23392         Roo.log('picker set value');
23393         Roo.log(this.getValue());
23394         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23395         m.show(this.el, 'tl-bl?');
23396         ignorehide  = false;
23397         // this will trigger hideMonthPicker..
23398         
23399         
23400         // hidden the day picker
23401         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23402         
23403         
23404         
23405       
23406         
23407         p.showMonthPicker.defer(100, p);
23408     
23409         
23410        
23411     },
23412
23413     beforeBlur : function(){
23414         var v = this.parseDate(this.getRawValue());
23415         if(v){
23416             this.setValue(v);
23417         }
23418     }
23419
23420     /** @cfg {Boolean} grow @hide */
23421     /** @cfg {Number} growMin @hide */
23422     /** @cfg {Number} growMax @hide */
23423     /**
23424      * @hide
23425      * @method autoSize
23426      */
23427 });/*
23428  * Based on:
23429  * Ext JS Library 1.1.1
23430  * Copyright(c) 2006-2007, Ext JS, LLC.
23431  *
23432  * Originally Released Under LGPL - original licence link has changed is not relivant.
23433  *
23434  * Fork - LGPL
23435  * <script type="text/javascript">
23436  */
23437  
23438
23439 /**
23440  * @class Roo.form.ComboBox
23441  * @extends Roo.form.TriggerField
23442  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23443  * @constructor
23444  * Create a new ComboBox.
23445  * @param {Object} config Configuration options
23446  */
23447 Roo.form.ComboBox = function(config){
23448     Roo.form.ComboBox.superclass.constructor.call(this, config);
23449     this.addEvents({
23450         /**
23451          * @event expand
23452          * Fires when the dropdown list is expanded
23453              * @param {Roo.form.ComboBox} combo This combo box
23454              */
23455         'expand' : true,
23456         /**
23457          * @event collapse
23458          * Fires when the dropdown list is collapsed
23459              * @param {Roo.form.ComboBox} combo This combo box
23460              */
23461         'collapse' : true,
23462         /**
23463          * @event beforeselect
23464          * Fires before a list item is selected. Return false to cancel the selection.
23465              * @param {Roo.form.ComboBox} combo This combo box
23466              * @param {Roo.data.Record} record The data record returned from the underlying store
23467              * @param {Number} index The index of the selected item in the dropdown list
23468              */
23469         'beforeselect' : true,
23470         /**
23471          * @event select
23472          * Fires when a list item is selected
23473              * @param {Roo.form.ComboBox} combo This combo box
23474              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23475              * @param {Number} index The index of the selected item in the dropdown list
23476              */
23477         'select' : true,
23478         /**
23479          * @event beforequery
23480          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23481          * The event object passed has these properties:
23482              * @param {Roo.form.ComboBox} combo This combo box
23483              * @param {String} query The query
23484              * @param {Boolean} forceAll true to force "all" query
23485              * @param {Boolean} cancel true to cancel the query
23486              * @param {Object} e The query event object
23487              */
23488         'beforequery': true,
23489          /**
23490          * @event add
23491          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23492              * @param {Roo.form.ComboBox} combo This combo box
23493              */
23494         'add' : true,
23495         /**
23496          * @event edit
23497          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23498              * @param {Roo.form.ComboBox} combo This combo box
23499              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23500              */
23501         'edit' : true
23502         
23503         
23504     });
23505     if(this.transform){
23506         this.allowDomMove = false;
23507         var s = Roo.getDom(this.transform);
23508         if(!this.hiddenName){
23509             this.hiddenName = s.name;
23510         }
23511         if(!this.store){
23512             this.mode = 'local';
23513             var d = [], opts = s.options;
23514             for(var i = 0, len = opts.length;i < len; i++){
23515                 var o = opts[i];
23516                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23517                 if(o.selected) {
23518                     this.value = value;
23519                 }
23520                 d.push([value, o.text]);
23521             }
23522             this.store = new Roo.data.SimpleStore({
23523                 'id': 0,
23524                 fields: ['value', 'text'],
23525                 data : d
23526             });
23527             this.valueField = 'value';
23528             this.displayField = 'text';
23529         }
23530         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23531         if(!this.lazyRender){
23532             this.target = true;
23533             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23534             s.parentNode.removeChild(s); // remove it
23535             this.render(this.el.parentNode);
23536         }else{
23537             s.parentNode.removeChild(s); // remove it
23538         }
23539
23540     }
23541     if (this.store) {
23542         this.store = Roo.factory(this.store, Roo.data);
23543     }
23544     
23545     this.selectedIndex = -1;
23546     if(this.mode == 'local'){
23547         if(config.queryDelay === undefined){
23548             this.queryDelay = 10;
23549         }
23550         if(config.minChars === undefined){
23551             this.minChars = 0;
23552         }
23553     }
23554 };
23555
23556 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23557     /**
23558      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23559      */
23560     /**
23561      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23562      * rendering into an Roo.Editor, defaults to false)
23563      */
23564     /**
23565      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23566      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23567      */
23568     /**
23569      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23570      */
23571     /**
23572      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23573      * the dropdown list (defaults to undefined, with no header element)
23574      */
23575
23576      /**
23577      * @cfg {String/Roo.Template} tpl The template to use to render the output
23578      */
23579      
23580     // private
23581     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23582     /**
23583      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23584      */
23585     listWidth: undefined,
23586     /**
23587      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23588      * mode = 'remote' or 'text' if mode = 'local')
23589      */
23590     displayField: undefined,
23591     /**
23592      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23593      * mode = 'remote' or 'value' if mode = 'local'). 
23594      * Note: use of a valueField requires the user make a selection
23595      * in order for a value to be mapped.
23596      */
23597     valueField: undefined,
23598     
23599     
23600     /**
23601      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23602      * field's data value (defaults to the underlying DOM element's name)
23603      */
23604     hiddenName: undefined,
23605     /**
23606      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23607      */
23608     listClass: '',
23609     /**
23610      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23611      */
23612     selectedClass: 'x-combo-selected',
23613     /**
23614      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23615      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23616      * which displays a downward arrow icon).
23617      */
23618     triggerClass : 'x-form-arrow-trigger',
23619     /**
23620      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23621      */
23622     shadow:'sides',
23623     /**
23624      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23625      * anchor positions (defaults to 'tl-bl')
23626      */
23627     listAlign: 'tl-bl?',
23628     /**
23629      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23630      */
23631     maxHeight: 300,
23632     /**
23633      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23634      * query specified by the allQuery config option (defaults to 'query')
23635      */
23636     triggerAction: 'query',
23637     /**
23638      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23639      * (defaults to 4, does not apply if editable = false)
23640      */
23641     minChars : 4,
23642     /**
23643      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23644      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23645      */
23646     typeAhead: false,
23647     /**
23648      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23649      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23650      */
23651     queryDelay: 500,
23652     /**
23653      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23654      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23655      */
23656     pageSize: 0,
23657     /**
23658      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23659      * when editable = true (defaults to false)
23660      */
23661     selectOnFocus:false,
23662     /**
23663      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23664      */
23665     queryParam: 'query',
23666     /**
23667      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23668      * when mode = 'remote' (defaults to 'Loading...')
23669      */
23670     loadingText: 'Loading...',
23671     /**
23672      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23673      */
23674     resizable: false,
23675     /**
23676      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23677      */
23678     handleHeight : 8,
23679     /**
23680      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23681      * traditional select (defaults to true)
23682      */
23683     editable: true,
23684     /**
23685      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23686      */
23687     allQuery: '',
23688     /**
23689      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23690      */
23691     mode: 'remote',
23692     /**
23693      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23694      * listWidth has a higher value)
23695      */
23696     minListWidth : 70,
23697     /**
23698      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23699      * allow the user to set arbitrary text into the field (defaults to false)
23700      */
23701     forceSelection:false,
23702     /**
23703      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23704      * if typeAhead = true (defaults to 250)
23705      */
23706     typeAheadDelay : 250,
23707     /**
23708      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23709      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23710      */
23711     valueNotFoundText : undefined,
23712     /**
23713      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23714      */
23715     blockFocus : false,
23716     
23717     /**
23718      * @cfg {Boolean} disableClear Disable showing of clear button.
23719      */
23720     disableClear : false,
23721     /**
23722      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23723      */
23724     alwaysQuery : false,
23725     
23726     //private
23727     addicon : false,
23728     editicon: false,
23729     
23730     // element that contains real text value.. (when hidden is used..)
23731      
23732     // private
23733     onRender : function(ct, position){
23734         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23735         if(this.hiddenName){
23736             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23737                     'before', true);
23738             this.hiddenField.value =
23739                 this.hiddenValue !== undefined ? this.hiddenValue :
23740                 this.value !== undefined ? this.value : '';
23741
23742             // prevent input submission
23743             this.el.dom.removeAttribute('name');
23744              
23745              
23746         }
23747         if(Roo.isGecko){
23748             this.el.dom.setAttribute('autocomplete', 'off');
23749         }
23750
23751         var cls = 'x-combo-list';
23752
23753         this.list = new Roo.Layer({
23754             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23755         });
23756
23757         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23758         this.list.setWidth(lw);
23759         this.list.swallowEvent('mousewheel');
23760         this.assetHeight = 0;
23761
23762         if(this.title){
23763             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23764             this.assetHeight += this.header.getHeight();
23765         }
23766
23767         this.innerList = this.list.createChild({cls:cls+'-inner'});
23768         this.innerList.on('mouseover', this.onViewOver, this);
23769         this.innerList.on('mousemove', this.onViewMove, this);
23770         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23771         
23772         if(this.allowBlank && !this.pageSize && !this.disableClear){
23773             this.footer = this.list.createChild({cls:cls+'-ft'});
23774             this.pageTb = new Roo.Toolbar(this.footer);
23775            
23776         }
23777         if(this.pageSize){
23778             this.footer = this.list.createChild({cls:cls+'-ft'});
23779             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23780                     {pageSize: this.pageSize});
23781             
23782         }
23783         
23784         if (this.pageTb && this.allowBlank && !this.disableClear) {
23785             var _this = this;
23786             this.pageTb.add(new Roo.Toolbar.Fill(), {
23787                 cls: 'x-btn-icon x-btn-clear',
23788                 text: '&#160;',
23789                 handler: function()
23790                 {
23791                     _this.collapse();
23792                     _this.clearValue();
23793                     _this.onSelect(false, -1);
23794                 }
23795             });
23796         }
23797         if (this.footer) {
23798             this.assetHeight += this.footer.getHeight();
23799         }
23800         
23801
23802         if(!this.tpl){
23803             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23804         }
23805
23806         this.view = new Roo.View(this.innerList, this.tpl, {
23807             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23808         });
23809
23810         this.view.on('click', this.onViewClick, this);
23811
23812         this.store.on('beforeload', this.onBeforeLoad, this);
23813         this.store.on('load', this.onLoad, this);
23814         this.store.on('loadexception', this.onLoadException, this);
23815
23816         if(this.resizable){
23817             this.resizer = new Roo.Resizable(this.list,  {
23818                pinned:true, handles:'se'
23819             });
23820             this.resizer.on('resize', function(r, w, h){
23821                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23822                 this.listWidth = w;
23823                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23824                 this.restrictHeight();
23825             }, this);
23826             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23827         }
23828         if(!this.editable){
23829             this.editable = true;
23830             this.setEditable(false);
23831         }  
23832         
23833         
23834         if (typeof(this.events.add.listeners) != 'undefined') {
23835             
23836             this.addicon = this.wrap.createChild(
23837                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23838        
23839             this.addicon.on('click', function(e) {
23840                 this.fireEvent('add', this);
23841             }, this);
23842         }
23843         if (typeof(this.events.edit.listeners) != 'undefined') {
23844             
23845             this.editicon = this.wrap.createChild(
23846                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23847             if (this.addicon) {
23848                 this.editicon.setStyle('margin-left', '40px');
23849             }
23850             this.editicon.on('click', function(e) {
23851                 
23852                 // we fire even  if inothing is selected..
23853                 this.fireEvent('edit', this, this.lastData );
23854                 
23855             }, this);
23856         }
23857         
23858         
23859         
23860     },
23861
23862     // private
23863     initEvents : function(){
23864         Roo.form.ComboBox.superclass.initEvents.call(this);
23865
23866         this.keyNav = new Roo.KeyNav(this.el, {
23867             "up" : function(e){
23868                 this.inKeyMode = true;
23869                 this.selectPrev();
23870             },
23871
23872             "down" : function(e){
23873                 if(!this.isExpanded()){
23874                     this.onTriggerClick();
23875                 }else{
23876                     this.inKeyMode = true;
23877                     this.selectNext();
23878                 }
23879             },
23880
23881             "enter" : function(e){
23882                 this.onViewClick();
23883                 //return true;
23884             },
23885
23886             "esc" : function(e){
23887                 this.collapse();
23888             },
23889
23890             "tab" : function(e){
23891                 this.onViewClick(false);
23892                 this.fireEvent("specialkey", this, e);
23893                 return true;
23894             },
23895
23896             scope : this,
23897
23898             doRelay : function(foo, bar, hname){
23899                 if(hname == 'down' || this.scope.isExpanded()){
23900                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23901                 }
23902                 return true;
23903             },
23904
23905             forceKeyDown: true
23906         });
23907         this.queryDelay = Math.max(this.queryDelay || 10,
23908                 this.mode == 'local' ? 10 : 250);
23909         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23910         if(this.typeAhead){
23911             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23912         }
23913         if(this.editable !== false){
23914             this.el.on("keyup", this.onKeyUp, this);
23915         }
23916         if(this.forceSelection){
23917             this.on('blur', this.doForce, this);
23918         }
23919     },
23920
23921     onDestroy : function(){
23922         if(this.view){
23923             this.view.setStore(null);
23924             this.view.el.removeAllListeners();
23925             this.view.el.remove();
23926             this.view.purgeListeners();
23927         }
23928         if(this.list){
23929             this.list.destroy();
23930         }
23931         if(this.store){
23932             this.store.un('beforeload', this.onBeforeLoad, this);
23933             this.store.un('load', this.onLoad, this);
23934             this.store.un('loadexception', this.onLoadException, this);
23935         }
23936         Roo.form.ComboBox.superclass.onDestroy.call(this);
23937     },
23938
23939     // private
23940     fireKey : function(e){
23941         if(e.isNavKeyPress() && !this.list.isVisible()){
23942             this.fireEvent("specialkey", this, e);
23943         }
23944     },
23945
23946     // private
23947     onResize: function(w, h){
23948         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23949         
23950         if(typeof w != 'number'){
23951             // we do not handle it!?!?
23952             return;
23953         }
23954         var tw = this.trigger.getWidth();
23955         tw += this.addicon ? this.addicon.getWidth() : 0;
23956         tw += this.editicon ? this.editicon.getWidth() : 0;
23957         var x = w - tw;
23958         this.el.setWidth( this.adjustWidth('input', x));
23959             
23960         this.trigger.setStyle('left', x+'px');
23961         
23962         if(this.list && this.listWidth === undefined){
23963             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23964             this.list.setWidth(lw);
23965             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23966         }
23967         
23968     
23969         
23970     },
23971
23972     /**
23973      * Allow or prevent the user from directly editing the field text.  If false is passed,
23974      * the user will only be able to select from the items defined in the dropdown list.  This method
23975      * is the runtime equivalent of setting the 'editable' config option at config time.
23976      * @param {Boolean} value True to allow the user to directly edit the field text
23977      */
23978     setEditable : function(value){
23979         if(value == this.editable){
23980             return;
23981         }
23982         this.editable = value;
23983         if(!value){
23984             this.el.dom.setAttribute('readOnly', true);
23985             this.el.on('mousedown', this.onTriggerClick,  this);
23986             this.el.addClass('x-combo-noedit');
23987         }else{
23988             this.el.dom.setAttribute('readOnly', false);
23989             this.el.un('mousedown', this.onTriggerClick,  this);
23990             this.el.removeClass('x-combo-noedit');
23991         }
23992     },
23993
23994     // private
23995     onBeforeLoad : function(){
23996         if(!this.hasFocus){
23997             return;
23998         }
23999         this.innerList.update(this.loadingText ?
24000                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
24001         this.restrictHeight();
24002         this.selectedIndex = -1;
24003     },
24004
24005     // private
24006     onLoad : function(){
24007         if(!this.hasFocus){
24008             return;
24009         }
24010         if(this.store.getCount() > 0){
24011             this.expand();
24012             this.restrictHeight();
24013             if(this.lastQuery == this.allQuery){
24014                 if(this.editable){
24015                     this.el.dom.select();
24016                 }
24017                 if(!this.selectByValue(this.value, true)){
24018                     this.select(0, true);
24019                 }
24020             }else{
24021                 this.selectNext();
24022                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24023                     this.taTask.delay(this.typeAheadDelay);
24024                 }
24025             }
24026         }else{
24027             this.onEmptyResults();
24028         }
24029         //this.el.focus();
24030     },
24031     // private
24032     onLoadException : function()
24033     {
24034         this.collapse();
24035         Roo.log(this.store.reader.jsonData);
24036         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24037             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24038         }
24039         
24040         
24041     },
24042     // private
24043     onTypeAhead : function(){
24044         if(this.store.getCount() > 0){
24045             var r = this.store.getAt(0);
24046             var newValue = r.data[this.displayField];
24047             var len = newValue.length;
24048             var selStart = this.getRawValue().length;
24049             if(selStart != len){
24050                 this.setRawValue(newValue);
24051                 this.selectText(selStart, newValue.length);
24052             }
24053         }
24054     },
24055
24056     // private
24057     onSelect : function(record, index){
24058         if(this.fireEvent('beforeselect', this, record, index) !== false){
24059             this.setFromData(index > -1 ? record.data : false);
24060             this.collapse();
24061             this.fireEvent('select', this, record, index);
24062         }
24063     },
24064
24065     /**
24066      * Returns the currently selected field value or empty string if no value is set.
24067      * @return {String} value The selected value
24068      */
24069     getValue : function(){
24070         if(this.valueField){
24071             return typeof this.value != 'undefined' ? this.value : '';
24072         }else{
24073             return Roo.form.ComboBox.superclass.getValue.call(this);
24074         }
24075     },
24076
24077     /**
24078      * Clears any text/value currently set in the field
24079      */
24080     clearValue : function(){
24081         if(this.hiddenField){
24082             this.hiddenField.value = '';
24083         }
24084         this.value = '';
24085         this.setRawValue('');
24086         this.lastSelectionText = '';
24087         
24088     },
24089
24090     /**
24091      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24092      * will be displayed in the field.  If the value does not match the data value of an existing item,
24093      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24094      * Otherwise the field will be blank (although the value will still be set).
24095      * @param {String} value The value to match
24096      */
24097     setValue : function(v){
24098         var text = v;
24099         if(this.valueField){
24100             var r = this.findRecord(this.valueField, v);
24101             if(r){
24102                 text = r.data[this.displayField];
24103             }else if(this.valueNotFoundText !== undefined){
24104                 text = this.valueNotFoundText;
24105             }
24106         }
24107         this.lastSelectionText = text;
24108         if(this.hiddenField){
24109             this.hiddenField.value = v;
24110         }
24111         Roo.form.ComboBox.superclass.setValue.call(this, text);
24112         this.value = v;
24113     },
24114     /**
24115      * @property {Object} the last set data for the element
24116      */
24117     
24118     lastData : false,
24119     /**
24120      * Sets the value of the field based on a object which is related to the record format for the store.
24121      * @param {Object} value the value to set as. or false on reset?
24122      */
24123     setFromData : function(o){
24124         var dv = ''; // display value
24125         var vv = ''; // value value..
24126         this.lastData = o;
24127         if (this.displayField) {
24128             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24129         } else {
24130             // this is an error condition!!!
24131             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24132         }
24133         
24134         if(this.valueField){
24135             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24136         }
24137         if(this.hiddenField){
24138             this.hiddenField.value = vv;
24139             
24140             this.lastSelectionText = dv;
24141             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24142             this.value = vv;
24143             return;
24144         }
24145         // no hidden field.. - we store the value in 'value', but still display
24146         // display field!!!!
24147         this.lastSelectionText = dv;
24148         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24149         this.value = vv;
24150         
24151         
24152     },
24153     // private
24154     reset : function(){
24155         // overridden so that last data is reset..
24156         this.setValue(this.originalValue);
24157         this.clearInvalid();
24158         this.lastData = false;
24159         if (this.view) {
24160             this.view.clearSelections();
24161         }
24162     },
24163     // private
24164     findRecord : function(prop, value){
24165         var record;
24166         if(this.store.getCount() > 0){
24167             this.store.each(function(r){
24168                 if(r.data[prop] == value){
24169                     record = r;
24170                     return false;
24171                 }
24172                 return true;
24173             });
24174         }
24175         return record;
24176     },
24177     
24178     getName: function()
24179     {
24180         // returns hidden if it's set..
24181         if (!this.rendered) {return ''};
24182         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24183         
24184     },
24185     // private
24186     onViewMove : function(e, t){
24187         this.inKeyMode = false;
24188     },
24189
24190     // private
24191     onViewOver : function(e, t){
24192         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24193             return;
24194         }
24195         var item = this.view.findItemFromChild(t);
24196         if(item){
24197             var index = this.view.indexOf(item);
24198             this.select(index, false);
24199         }
24200     },
24201
24202     // private
24203     onViewClick : function(doFocus)
24204     {
24205         var index = this.view.getSelectedIndexes()[0];
24206         var r = this.store.getAt(index);
24207         if(r){
24208             this.onSelect(r, index);
24209         }
24210         if(doFocus !== false && !this.blockFocus){
24211             this.el.focus();
24212         }
24213     },
24214
24215     // private
24216     restrictHeight : function(){
24217         this.innerList.dom.style.height = '';
24218         var inner = this.innerList.dom;
24219         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24220         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24221         this.list.beginUpdate();
24222         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24223         this.list.alignTo(this.el, this.listAlign);
24224         this.list.endUpdate();
24225     },
24226
24227     // private
24228     onEmptyResults : function(){
24229         this.collapse();
24230     },
24231
24232     /**
24233      * Returns true if the dropdown list is expanded, else false.
24234      */
24235     isExpanded : function(){
24236         return this.list.isVisible();
24237     },
24238
24239     /**
24240      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24241      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24242      * @param {String} value The data value of the item to select
24243      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24244      * selected item if it is not currently in view (defaults to true)
24245      * @return {Boolean} True if the value matched an item in the list, else false
24246      */
24247     selectByValue : function(v, scrollIntoView){
24248         if(v !== undefined && v !== null){
24249             var r = this.findRecord(this.valueField || this.displayField, v);
24250             if(r){
24251                 this.select(this.store.indexOf(r), scrollIntoView);
24252                 return true;
24253             }
24254         }
24255         return false;
24256     },
24257
24258     /**
24259      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24260      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24261      * @param {Number} index The zero-based index of the list item to select
24262      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24263      * selected item if it is not currently in view (defaults to true)
24264      */
24265     select : function(index, scrollIntoView){
24266         this.selectedIndex = index;
24267         this.view.select(index);
24268         if(scrollIntoView !== false){
24269             var el = this.view.getNode(index);
24270             if(el){
24271                 this.innerList.scrollChildIntoView(el, false);
24272             }
24273         }
24274     },
24275
24276     // private
24277     selectNext : function(){
24278         var ct = this.store.getCount();
24279         if(ct > 0){
24280             if(this.selectedIndex == -1){
24281                 this.select(0);
24282             }else if(this.selectedIndex < ct-1){
24283                 this.select(this.selectedIndex+1);
24284             }
24285         }
24286     },
24287
24288     // private
24289     selectPrev : function(){
24290         var ct = this.store.getCount();
24291         if(ct > 0){
24292             if(this.selectedIndex == -1){
24293                 this.select(0);
24294             }else if(this.selectedIndex != 0){
24295                 this.select(this.selectedIndex-1);
24296             }
24297         }
24298     },
24299
24300     // private
24301     onKeyUp : function(e){
24302         if(this.editable !== false && !e.isSpecialKey()){
24303             this.lastKey = e.getKey();
24304             this.dqTask.delay(this.queryDelay);
24305         }
24306     },
24307
24308     // private
24309     validateBlur : function(){
24310         return !this.list || !this.list.isVisible();   
24311     },
24312
24313     // private
24314     initQuery : function(){
24315         this.doQuery(this.getRawValue());
24316     },
24317
24318     // private
24319     doForce : function(){
24320         if(this.el.dom.value.length > 0){
24321             this.el.dom.value =
24322                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24323              
24324         }
24325     },
24326
24327     /**
24328      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24329      * query allowing the query action to be canceled if needed.
24330      * @param {String} query The SQL query to execute
24331      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24332      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24333      * saved in the current store (defaults to false)
24334      */
24335     doQuery : function(q, forceAll){
24336         if(q === undefined || q === null){
24337             q = '';
24338         }
24339         var qe = {
24340             query: q,
24341             forceAll: forceAll,
24342             combo: this,
24343             cancel:false
24344         };
24345         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24346             return false;
24347         }
24348         q = qe.query;
24349         forceAll = qe.forceAll;
24350         if(forceAll === true || (q.length >= this.minChars)){
24351             if(this.lastQuery != q || this.alwaysQuery){
24352                 this.lastQuery = q;
24353                 if(this.mode == 'local'){
24354                     this.selectedIndex = -1;
24355                     if(forceAll){
24356                         this.store.clearFilter();
24357                     }else{
24358                         this.store.filter(this.displayField, q);
24359                     }
24360                     this.onLoad();
24361                 }else{
24362                     this.store.baseParams[this.queryParam] = q;
24363                     this.store.load({
24364                         params: this.getParams(q)
24365                     });
24366                     this.expand();
24367                 }
24368             }else{
24369                 this.selectedIndex = -1;
24370                 this.onLoad();   
24371             }
24372         }
24373     },
24374
24375     // private
24376     getParams : function(q){
24377         var p = {};
24378         //p[this.queryParam] = q;
24379         if(this.pageSize){
24380             p.start = 0;
24381             p.limit = this.pageSize;
24382         }
24383         return p;
24384     },
24385
24386     /**
24387      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24388      */
24389     collapse : function(){
24390         if(!this.isExpanded()){
24391             return;
24392         }
24393         this.list.hide();
24394         Roo.get(document).un('mousedown', this.collapseIf, this);
24395         Roo.get(document).un('mousewheel', this.collapseIf, this);
24396         if (!this.editable) {
24397             Roo.get(document).un('keydown', this.listKeyPress, this);
24398         }
24399         this.fireEvent('collapse', this);
24400     },
24401
24402     // private
24403     collapseIf : function(e){
24404         if(!e.within(this.wrap) && !e.within(this.list)){
24405             this.collapse();
24406         }
24407     },
24408
24409     /**
24410      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24411      */
24412     expand : function(){
24413         if(this.isExpanded() || !this.hasFocus){
24414             return;
24415         }
24416         this.list.alignTo(this.el, this.listAlign);
24417         this.list.show();
24418         Roo.get(document).on('mousedown', this.collapseIf, this);
24419         Roo.get(document).on('mousewheel', this.collapseIf, this);
24420         if (!this.editable) {
24421             Roo.get(document).on('keydown', this.listKeyPress, this);
24422         }
24423         
24424         this.fireEvent('expand', this);
24425     },
24426
24427     // private
24428     // Implements the default empty TriggerField.onTriggerClick function
24429     onTriggerClick : function(){
24430         if(this.disabled){
24431             return;
24432         }
24433         if(this.isExpanded()){
24434             this.collapse();
24435             if (!this.blockFocus) {
24436                 this.el.focus();
24437             }
24438             
24439         }else {
24440             this.hasFocus = true;
24441             if(this.triggerAction == 'all') {
24442                 this.doQuery(this.allQuery, true);
24443             } else {
24444                 this.doQuery(this.getRawValue());
24445             }
24446             if (!this.blockFocus) {
24447                 this.el.focus();
24448             }
24449         }
24450     },
24451     listKeyPress : function(e)
24452     {
24453         //Roo.log('listkeypress');
24454         // scroll to first matching element based on key pres..
24455         if (e.isSpecialKey()) {
24456             return false;
24457         }
24458         var k = String.fromCharCode(e.getKey()).toUpperCase();
24459         //Roo.log(k);
24460         var match  = false;
24461         var csel = this.view.getSelectedNodes();
24462         var cselitem = false;
24463         if (csel.length) {
24464             var ix = this.view.indexOf(csel[0]);
24465             cselitem  = this.store.getAt(ix);
24466             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24467                 cselitem = false;
24468             }
24469             
24470         }
24471         
24472         this.store.each(function(v) { 
24473             if (cselitem) {
24474                 // start at existing selection.
24475                 if (cselitem.id == v.id) {
24476                     cselitem = false;
24477                 }
24478                 return;
24479             }
24480                 
24481             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24482                 match = this.store.indexOf(v);
24483                 return false;
24484             }
24485         }, this);
24486         
24487         if (match === false) {
24488             return true; // no more action?
24489         }
24490         // scroll to?
24491         this.view.select(match);
24492         var sn = Roo.get(this.view.getSelectedNodes()[0])
24493         sn.scrollIntoView(sn.dom.parentNode, false);
24494     }
24495
24496     /** 
24497     * @cfg {Boolean} grow 
24498     * @hide 
24499     */
24500     /** 
24501     * @cfg {Number} growMin 
24502     * @hide 
24503     */
24504     /** 
24505     * @cfg {Number} growMax 
24506     * @hide 
24507     */
24508     /**
24509      * @hide
24510      * @method autoSize
24511      */
24512 });/*
24513  * Copyright(c) 2010-2012, Roo J Solutions Limited
24514  *
24515  * Licence LGPL
24516  *
24517  */
24518
24519 /**
24520  * @class Roo.form.ComboBoxArray
24521  * @extends Roo.form.TextField
24522  * A facebook style adder... for lists of email / people / countries  etc...
24523  * pick multiple items from a combo box, and shows each one.
24524  *
24525  *  Fred [x]  Brian [x]  [Pick another |v]
24526  *
24527  *
24528  *  For this to work: it needs various extra information
24529  *    - normal combo problay has
24530  *      name, hiddenName
24531  *    + displayField, valueField
24532  *
24533  *    For our purpose...
24534  *
24535  *
24536  *   If we change from 'extends' to wrapping...
24537  *   
24538  *  
24539  *
24540  
24541  
24542  * @constructor
24543  * Create a new ComboBoxArray.
24544  * @param {Object} config Configuration options
24545  */
24546  
24547
24548 Roo.form.ComboBoxArray = function(config)
24549 {
24550     
24551     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24552     
24553     this.items = new Roo.util.MixedCollection(false);
24554     
24555     // construct the child combo...
24556     
24557     
24558     
24559     
24560    
24561     
24562 }
24563
24564  
24565 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24566
24567     /**
24568      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24569      */
24570     
24571     lastData : false,
24572     
24573     // behavies liek a hiddne field
24574     inputType:      'hidden',
24575     /**
24576      * @cfg {Number} width The width of the box that displays the selected element
24577      */ 
24578     width:          300,
24579
24580     
24581     
24582     /**
24583      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24584      */
24585     name : false,
24586     /**
24587      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24588      */
24589     hiddenName : false,
24590     
24591     
24592     // private the array of items that are displayed..
24593     items  : false,
24594     // private - the hidden field el.
24595     hiddenEl : false,
24596     // private - the filed el..
24597     el : false,
24598     
24599     //validateValue : function() { return true; }, // all values are ok!
24600     //onAddClick: function() { },
24601     
24602     onRender : function(ct, position) 
24603     {
24604         
24605         // create the standard hidden element
24606         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24607         
24608         
24609         // give fake names to child combo;
24610         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24611         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24612         
24613         this.combo = Roo.factory(this.combo, Roo.form);
24614         this.combo.onRender(ct, position);
24615         if (typeof(this.combo.width) != 'undefined') {
24616             this.combo.onResize(this.combo.width,0);
24617         }
24618         
24619         this.combo.initEvents();
24620         
24621         // assigned so form know we need to do this..
24622         this.store          = this.combo.store;
24623         this.valueField     = this.combo.valueField;
24624         this.displayField   = this.combo.displayField ;
24625         
24626         
24627         this.combo.wrap.addClass('x-cbarray-grp');
24628         
24629         var cbwrap = this.combo.wrap.createChild(
24630             {tag: 'div', cls: 'x-cbarray-cb'},
24631             this.combo.el.dom
24632         );
24633         
24634              
24635         this.hiddenEl = this.combo.wrap.createChild({
24636             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24637         });
24638         this.el = this.combo.wrap.createChild({
24639             tag: 'input',  type:'hidden' , name: this.name, value : ''
24640         });
24641          //   this.el.dom.removeAttribute("name");
24642         
24643         
24644         this.outerWrap = this.combo.wrap;
24645         this.wrap = cbwrap;
24646         
24647         this.outerWrap.setWidth(this.width);
24648         this.outerWrap.dom.removeChild(this.el.dom);
24649         
24650         this.wrap.dom.appendChild(this.el.dom);
24651         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24652         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24653         
24654         this.combo.trigger.setStyle('position','relative');
24655         this.combo.trigger.setStyle('left', '0px');
24656         this.combo.trigger.setStyle('top', '2px');
24657         
24658         this.combo.el.setStyle('vertical-align', 'text-bottom');
24659         
24660         //this.trigger.setStyle('vertical-align', 'top');
24661         
24662         // this should use the code from combo really... on('add' ....)
24663         if (this.adder) {
24664             
24665         
24666             this.adder = this.outerWrap.createChild(
24667                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24668             var _t = this;
24669             this.adder.on('click', function(e) {
24670                 _t.fireEvent('adderclick', this, e);
24671             }, _t);
24672         }
24673         //var _t = this;
24674         //this.adder.on('click', this.onAddClick, _t);
24675         
24676         
24677         this.combo.on('select', function(cb, rec, ix) {
24678             this.addItem(rec.data);
24679             
24680             cb.setValue('');
24681             cb.el.dom.value = '';
24682             //cb.lastData = rec.data;
24683             // add to list
24684             
24685         }, this);
24686         
24687         
24688     },
24689     
24690     
24691     getName: function()
24692     {
24693         // returns hidden if it's set..
24694         if (!this.rendered) {return ''};
24695         return  this.hiddenName ? this.hiddenName : this.name;
24696         
24697     },
24698     
24699     
24700     onResize: function(w, h){
24701         
24702         return;
24703         // not sure if this is needed..
24704         //this.combo.onResize(w,h);
24705         
24706         if(typeof w != 'number'){
24707             // we do not handle it!?!?
24708             return;
24709         }
24710         var tw = this.combo.trigger.getWidth();
24711         tw += this.addicon ? this.addicon.getWidth() : 0;
24712         tw += this.editicon ? this.editicon.getWidth() : 0;
24713         var x = w - tw;
24714         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24715             
24716         this.combo.trigger.setStyle('left', '0px');
24717         
24718         if(this.list && this.listWidth === undefined){
24719             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24720             this.list.setWidth(lw);
24721             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24722         }
24723         
24724     
24725         
24726     },
24727     
24728     addItem: function(rec)
24729     {
24730         var valueField = this.combo.valueField;
24731         var displayField = this.combo.displayField;
24732         if (this.items.indexOfKey(rec[valueField]) > -1) {
24733             //console.log("GOT " + rec.data.id);
24734             return;
24735         }
24736         
24737         var x = new Roo.form.ComboBoxArray.Item({
24738             //id : rec[this.idField],
24739             data : rec,
24740             displayField : displayField ,
24741             tipField : displayField ,
24742             cb : this
24743         });
24744         // use the 
24745         this.items.add(rec[valueField],x);
24746         // add it before the element..
24747         this.updateHiddenEl();
24748         x.render(this.outerWrap, this.wrap.dom);
24749         // add the image handler..
24750     },
24751     
24752     updateHiddenEl : function()
24753     {
24754         this.validate();
24755         if (!this.hiddenEl) {
24756             return;
24757         }
24758         var ar = [];
24759         var idField = this.combo.valueField;
24760         
24761         this.items.each(function(f) {
24762             ar.push(f.data[idField]);
24763            
24764         });
24765         this.hiddenEl.dom.value = ar.join(',');
24766         this.validate();
24767     },
24768     
24769     reset : function()
24770     {
24771         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24772         this.items.each(function(f) {
24773            f.remove(); 
24774         });
24775         this.el.dom.value = '';
24776         if (this.hiddenEl) {
24777             this.hiddenEl.dom.value = '';
24778         }
24779         
24780     },
24781     getValue: function()
24782     {
24783         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24784     },
24785     setValue: function(v) // not a valid action - must use addItems..
24786     {
24787          
24788         this.reset();
24789         
24790         
24791         
24792         if (this.store.isLocal && (typeof(v) == 'string')) {
24793             // then we can use the store to find the values..
24794             // comma seperated at present.. this needs to allow JSON based encoding..
24795             this.hiddenEl.value  = v;
24796             var v_ar = [];
24797             Roo.each(v.split(','), function(k) {
24798                 Roo.log("CHECK " + this.valueField + ',' + k);
24799                 var li = this.store.query(this.valueField, k);
24800                 if (!li.length) {
24801                     return;
24802                 }
24803                 var add = {};
24804                 add[this.valueField] = k;
24805                 add[this.displayField] = li.item(0).data[this.displayField];
24806                 
24807                 this.addItem(add);
24808             }, this) 
24809              
24810         }
24811         if (typeof(v) == 'object') {
24812             // then let's assume it's an array of objects..
24813             Roo.each(v, function(l) {
24814                 this.addItem(l);
24815             }, this);
24816              
24817         }
24818         
24819         
24820     },
24821     setFromData: function(v)
24822     {
24823         // this recieves an object, if setValues is called.
24824         this.reset();
24825         this.el.dom.value = v[this.displayField];
24826         this.hiddenEl.dom.value = v[this.valueField];
24827         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24828             return;
24829         }
24830         var kv = v[this.valueField];
24831         var dv = v[this.displayField];
24832         kv = typeof(kv) != 'string' ? '' : kv;
24833         dv = typeof(dv) != 'string' ? '' : dv;
24834         
24835         
24836         var keys = kv.split(',');
24837         var display = dv.split(',');
24838         for (var i = 0 ; i < keys.length; i++) {
24839             
24840             add = {};
24841             add[this.valueField] = keys[i];
24842             add[this.displayField] = display[i];
24843             this.addItem(add);
24844         }
24845       
24846         
24847     },
24848     
24849     
24850     validateValue : function(value){
24851         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24852         
24853     }
24854     
24855 });
24856
24857
24858
24859 /**
24860  * @class Roo.form.ComboBoxArray.Item
24861  * @extends Roo.BoxComponent
24862  * A selected item in the list
24863  *  Fred [x]  Brian [x]  [Pick another |v]
24864  * 
24865  * @constructor
24866  * Create a new item.
24867  * @param {Object} config Configuration options
24868  */
24869  
24870 Roo.form.ComboBoxArray.Item = function(config) {
24871     config.id = Roo.id();
24872     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24873 }
24874
24875 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24876     data : {},
24877     cb: false,
24878     displayField : false,
24879     tipField : false,
24880     
24881     
24882     defaultAutoCreate : {
24883         tag: 'div',
24884         cls: 'x-cbarray-item',
24885         cn : [ 
24886             { tag: 'div' },
24887             {
24888                 tag: 'img',
24889                 width:16,
24890                 height : 16,
24891                 src : Roo.BLANK_IMAGE_URL ,
24892                 align: 'center'
24893             }
24894         ]
24895         
24896     },
24897     
24898  
24899     onRender : function(ct, position)
24900     {
24901         Roo.form.Field.superclass.onRender.call(this, ct, position);
24902         
24903         if(!this.el){
24904             var cfg = this.getAutoCreate();
24905             this.el = ct.createChild(cfg, position);
24906         }
24907         
24908         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24909         
24910         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24911             this.cb.renderer(this.data) :
24912             String.format('{0}',this.data[this.displayField]);
24913         
24914             
24915         this.el.child('div').dom.setAttribute('qtip',
24916                         String.format('{0}',this.data[this.tipField])
24917         );
24918         
24919         this.el.child('img').on('click', this.remove, this);
24920         
24921     },
24922    
24923     remove : function()
24924     {
24925         
24926         this.cb.items.remove(this);
24927         this.el.child('img').un('click', this.remove, this);
24928         this.el.remove();
24929         this.cb.updateHiddenEl();
24930     }
24931     
24932     
24933 });/*
24934  * Based on:
24935  * Ext JS Library 1.1.1
24936  * Copyright(c) 2006-2007, Ext JS, LLC.
24937  *
24938  * Originally Released Under LGPL - original licence link has changed is not relivant.
24939  *
24940  * Fork - LGPL
24941  * <script type="text/javascript">
24942  */
24943 /**
24944  * @class Roo.form.Checkbox
24945  * @extends Roo.form.Field
24946  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24947  * @constructor
24948  * Creates a new Checkbox
24949  * @param {Object} config Configuration options
24950  */
24951 Roo.form.Checkbox = function(config){
24952     Roo.form.Checkbox.superclass.constructor.call(this, config);
24953     this.addEvents({
24954         /**
24955          * @event check
24956          * Fires when the checkbox is checked or unchecked.
24957              * @param {Roo.form.Checkbox} this This checkbox
24958              * @param {Boolean} checked The new checked value
24959              */
24960         check : true
24961     });
24962 };
24963
24964 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24965     /**
24966      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24967      */
24968     focusClass : undefined,
24969     /**
24970      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24971      */
24972     fieldClass: "x-form-field",
24973     /**
24974      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24975      */
24976     checked: false,
24977     /**
24978      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24979      * {tag: "input", type: "checkbox", autocomplete: "off"})
24980      */
24981     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24982     /**
24983      * @cfg {String} boxLabel The text that appears beside the checkbox
24984      */
24985     boxLabel : "",
24986     /**
24987      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24988      */  
24989     inputValue : '1',
24990     /**
24991      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24992      */
24993      valueOff: '0', // value when not checked..
24994
24995     actionMode : 'viewEl', 
24996     //
24997     // private
24998     itemCls : 'x-menu-check-item x-form-item',
24999     groupClass : 'x-menu-group-item',
25000     inputType : 'hidden',
25001     
25002     
25003     inSetChecked: false, // check that we are not calling self...
25004     
25005     inputElement: false, // real input element?
25006     basedOn: false, // ????
25007     
25008     isFormField: true, // not sure where this is needed!!!!
25009
25010     onResize : function(){
25011         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
25012         if(!this.boxLabel){
25013             this.el.alignTo(this.wrap, 'c-c');
25014         }
25015     },
25016
25017     initEvents : function(){
25018         Roo.form.Checkbox.superclass.initEvents.call(this);
25019         this.el.on("click", this.onClick,  this);
25020         this.el.on("change", this.onClick,  this);
25021     },
25022
25023
25024     getResizeEl : function(){
25025         return this.wrap;
25026     },
25027
25028     getPositionEl : function(){
25029         return this.wrap;
25030     },
25031
25032     // private
25033     onRender : function(ct, position){
25034         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25035         /*
25036         if(this.inputValue !== undefined){
25037             this.el.dom.value = this.inputValue;
25038         }
25039         */
25040         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25041         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25042         var viewEl = this.wrap.createChild({ 
25043             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25044         this.viewEl = viewEl;   
25045         this.wrap.on('click', this.onClick,  this); 
25046         
25047         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25048         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25049         
25050         
25051         
25052         if(this.boxLabel){
25053             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25054         //    viewEl.on('click', this.onClick,  this); 
25055         }
25056         //if(this.checked){
25057             this.setChecked(this.checked);
25058         //}else{
25059             //this.checked = this.el.dom;
25060         //}
25061
25062     },
25063
25064     // private
25065     initValue : Roo.emptyFn,
25066
25067     /**
25068      * Returns the checked state of the checkbox.
25069      * @return {Boolean} True if checked, else false
25070      */
25071     getValue : function(){
25072         if(this.el){
25073             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25074         }
25075         return this.valueOff;
25076         
25077     },
25078
25079         // private
25080     onClick : function(){ 
25081         this.setChecked(!this.checked);
25082
25083         //if(this.el.dom.checked != this.checked){
25084         //    this.setValue(this.el.dom.checked);
25085        // }
25086     },
25087
25088     /**
25089      * Sets the checked state of the checkbox.
25090      * On is always based on a string comparison between inputValue and the param.
25091      * @param {Boolean/String} value - the value to set 
25092      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25093      */
25094     setValue : function(v,suppressEvent){
25095         
25096         
25097         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25098         //if(this.el && this.el.dom){
25099         //    this.el.dom.checked = this.checked;
25100         //    this.el.dom.defaultChecked = this.checked;
25101         //}
25102         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25103         //this.fireEvent("check", this, this.checked);
25104     },
25105     // private..
25106     setChecked : function(state,suppressEvent)
25107     {
25108         if (this.inSetChecked) {
25109             this.checked = state;
25110             return;
25111         }
25112         
25113     
25114         if(this.wrap){
25115             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25116         }
25117         this.checked = state;
25118         if(suppressEvent !== true){
25119             this.fireEvent('check', this, state);
25120         }
25121         this.inSetChecked = true;
25122         this.el.dom.value = state ? this.inputValue : this.valueOff;
25123         this.inSetChecked = false;
25124         
25125     },
25126     // handle setting of hidden value by some other method!!?!?
25127     setFromHidden: function()
25128     {
25129         if(!this.el){
25130             return;
25131         }
25132         //console.log("SET FROM HIDDEN");
25133         //alert('setFrom hidden');
25134         this.setValue(this.el.dom.value);
25135     },
25136     
25137     onDestroy : function()
25138     {
25139         if(this.viewEl){
25140             Roo.get(this.viewEl).remove();
25141         }
25142          
25143         Roo.form.Checkbox.superclass.onDestroy.call(this);
25144     }
25145
25146 });/*
25147  * Based on:
25148  * Ext JS Library 1.1.1
25149  * Copyright(c) 2006-2007, Ext JS, LLC.
25150  *
25151  * Originally Released Under LGPL - original licence link has changed is not relivant.
25152  *
25153  * Fork - LGPL
25154  * <script type="text/javascript">
25155  */
25156  
25157 /**
25158  * @class Roo.form.Radio
25159  * @extends Roo.form.Checkbox
25160  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25161  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25162  * @constructor
25163  * Creates a new Radio
25164  * @param {Object} config Configuration options
25165  */
25166 Roo.form.Radio = function(){
25167     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25168 };
25169 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25170     inputType: 'radio',
25171
25172     /**
25173      * If this radio is part of a group, it will return the selected value
25174      * @return {String}
25175      */
25176     getGroupValue : function(){
25177         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25178     },
25179     
25180     
25181     onRender : function(ct, position){
25182         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25183         
25184         if(this.inputValue !== undefined){
25185             this.el.dom.value = this.inputValue;
25186         }
25187          
25188         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25189         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25190         //var viewEl = this.wrap.createChild({ 
25191         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25192         //this.viewEl = viewEl;   
25193         //this.wrap.on('click', this.onClick,  this); 
25194         
25195         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25196         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
25197         
25198         
25199         
25200         if(this.boxLabel){
25201             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25202         //    viewEl.on('click', this.onClick,  this); 
25203         }
25204          if(this.checked){
25205             this.el.dom.checked =   'checked' ;
25206         }
25207          
25208     } 
25209     
25210     
25211 });//<script type="text/javascript">
25212
25213 /*
25214  * Ext JS Library 1.1.1
25215  * Copyright(c) 2006-2007, Ext JS, LLC.
25216  * licensing@extjs.com
25217  * 
25218  * http://www.extjs.com/license
25219  */
25220  
25221  /*
25222   * 
25223   * Known bugs:
25224   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25225   * - IE ? - no idea how much works there.
25226   * 
25227   * 
25228   * 
25229   */
25230  
25231
25232 /**
25233  * @class Ext.form.HtmlEditor
25234  * @extends Ext.form.Field
25235  * Provides a lightweight HTML Editor component.
25236  *
25237  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25238  * 
25239  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25240  * supported by this editor.</b><br/><br/>
25241  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25242  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25243  */
25244 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25245       /**
25246      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25247      */
25248     toolbars : false,
25249     /**
25250      * @cfg {String} createLinkText The default text for the create link prompt
25251      */
25252     createLinkText : 'Please enter the URL for the link:',
25253     /**
25254      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25255      */
25256     defaultLinkValue : 'http:/'+'/',
25257    
25258      /**
25259      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25260      *                        Roo.resizable.
25261      */
25262     resizable : false,
25263      /**
25264      * @cfg {Number} height (in pixels)
25265      */   
25266     height: 300,
25267    /**
25268      * @cfg {Number} width (in pixels)
25269      */   
25270     width: 500,
25271     
25272     /**
25273      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25274      * 
25275      */
25276     stylesheets: false,
25277     
25278     // id of frame..
25279     frameId: false,
25280     
25281     // private properties
25282     validationEvent : false,
25283     deferHeight: true,
25284     initialized : false,
25285     activated : false,
25286     sourceEditMode : false,
25287     onFocus : Roo.emptyFn,
25288     iframePad:3,
25289     hideMode:'offsets',
25290     
25291     defaultAutoCreate : { // modified by initCompnoent..
25292         tag: "textarea",
25293         style:"width:500px;height:300px;",
25294         autocomplete: "off"
25295     },
25296
25297     // private
25298     initComponent : function(){
25299         this.addEvents({
25300             /**
25301              * @event initialize
25302              * Fires when the editor is fully initialized (including the iframe)
25303              * @param {HtmlEditor} this
25304              */
25305             initialize: true,
25306             /**
25307              * @event activate
25308              * Fires when the editor is first receives the focus. Any insertion must wait
25309              * until after this event.
25310              * @param {HtmlEditor} this
25311              */
25312             activate: true,
25313              /**
25314              * @event beforesync
25315              * Fires before the textarea is updated with content from the editor iframe. Return false
25316              * to cancel the sync.
25317              * @param {HtmlEditor} this
25318              * @param {String} html
25319              */
25320             beforesync: true,
25321              /**
25322              * @event beforepush
25323              * Fires before the iframe editor is updated with content from the textarea. Return false
25324              * to cancel the push.
25325              * @param {HtmlEditor} this
25326              * @param {String} html
25327              */
25328             beforepush: true,
25329              /**
25330              * @event sync
25331              * Fires when the textarea is updated with content from the editor iframe.
25332              * @param {HtmlEditor} this
25333              * @param {String} html
25334              */
25335             sync: true,
25336              /**
25337              * @event push
25338              * Fires when the iframe editor is updated with content from the textarea.
25339              * @param {HtmlEditor} this
25340              * @param {String} html
25341              */
25342             push: true,
25343              /**
25344              * @event editmodechange
25345              * Fires when the editor switches edit modes
25346              * @param {HtmlEditor} this
25347              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25348              */
25349             editmodechange: true,
25350             /**
25351              * @event editorevent
25352              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25353              * @param {HtmlEditor} this
25354              */
25355             editorevent: true
25356         });
25357         this.defaultAutoCreate =  {
25358             tag: "textarea",
25359             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25360             autocomplete: "off"
25361         };
25362     },
25363
25364     /**
25365      * Protected method that will not generally be called directly. It
25366      * is called when the editor creates its toolbar. Override this method if you need to
25367      * add custom toolbar buttons.
25368      * @param {HtmlEditor} editor
25369      */
25370     createToolbar : function(editor){
25371         if (!editor.toolbars || !editor.toolbars.length) {
25372             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25373         }
25374         
25375         for (var i =0 ; i < editor.toolbars.length;i++) {
25376             editor.toolbars[i] = Roo.factory(
25377                     typeof(editor.toolbars[i]) == 'string' ?
25378                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25379                 Roo.form.HtmlEditor);
25380             editor.toolbars[i].init(editor);
25381         }
25382          
25383         
25384     },
25385
25386     /**
25387      * Protected method that will not generally be called directly. It
25388      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25389      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25390      */
25391     getDocMarkup : function(){
25392         // body styles..
25393         var st = '';
25394         if (this.stylesheets === false) {
25395             
25396             Roo.get(document.head).select('style').each(function(node) {
25397                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25398             });
25399             
25400             Roo.get(document.head).select('link').each(function(node) { 
25401                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25402             });
25403             
25404         } else if (!this.stylesheets.length) {
25405                 // simple..
25406                 st = '<style type="text/css">' +
25407                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25408                    '</style>';
25409         } else {
25410             Roo.each(this.stylesheets, function(s) {
25411                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25412             });
25413             
25414         }
25415         
25416         st +=  '<style type="text/css">' +
25417             'IMG { cursor: pointer } ' +
25418         '</style>';
25419
25420         
25421         return '<html><head>' + st  +
25422             //<style type="text/css">' +
25423             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25424             //'</style>' +
25425             ' </head><body class="roo-htmleditor-body"></body></html>';
25426     },
25427
25428     // private
25429     onRender : function(ct, position)
25430     {
25431         var _t = this;
25432         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25433         this.el.dom.style.border = '0 none';
25434         this.el.dom.setAttribute('tabIndex', -1);
25435         this.el.addClass('x-hidden');
25436         if(Roo.isIE){ // fix IE 1px bogus margin
25437             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25438         }
25439         this.wrap = this.el.wrap({
25440             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25441         });
25442         
25443         if (this.resizable) {
25444             this.resizeEl = new Roo.Resizable(this.wrap, {
25445                 pinned : true,
25446                 wrap: true,
25447                 dynamic : true,
25448                 minHeight : this.height,
25449                 height: this.height,
25450                 handles : this.resizable,
25451                 width: this.width,
25452                 listeners : {
25453                     resize : function(r, w, h) {
25454                         _t.onResize(w,h); // -something
25455                     }
25456                 }
25457             });
25458             
25459         }
25460
25461         this.frameId = Roo.id();
25462         
25463         this.createToolbar(this);
25464         
25465       
25466         
25467         var iframe = this.wrap.createChild({
25468             tag: 'iframe',
25469             id: this.frameId,
25470             name: this.frameId,
25471             frameBorder : 'no',
25472             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25473         }, this.el
25474         );
25475         
25476        // console.log(iframe);
25477         //this.wrap.dom.appendChild(iframe);
25478
25479         this.iframe = iframe.dom;
25480
25481          this.assignDocWin();
25482         
25483         this.doc.designMode = 'on';
25484        
25485         this.doc.open();
25486         this.doc.write(this.getDocMarkup());
25487         this.doc.close();
25488
25489         
25490         var task = { // must defer to wait for browser to be ready
25491             run : function(){
25492                 //console.log("run task?" + this.doc.readyState);
25493                 this.assignDocWin();
25494                 if(this.doc.body || this.doc.readyState == 'complete'){
25495                     try {
25496                         this.doc.designMode="on";
25497                     } catch (e) {
25498                         return;
25499                     }
25500                     Roo.TaskMgr.stop(task);
25501                     this.initEditor.defer(10, this);
25502                 }
25503             },
25504             interval : 10,
25505             duration:10000,
25506             scope: this
25507         };
25508         Roo.TaskMgr.start(task);
25509
25510         if(!this.width){
25511             this.setSize(this.wrap.getSize());
25512         }
25513         if (this.resizeEl) {
25514             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25515             // should trigger onReize..
25516         }
25517     },
25518
25519     // private
25520     onResize : function(w, h)
25521     {
25522         //Roo.log('resize: ' +w + ',' + h );
25523         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25524         if(this.el && this.iframe){
25525             if(typeof w == 'number'){
25526                 var aw = w - this.wrap.getFrameWidth('lr');
25527                 this.el.setWidth(this.adjustWidth('textarea', aw));
25528                 this.iframe.style.width = aw + 'px';
25529             }
25530             if(typeof h == 'number'){
25531                 var tbh = 0;
25532                 for (var i =0; i < this.toolbars.length;i++) {
25533                     // fixme - ask toolbars for heights?
25534                     tbh += this.toolbars[i].tb.el.getHeight();
25535                     if (this.toolbars[i].footer) {
25536                         tbh += this.toolbars[i].footer.el.getHeight();
25537                     }
25538                 }
25539                 
25540                 
25541                 
25542                 
25543                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25544                 ah -= 5; // knock a few pixes off for look..
25545                 this.el.setHeight(this.adjustWidth('textarea', ah));
25546                 this.iframe.style.height = ah + 'px';
25547                 if(this.doc){
25548                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25549                 }
25550             }
25551         }
25552     },
25553
25554     /**
25555      * Toggles the editor between standard and source edit mode.
25556      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25557      */
25558     toggleSourceEdit : function(sourceEditMode){
25559         
25560         this.sourceEditMode = sourceEditMode === true;
25561         
25562         if(this.sourceEditMode){
25563 //            Roo.log('in');
25564 //            Roo.log(this.syncValue());
25565             this.syncValue();
25566             this.iframe.className = 'x-hidden';
25567             this.el.removeClass('x-hidden');
25568             this.el.dom.removeAttribute('tabIndex');
25569             this.el.focus();
25570         }else{
25571 //            Roo.log('out')
25572 //            Roo.log(this.pushValue()); 
25573             this.pushValue();
25574             this.iframe.className = '';
25575             this.el.addClass('x-hidden');
25576             this.el.dom.setAttribute('tabIndex', -1);
25577             this.deferFocus();
25578         }
25579         this.setSize(this.wrap.getSize());
25580         this.fireEvent('editmodechange', this, this.sourceEditMode);
25581     },
25582
25583     // private used internally
25584     createLink : function(){
25585         var url = prompt(this.createLinkText, this.defaultLinkValue);
25586         if(url && url != 'http:/'+'/'){
25587             this.relayCmd('createlink', url);
25588         }
25589     },
25590
25591     // private (for BoxComponent)
25592     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25593
25594     // private (for BoxComponent)
25595     getResizeEl : function(){
25596         return this.wrap;
25597     },
25598
25599     // private (for BoxComponent)
25600     getPositionEl : function(){
25601         return this.wrap;
25602     },
25603
25604     // private
25605     initEvents : function(){
25606         this.originalValue = this.getValue();
25607     },
25608
25609     /**
25610      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25611      * @method
25612      */
25613     markInvalid : Roo.emptyFn,
25614     /**
25615      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25616      * @method
25617      */
25618     clearInvalid : Roo.emptyFn,
25619
25620     setValue : function(v){
25621         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25622         this.pushValue();
25623     },
25624
25625     /**
25626      * Protected method that will not generally be called directly. If you need/want
25627      * custom HTML cleanup, this is the method you should override.
25628      * @param {String} html The HTML to be cleaned
25629      * return {String} The cleaned HTML
25630      */
25631     cleanHtml : function(html){
25632         html = String(html);
25633         if(html.length > 5){
25634             if(Roo.isSafari){ // strip safari nonsense
25635                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25636             }
25637         }
25638         if(html == '&nbsp;'){
25639             html = '';
25640         }
25641         return html;
25642     },
25643
25644     /**
25645      * Protected method that will not generally be called directly. Syncs the contents
25646      * of the editor iframe with the textarea.
25647      */
25648     syncValue : function(){
25649         if(this.initialized){
25650             var bd = (this.doc.body || this.doc.documentElement);
25651             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25652             var html = bd.innerHTML;
25653             if(Roo.isSafari){
25654                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25655                 var m = bs.match(/text-align:(.*?);/i);
25656                 if(m && m[1]){
25657                     html = '<div style="'+m[0]+'">' + html + '</div>';
25658                 }
25659             }
25660             html = this.cleanHtml(html);
25661             // fix up the special chars.. normaly like back quotes in word...
25662             // however we do not want to do this with chinese..
25663             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25664                 var cc = b.charCodeAt();
25665                 if (
25666                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25667                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25668                     (cc >= 0xf900 && cc < 0xfb00 )
25669                 ) {
25670                         return b;
25671                 }
25672                 return "&#"+cc+";" 
25673             });
25674             if(this.fireEvent('beforesync', this, html) !== false){
25675                 this.el.dom.value = html;
25676                 this.fireEvent('sync', this, html);
25677             }
25678         }
25679     },
25680
25681     /**
25682      * Protected method that will not generally be called directly. Pushes the value of the textarea
25683      * into the iframe editor.
25684      */
25685     pushValue : function(){
25686         if(this.initialized){
25687             var v = this.el.dom.value;
25688             
25689             if(v.length < 1){
25690                 v = '&#160;';
25691             }
25692             
25693             if(this.fireEvent('beforepush', this, v) !== false){
25694                 var d = (this.doc.body || this.doc.documentElement);
25695                 d.innerHTML = v;
25696                 this.cleanUpPaste();
25697                 this.el.dom.value = d.innerHTML;
25698                 this.fireEvent('push', this, v);
25699             }
25700         }
25701     },
25702
25703     // private
25704     deferFocus : function(){
25705         this.focus.defer(10, this);
25706     },
25707
25708     // doc'ed in Field
25709     focus : function(){
25710         if(this.win && !this.sourceEditMode){
25711             this.win.focus();
25712         }else{
25713             this.el.focus();
25714         }
25715     },
25716     
25717     assignDocWin: function()
25718     {
25719         var iframe = this.iframe;
25720         
25721          if(Roo.isIE){
25722             this.doc = iframe.contentWindow.document;
25723             this.win = iframe.contentWindow;
25724         } else {
25725             if (!Roo.get(this.frameId)) {
25726                 return;
25727             }
25728             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25729             this.win = Roo.get(this.frameId).dom.contentWindow;
25730         }
25731     },
25732     
25733     // private
25734     initEditor : function(){
25735         //console.log("INIT EDITOR");
25736         this.assignDocWin();
25737         
25738         
25739         
25740         this.doc.designMode="on";
25741         this.doc.open();
25742         this.doc.write(this.getDocMarkup());
25743         this.doc.close();
25744         
25745         var dbody = (this.doc.body || this.doc.documentElement);
25746         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25747         // this copies styles from the containing element into thsi one..
25748         // not sure why we need all of this..
25749         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25750         ss['background-attachment'] = 'fixed'; // w3c
25751         dbody.bgProperties = 'fixed'; // ie
25752         Roo.DomHelper.applyStyles(dbody, ss);
25753         Roo.EventManager.on(this.doc, {
25754             //'mousedown': this.onEditorEvent,
25755             'mouseup': this.onEditorEvent,
25756             'dblclick': this.onEditorEvent,
25757             'click': this.onEditorEvent,
25758             'keyup': this.onEditorEvent,
25759             buffer:100,
25760             scope: this
25761         });
25762         if(Roo.isGecko){
25763             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25764         }
25765         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25766             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25767         }
25768         this.initialized = true;
25769
25770         this.fireEvent('initialize', this);
25771         this.pushValue();
25772     },
25773
25774     // private
25775     onDestroy : function(){
25776         
25777         
25778         
25779         if(this.rendered){
25780             
25781             for (var i =0; i < this.toolbars.length;i++) {
25782                 // fixme - ask toolbars for heights?
25783                 this.toolbars[i].onDestroy();
25784             }
25785             
25786             this.wrap.dom.innerHTML = '';
25787             this.wrap.remove();
25788         }
25789     },
25790
25791     // private
25792     onFirstFocus : function(){
25793         
25794         this.assignDocWin();
25795         
25796         
25797         this.activated = true;
25798         for (var i =0; i < this.toolbars.length;i++) {
25799             this.toolbars[i].onFirstFocus();
25800         }
25801        
25802         if(Roo.isGecko){ // prevent silly gecko errors
25803             this.win.focus();
25804             var s = this.win.getSelection();
25805             if(!s.focusNode || s.focusNode.nodeType != 3){
25806                 var r = s.getRangeAt(0);
25807                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25808                 r.collapse(true);
25809                 this.deferFocus();
25810             }
25811             try{
25812                 this.execCmd('useCSS', true);
25813                 this.execCmd('styleWithCSS', false);
25814             }catch(e){}
25815         }
25816         this.fireEvent('activate', this);
25817     },
25818
25819     // private
25820     adjustFont: function(btn){
25821         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25822         //if(Roo.isSafari){ // safari
25823         //    adjust *= 2;
25824        // }
25825         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25826         if(Roo.isSafari){ // safari
25827             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25828             v =  (v < 10) ? 10 : v;
25829             v =  (v > 48) ? 48 : v;
25830             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25831             
25832         }
25833         
25834         
25835         v = Math.max(1, v+adjust);
25836         
25837         this.execCmd('FontSize', v  );
25838     },
25839
25840     onEditorEvent : function(e){
25841         this.fireEvent('editorevent', this, e);
25842       //  this.updateToolbar();
25843         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25844     },
25845
25846     insertTag : function(tg)
25847     {
25848         // could be a bit smarter... -> wrap the current selected tRoo..
25849         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25850             
25851             range = this.createRange(this.getSelection());
25852             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25853             wrappingNode.appendChild(range.extractContents());
25854             range.insertNode(wrappingNode);
25855
25856             return;
25857             
25858             
25859             
25860         }
25861         this.execCmd("formatblock",   tg);
25862         
25863     },
25864     
25865     insertText : function(txt)
25866     {
25867         
25868         
25869         var range = this.createRange();
25870         range.deleteContents();
25871                //alert(Sender.getAttribute('label'));
25872                
25873         range.insertNode(this.doc.createTextNode(txt));
25874     } ,
25875     
25876     // private
25877     relayBtnCmd : function(btn){
25878         this.relayCmd(btn.cmd);
25879     },
25880
25881     /**
25882      * Executes a Midas editor command on the editor document and performs necessary focus and
25883      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25884      * @param {String} cmd The Midas command
25885      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25886      */
25887     relayCmd : function(cmd, value){
25888         this.win.focus();
25889         this.execCmd(cmd, value);
25890         this.fireEvent('editorevent', this);
25891         //this.updateToolbar();
25892         this.deferFocus();
25893     },
25894
25895     /**
25896      * Executes a Midas editor command directly on the editor document.
25897      * For visual commands, you should use {@link #relayCmd} instead.
25898      * <b>This should only be called after the editor is initialized.</b>
25899      * @param {String} cmd The Midas command
25900      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25901      */
25902     execCmd : function(cmd, value){
25903         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25904         this.syncValue();
25905     },
25906  
25907  
25908    
25909     /**
25910      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25911      * to insert tRoo.
25912      * @param {String} text | dom node.. 
25913      */
25914     insertAtCursor : function(text)
25915     {
25916         
25917         
25918         
25919         if(!this.activated){
25920             return;
25921         }
25922         /*
25923         if(Roo.isIE){
25924             this.win.focus();
25925             var r = this.doc.selection.createRange();
25926             if(r){
25927                 r.collapse(true);
25928                 r.pasteHTML(text);
25929                 this.syncValue();
25930                 this.deferFocus();
25931             
25932             }
25933             return;
25934         }
25935         */
25936         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25937             this.win.focus();
25938             
25939             
25940             // from jquery ui (MIT licenced)
25941             var range, node;
25942             var win = this.win;
25943             
25944             if (win.getSelection && win.getSelection().getRangeAt) {
25945                 range = win.getSelection().getRangeAt(0);
25946                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25947                 range.insertNode(node);
25948             } else if (win.document.selection && win.document.selection.createRange) {
25949                 // no firefox support
25950                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25951                 win.document.selection.createRange().pasteHTML(txt);
25952             } else {
25953                 // no firefox support
25954                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25955                 this.execCmd('InsertHTML', txt);
25956             } 
25957             
25958             this.syncValue();
25959             
25960             this.deferFocus();
25961         }
25962     },
25963  // private
25964     mozKeyPress : function(e){
25965         if(e.ctrlKey){
25966             var c = e.getCharCode(), cmd;
25967           
25968             if(c > 0){
25969                 c = String.fromCharCode(c).toLowerCase();
25970                 switch(c){
25971                     case 'b':
25972                         cmd = 'bold';
25973                         break;
25974                     case 'i':
25975                         cmd = 'italic';
25976                         break;
25977                     
25978                     case 'u':
25979                         cmd = 'underline';
25980                         break;
25981                     
25982                     case 'v':
25983                         this.cleanUpPaste.defer(100, this);
25984                         return;
25985                         
25986                 }
25987                 if(cmd){
25988                     this.win.focus();
25989                     this.execCmd(cmd);
25990                     this.deferFocus();
25991                     e.preventDefault();
25992                 }
25993                 
25994             }
25995         }
25996     },
25997
25998     // private
25999     fixKeys : function(){ // load time branching for fastest keydown performance
26000         if(Roo.isIE){
26001             return function(e){
26002                 var k = e.getKey(), r;
26003                 if(k == e.TAB){
26004                     e.stopEvent();
26005                     r = this.doc.selection.createRange();
26006                     if(r){
26007                         r.collapse(true);
26008                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26009                         this.deferFocus();
26010                     }
26011                     return;
26012                 }
26013                 
26014                 if(k == e.ENTER){
26015                     r = this.doc.selection.createRange();
26016                     if(r){
26017                         var target = r.parentElement();
26018                         if(!target || target.tagName.toLowerCase() != 'li'){
26019                             e.stopEvent();
26020                             r.pasteHTML('<br />');
26021                             r.collapse(false);
26022                             r.select();
26023                         }
26024                     }
26025                 }
26026                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26027                     this.cleanUpPaste.defer(100, this);
26028                     return;
26029                 }
26030                 
26031                 
26032             };
26033         }else if(Roo.isOpera){
26034             return function(e){
26035                 var k = e.getKey();
26036                 if(k == e.TAB){
26037                     e.stopEvent();
26038                     this.win.focus();
26039                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26040                     this.deferFocus();
26041                 }
26042                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26043                     this.cleanUpPaste.defer(100, this);
26044                     return;
26045                 }
26046                 
26047             };
26048         }else if(Roo.isSafari){
26049             return function(e){
26050                 var k = e.getKey();
26051                 
26052                 if(k == e.TAB){
26053                     e.stopEvent();
26054                     this.execCmd('InsertText','\t');
26055                     this.deferFocus();
26056                     return;
26057                 }
26058                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26059                     this.cleanUpPaste.defer(100, this);
26060                     return;
26061                 }
26062                 
26063              };
26064         }
26065     }(),
26066     
26067     getAllAncestors: function()
26068     {
26069         var p = this.getSelectedNode();
26070         var a = [];
26071         if (!p) {
26072             a.push(p); // push blank onto stack..
26073             p = this.getParentElement();
26074         }
26075         
26076         
26077         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26078             a.push(p);
26079             p = p.parentNode;
26080         }
26081         a.push(this.doc.body);
26082         return a;
26083     },
26084     lastSel : false,
26085     lastSelNode : false,
26086     
26087     
26088     getSelection : function() 
26089     {
26090         this.assignDocWin();
26091         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26092     },
26093     
26094     getSelectedNode: function() 
26095     {
26096         // this may only work on Gecko!!!
26097         
26098         // should we cache this!!!!
26099         
26100         
26101         
26102          
26103         var range = this.createRange(this.getSelection()).cloneRange();
26104         
26105         if (Roo.isIE) {
26106             var parent = range.parentElement();
26107             while (true) {
26108                 var testRange = range.duplicate();
26109                 testRange.moveToElementText(parent);
26110                 if (testRange.inRange(range)) {
26111                     break;
26112                 }
26113                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26114                     break;
26115                 }
26116                 parent = parent.parentElement;
26117             }
26118             return parent;
26119         }
26120         
26121         // is ancestor a text element.
26122         var ac =  range.commonAncestorContainer;
26123         if (ac.nodeType == 3) {
26124             ac = ac.parentNode;
26125         }
26126         
26127         var ar = ac.childNodes;
26128          
26129         var nodes = [];
26130         var other_nodes = [];
26131         var has_other_nodes = false;
26132         for (var i=0;i<ar.length;i++) {
26133             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26134                 continue;
26135             }
26136             // fullly contained node.
26137             
26138             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26139                 nodes.push(ar[i]);
26140                 continue;
26141             }
26142             
26143             // probably selected..
26144             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26145                 other_nodes.push(ar[i]);
26146                 continue;
26147             }
26148             // outer..
26149             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26150                 continue;
26151             }
26152             
26153             
26154             has_other_nodes = true;
26155         }
26156         if (!nodes.length && other_nodes.length) {
26157             nodes= other_nodes;
26158         }
26159         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26160             return false;
26161         }
26162         
26163         return nodes[0];
26164     },
26165     createRange: function(sel)
26166     {
26167         // this has strange effects when using with 
26168         // top toolbar - not sure if it's a great idea.
26169         //this.editor.contentWindow.focus();
26170         if (typeof sel != "undefined") {
26171             try {
26172                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26173             } catch(e) {
26174                 return this.doc.createRange();
26175             }
26176         } else {
26177             return this.doc.createRange();
26178         }
26179     },
26180     getParentElement: function()
26181     {
26182         
26183         this.assignDocWin();
26184         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26185         
26186         var range = this.createRange(sel);
26187          
26188         try {
26189             var p = range.commonAncestorContainer;
26190             while (p.nodeType == 3) { // text node
26191                 p = p.parentNode;
26192             }
26193             return p;
26194         } catch (e) {
26195             return null;
26196         }
26197     
26198     },
26199     /***
26200      *
26201      * Range intersection.. the hard stuff...
26202      *  '-1' = before
26203      *  '0' = hits..
26204      *  '1' = after.
26205      *         [ -- selected range --- ]
26206      *   [fail]                        [fail]
26207      *
26208      *    basically..
26209      *      if end is before start or  hits it. fail.
26210      *      if start is after end or hits it fail.
26211      *
26212      *   if either hits (but other is outside. - then it's not 
26213      *   
26214      *    
26215      **/
26216     
26217     
26218     // @see http://www.thismuchiknow.co.uk/?p=64.
26219     rangeIntersectsNode : function(range, node)
26220     {
26221         var nodeRange = node.ownerDocument.createRange();
26222         try {
26223             nodeRange.selectNode(node);
26224         } catch (e) {
26225             nodeRange.selectNodeContents(node);
26226         }
26227     
26228         var rangeStartRange = range.cloneRange();
26229         rangeStartRange.collapse(true);
26230     
26231         var rangeEndRange = range.cloneRange();
26232         rangeEndRange.collapse(false);
26233     
26234         var nodeStartRange = nodeRange.cloneRange();
26235         nodeStartRange.collapse(true);
26236     
26237         var nodeEndRange = nodeRange.cloneRange();
26238         nodeEndRange.collapse(false);
26239     
26240         return rangeStartRange.compareBoundaryPoints(
26241                  Range.START_TO_START, nodeEndRange) == -1 &&
26242                rangeEndRange.compareBoundaryPoints(
26243                  Range.START_TO_START, nodeStartRange) == 1;
26244         
26245          
26246     },
26247     rangeCompareNode : function(range, node)
26248     {
26249         var nodeRange = node.ownerDocument.createRange();
26250         try {
26251             nodeRange.selectNode(node);
26252         } catch (e) {
26253             nodeRange.selectNodeContents(node);
26254         }
26255         
26256         
26257         range.collapse(true);
26258     
26259         nodeRange.collapse(true);
26260      
26261         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26262         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26263          
26264         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26265         
26266         var nodeIsBefore   =  ss == 1;
26267         var nodeIsAfter    = ee == -1;
26268         
26269         if (nodeIsBefore && nodeIsAfter)
26270             return 0; // outer
26271         if (!nodeIsBefore && nodeIsAfter)
26272             return 1; //right trailed.
26273         
26274         if (nodeIsBefore && !nodeIsAfter)
26275             return 2;  // left trailed.
26276         // fully contined.
26277         return 3;
26278     },
26279
26280     // private? - in a new class?
26281     cleanUpPaste :  function()
26282     {
26283         // cleans up the whole document..
26284          Roo.log('cleanuppaste');
26285         this.cleanUpChildren(this.doc.body);
26286         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26287         if (clean != this.doc.body.innerHTML) {
26288             this.doc.body.innerHTML = clean;
26289         }
26290         
26291     },
26292     
26293     cleanWordChars : function(input) {// change the chars to hex code
26294         var he = Roo.form.HtmlEditor;
26295         
26296         var output = input;
26297         Roo.each(he.swapCodes, function(sw) { 
26298             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26299             
26300             output = output.replace(swapper, sw[1]);
26301         });
26302         
26303         return output;
26304     },
26305     
26306     
26307     cleanUpChildren : function (n)
26308     {
26309         if (!n.childNodes.length) {
26310             return;
26311         }
26312         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26313            this.cleanUpChild(n.childNodes[i]);
26314         }
26315     },
26316     
26317     
26318         
26319     
26320     cleanUpChild : function (node)
26321     {
26322         var ed = this;
26323         //console.log(node);
26324         if (node.nodeName == "#text") {
26325             // clean up silly Windows -- stuff?
26326             return; 
26327         }
26328         if (node.nodeName == "#comment") {
26329             node.parentNode.removeChild(node);
26330             // clean up silly Windows -- stuff?
26331             return; 
26332         }
26333         
26334         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26335             // remove node.
26336             node.parentNode.removeChild(node);
26337             return;
26338             
26339         }
26340         
26341         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26342         
26343         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26344         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26345         
26346         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26347         //    remove_keep_children = true;
26348         //}
26349         
26350         if (remove_keep_children) {
26351             this.cleanUpChildren(node);
26352             // inserts everything just before this node...
26353             while (node.childNodes.length) {
26354                 var cn = node.childNodes[0];
26355                 node.removeChild(cn);
26356                 node.parentNode.insertBefore(cn, node);
26357             }
26358             node.parentNode.removeChild(node);
26359             return;
26360         }
26361         
26362         if (!node.attributes || !node.attributes.length) {
26363             this.cleanUpChildren(node);
26364             return;
26365         }
26366         
26367         function cleanAttr(n,v)
26368         {
26369             
26370             if (v.match(/^\./) || v.match(/^\//)) {
26371                 return;
26372             }
26373             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26374                 return;
26375             }
26376             if (v.match(/^#/)) {
26377                 return;
26378             }
26379 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26380             node.removeAttribute(n);
26381             
26382         }
26383         
26384         function cleanStyle(n,v)
26385         {
26386             if (v.match(/expression/)) { //XSS?? should we even bother..
26387                 node.removeAttribute(n);
26388                 return;
26389             }
26390             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26391             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26392             
26393             
26394             var parts = v.split(/;/);
26395             var clean = [];
26396             
26397             Roo.each(parts, function(p) {
26398                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26399                 if (!p.length) {
26400                     return true;
26401                 }
26402                 var l = p.split(':').shift().replace(/\s+/g,'');
26403                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26404                 
26405                 
26406                 if ( cblack.indexOf(l) > -1) {
26407 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26408                     //node.removeAttribute(n);
26409                     return true;
26410                 }
26411                 //Roo.log()
26412                 // only allow 'c whitelisted system attributes'
26413                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26414 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26415                     //node.removeAttribute(n);
26416                     return true;
26417                 }
26418                 
26419                 
26420                  
26421                 
26422                 clean.push(p);
26423                 return true;
26424             });
26425             if (clean.length) { 
26426                 node.setAttribute(n, clean.join(';'));
26427             } else {
26428                 node.removeAttribute(n);
26429             }
26430             
26431         }
26432         
26433         
26434         for (var i = node.attributes.length-1; i > -1 ; i--) {
26435             var a = node.attributes[i];
26436             //console.log(a);
26437             
26438             if (a.name.toLowerCase().substr(0,2)=='on')  {
26439                 node.removeAttribute(a.name);
26440                 continue;
26441             }
26442             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26443                 node.removeAttribute(a.name);
26444                 continue;
26445             }
26446             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26447                 cleanAttr(a.name,a.value); // fixme..
26448                 continue;
26449             }
26450             if (a.name == 'style') {
26451                 cleanStyle(a.name,a.value);
26452                 continue;
26453             }
26454             /// clean up MS crap..
26455             // tecnically this should be a list of valid class'es..
26456             
26457             
26458             if (a.name == 'class') {
26459                 if (a.value.match(/^Mso/)) {
26460                     node.className = '';
26461                 }
26462                 
26463                 if (a.value.match(/body/)) {
26464                     node.className = '';
26465                 }
26466                 continue;
26467             }
26468             
26469             // style cleanup!?
26470             // class cleanup?
26471             
26472         }
26473         
26474         
26475         this.cleanUpChildren(node);
26476         
26477         
26478     }
26479     
26480     
26481     // hide stuff that is not compatible
26482     /**
26483      * @event blur
26484      * @hide
26485      */
26486     /**
26487      * @event change
26488      * @hide
26489      */
26490     /**
26491      * @event focus
26492      * @hide
26493      */
26494     /**
26495      * @event specialkey
26496      * @hide
26497      */
26498     /**
26499      * @cfg {String} fieldClass @hide
26500      */
26501     /**
26502      * @cfg {String} focusClass @hide
26503      */
26504     /**
26505      * @cfg {String} autoCreate @hide
26506      */
26507     /**
26508      * @cfg {String} inputType @hide
26509      */
26510     /**
26511      * @cfg {String} invalidClass @hide
26512      */
26513     /**
26514      * @cfg {String} invalidText @hide
26515      */
26516     /**
26517      * @cfg {String} msgFx @hide
26518      */
26519     /**
26520      * @cfg {String} validateOnBlur @hide
26521      */
26522 });
26523
26524 Roo.form.HtmlEditor.white = [
26525         'area', 'br', 'img', 'input', 'hr', 'wbr',
26526         
26527        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26528        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26529        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26530        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26531        'table',   'ul',         'xmp', 
26532        
26533        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26534       'thead',   'tr', 
26535      
26536       'dir', 'menu', 'ol', 'ul', 'dl',
26537        
26538       'embed',  'object'
26539 ];
26540
26541
26542 Roo.form.HtmlEditor.black = [
26543     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26544         'applet', // 
26545         'base',   'basefont', 'bgsound', 'blink',  'body', 
26546         'frame',  'frameset', 'head',    'html',   'ilayer', 
26547         'iframe', 'layer',  'link',     'meta',    'object',   
26548         'script', 'style' ,'title',  'xml' // clean later..
26549 ];
26550 Roo.form.HtmlEditor.clean = [
26551     'script', 'style', 'title', 'xml'
26552 ];
26553 Roo.form.HtmlEditor.remove = [
26554     'font'
26555 ];
26556 // attributes..
26557
26558 Roo.form.HtmlEditor.ablack = [
26559     'on'
26560 ];
26561     
26562 Roo.form.HtmlEditor.aclean = [ 
26563     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26564 ];
26565
26566 // protocols..
26567 Roo.form.HtmlEditor.pwhite= [
26568         'http',  'https',  'mailto'
26569 ];
26570
26571 // white listed style attributes.
26572 Roo.form.HtmlEditor.cwhite= [
26573       //  'text-align', /// default is to allow most things..
26574       
26575          
26576 //        'font-size'//??
26577 ];
26578
26579 // black listed style attributes.
26580 Roo.form.HtmlEditor.cblack= [
26581       //  'font-size' -- this can be set by the project 
26582 ];
26583
26584
26585 Roo.form.HtmlEditor.swapCodes   =[ 
26586     [    8211, "--" ], 
26587     [    8212, "--" ], 
26588     [    8216,  "'" ],  
26589     [    8217, "'" ],  
26590     [    8220, '"' ],  
26591     [    8221, '"' ],  
26592     [    8226, "*" ],  
26593     [    8230, "..." ]
26594 ]; 
26595
26596     // <script type="text/javascript">
26597 /*
26598  * Based on
26599  * Ext JS Library 1.1.1
26600  * Copyright(c) 2006-2007, Ext JS, LLC.
26601  *  
26602  
26603  */
26604
26605 /**
26606  * @class Roo.form.HtmlEditorToolbar1
26607  * Basic Toolbar
26608  * 
26609  * Usage:
26610  *
26611  new Roo.form.HtmlEditor({
26612     ....
26613     toolbars : [
26614         new Roo.form.HtmlEditorToolbar1({
26615             disable : { fonts: 1 , format: 1, ..., ... , ...],
26616             btns : [ .... ]
26617         })
26618     }
26619      
26620  * 
26621  * @cfg {Object} disable List of elements to disable..
26622  * @cfg {Array} btns List of additional buttons.
26623  * 
26624  * 
26625  * NEEDS Extra CSS? 
26626  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26627  */
26628  
26629 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26630 {
26631     
26632     Roo.apply(this, config);
26633     
26634     // default disabled, based on 'good practice'..
26635     this.disable = this.disable || {};
26636     Roo.applyIf(this.disable, {
26637         fontSize : true,
26638         colors : true,
26639         specialElements : true
26640     });
26641     
26642     
26643     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26644     // dont call parent... till later.
26645 }
26646
26647 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26648     
26649     tb: false,
26650     
26651     rendered: false,
26652     
26653     editor : false,
26654     /**
26655      * @cfg {Object} disable  List of toolbar elements to disable
26656          
26657      */
26658     disable : false,
26659       /**
26660      * @cfg {Array} fontFamilies An array of available font families
26661      */
26662     fontFamilies : [
26663         'Arial',
26664         'Courier New',
26665         'Tahoma',
26666         'Times New Roman',
26667         'Verdana'
26668     ],
26669     
26670     specialChars : [
26671            "&#169;",
26672           "&#174;",     
26673           "&#8482;",    
26674           "&#163;" ,    
26675          // "&#8212;",    
26676           "&#8230;",    
26677           "&#247;" ,    
26678         //  "&#225;" ,     ?? a acute?
26679            "&#8364;"    , //Euro
26680        //   "&#8220;"    ,
26681         //  "&#8221;"    ,
26682         //  "&#8226;"    ,
26683           "&#176;"  //   , // degrees
26684
26685          // "&#233;"     , // e ecute
26686          // "&#250;"     , // u ecute?
26687     ],
26688     
26689     specialElements : [
26690         {
26691             text: "Insert Table",
26692             xtype: 'MenuItem',
26693             xns : Roo.Menu,
26694             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26695                 
26696         },
26697         {    
26698             text: "Insert Image",
26699             xtype: 'MenuItem',
26700             xns : Roo.Menu,
26701             ihtml : '<img src="about:blank"/>'
26702             
26703         }
26704         
26705          
26706     ],
26707     
26708     
26709     inputElements : [ 
26710             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26711             "input:submit", "input:button", "select", "textarea", "label" ],
26712     formats : [
26713         ["p"] ,  
26714         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26715         ["pre"],[ "code"], 
26716         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26717         ['div'],['span']
26718     ],
26719      /**
26720      * @cfg {String} defaultFont default font to use.
26721      */
26722     defaultFont: 'tahoma',
26723    
26724     fontSelect : false,
26725     
26726     
26727     formatCombo : false,
26728     
26729     init : function(editor)
26730     {
26731         this.editor = editor;
26732         
26733         
26734         var fid = editor.frameId;
26735         var etb = this;
26736         function btn(id, toggle, handler){
26737             var xid = fid + '-'+ id ;
26738             return {
26739                 id : xid,
26740                 cmd : id,
26741                 cls : 'x-btn-icon x-edit-'+id,
26742                 enableToggle:toggle !== false,
26743                 scope: editor, // was editor...
26744                 handler:handler||editor.relayBtnCmd,
26745                 clickEvent:'mousedown',
26746                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26747                 tabIndex:-1
26748             };
26749         }
26750         
26751         
26752         
26753         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26754         this.tb = tb;
26755          // stop form submits
26756         tb.el.on('click', function(e){
26757             e.preventDefault(); // what does this do?
26758         });
26759
26760         if(!this.disable.font) { // && !Roo.isSafari){
26761             /* why no safari for fonts 
26762             editor.fontSelect = tb.el.createChild({
26763                 tag:'select',
26764                 tabIndex: -1,
26765                 cls:'x-font-select',
26766                 html: this.createFontOptions()
26767             });
26768             
26769             editor.fontSelect.on('change', function(){
26770                 var font = editor.fontSelect.dom.value;
26771                 editor.relayCmd('fontname', font);
26772                 editor.deferFocus();
26773             }, editor);
26774             
26775             tb.add(
26776                 editor.fontSelect.dom,
26777                 '-'
26778             );
26779             */
26780             
26781         };
26782         if(!this.disable.formats){
26783             this.formatCombo = new Roo.form.ComboBox({
26784                 store: new Roo.data.SimpleStore({
26785                     id : 'tag',
26786                     fields: ['tag'],
26787                     data : this.formats // from states.js
26788                 }),
26789                 blockFocus : true,
26790                 name : '',
26791                 //autoCreate : {tag: "div",  size: "20"},
26792                 displayField:'tag',
26793                 typeAhead: false,
26794                 mode: 'local',
26795                 editable : false,
26796                 triggerAction: 'all',
26797                 emptyText:'Add tag',
26798                 selectOnFocus:true,
26799                 width:135,
26800                 listeners : {
26801                     'select': function(c, r, i) {
26802                         editor.insertTag(r.get('tag'));
26803                         editor.focus();
26804                     }
26805                 }
26806
26807             });
26808             tb.addField(this.formatCombo);
26809             
26810         }
26811         
26812         if(!this.disable.format){
26813             tb.add(
26814                 btn('bold'),
26815                 btn('italic'),
26816                 btn('underline')
26817             );
26818         };
26819         if(!this.disable.fontSize){
26820             tb.add(
26821                 '-',
26822                 
26823                 
26824                 btn('increasefontsize', false, editor.adjustFont),
26825                 btn('decreasefontsize', false, editor.adjustFont)
26826             );
26827         };
26828         
26829         
26830         if(!this.disable.colors){
26831             tb.add(
26832                 '-', {
26833                     id:editor.frameId +'-forecolor',
26834                     cls:'x-btn-icon x-edit-forecolor',
26835                     clickEvent:'mousedown',
26836                     tooltip: this.buttonTips['forecolor'] || undefined,
26837                     tabIndex:-1,
26838                     menu : new Roo.menu.ColorMenu({
26839                         allowReselect: true,
26840                         focus: Roo.emptyFn,
26841                         value:'000000',
26842                         plain:true,
26843                         selectHandler: function(cp, color){
26844                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26845                             editor.deferFocus();
26846                         },
26847                         scope: editor,
26848                         clickEvent:'mousedown'
26849                     })
26850                 }, {
26851                     id:editor.frameId +'backcolor',
26852                     cls:'x-btn-icon x-edit-backcolor',
26853                     clickEvent:'mousedown',
26854                     tooltip: this.buttonTips['backcolor'] || undefined,
26855                     tabIndex:-1,
26856                     menu : new Roo.menu.ColorMenu({
26857                         focus: Roo.emptyFn,
26858                         value:'FFFFFF',
26859                         plain:true,
26860                         allowReselect: true,
26861                         selectHandler: function(cp, color){
26862                             if(Roo.isGecko){
26863                                 editor.execCmd('useCSS', false);
26864                                 editor.execCmd('hilitecolor', color);
26865                                 editor.execCmd('useCSS', true);
26866                                 editor.deferFocus();
26867                             }else{
26868                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26869                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26870                                 editor.deferFocus();
26871                             }
26872                         },
26873                         scope:editor,
26874                         clickEvent:'mousedown'
26875                     })
26876                 }
26877             );
26878         };
26879         // now add all the items...
26880         
26881
26882         if(!this.disable.alignments){
26883             tb.add(
26884                 '-',
26885                 btn('justifyleft'),
26886                 btn('justifycenter'),
26887                 btn('justifyright')
26888             );
26889         };
26890
26891         //if(!Roo.isSafari){
26892             if(!this.disable.links){
26893                 tb.add(
26894                     '-',
26895                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26896                 );
26897             };
26898
26899             if(!this.disable.lists){
26900                 tb.add(
26901                     '-',
26902                     btn('insertorderedlist'),
26903                     btn('insertunorderedlist')
26904                 );
26905             }
26906             if(!this.disable.sourceEdit){
26907                 tb.add(
26908                     '-',
26909                     btn('sourceedit', true, function(btn){
26910                         this.toggleSourceEdit(btn.pressed);
26911                     })
26912                 );
26913             }
26914         //}
26915         
26916         var smenu = { };
26917         // special menu.. - needs to be tidied up..
26918         if (!this.disable.special) {
26919             smenu = {
26920                 text: "&#169;",
26921                 cls: 'x-edit-none',
26922                 
26923                 menu : {
26924                     items : []
26925                 }
26926             };
26927             for (var i =0; i < this.specialChars.length; i++) {
26928                 smenu.menu.items.push({
26929                     
26930                     html: this.specialChars[i],
26931                     handler: function(a,b) {
26932                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26933                         //editor.insertAtCursor(a.html);
26934                         
26935                     },
26936                     tabIndex:-1
26937                 });
26938             }
26939             
26940             
26941             tb.add(smenu);
26942             
26943             
26944         }
26945          
26946         if (!this.disable.specialElements) {
26947             var semenu = {
26948                 text: "Other;",
26949                 cls: 'x-edit-none',
26950                 menu : {
26951                     items : []
26952                 }
26953             };
26954             for (var i =0; i < this.specialElements.length; i++) {
26955                 semenu.menu.items.push(
26956                     Roo.apply({ 
26957                         handler: function(a,b) {
26958                             editor.insertAtCursor(this.ihtml);
26959                         }
26960                     }, this.specialElements[i])
26961                 );
26962                     
26963             }
26964             
26965             tb.add(semenu);
26966             
26967             
26968         }
26969          
26970         
26971         if (this.btns) {
26972             for(var i =0; i< this.btns.length;i++) {
26973                 var b = Roo.factory(this.btns[i],Roo.form);
26974                 b.cls =  'x-edit-none';
26975                 b.scope = editor;
26976                 tb.add(b);
26977             }
26978         
26979         }
26980         
26981         
26982         
26983         // disable everything...
26984         
26985         this.tb.items.each(function(item){
26986            if(item.id != editor.frameId+ '-sourceedit'){
26987                 item.disable();
26988             }
26989         });
26990         this.rendered = true;
26991         
26992         // the all the btns;
26993         editor.on('editorevent', this.updateToolbar, this);
26994         // other toolbars need to implement this..
26995         //editor.on('editmodechange', this.updateToolbar, this);
26996     },
26997     
26998     
26999     
27000     /**
27001      * Protected method that will not generally be called directly. It triggers
27002      * a toolbar update by reading the markup state of the current selection in the editor.
27003      */
27004     updateToolbar: function(){
27005
27006         if(!this.editor.activated){
27007             this.editor.onFirstFocus();
27008             return;
27009         }
27010
27011         var btns = this.tb.items.map, 
27012             doc = this.editor.doc,
27013             frameId = this.editor.frameId;
27014
27015         if(!this.disable.font && !Roo.isSafari){
27016             /*
27017             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27018             if(name != this.fontSelect.dom.value){
27019                 this.fontSelect.dom.value = name;
27020             }
27021             */
27022         }
27023         if(!this.disable.format){
27024             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27025             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27026             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27027         }
27028         if(!this.disable.alignments){
27029             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27030             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27031             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27032         }
27033         if(!Roo.isSafari && !this.disable.lists){
27034             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27035             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27036         }
27037         
27038         var ans = this.editor.getAllAncestors();
27039         if (this.formatCombo) {
27040             
27041             
27042             var store = this.formatCombo.store;
27043             this.formatCombo.setValue("");
27044             for (var i =0; i < ans.length;i++) {
27045                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27046                     // select it..
27047                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27048                     break;
27049                 }
27050             }
27051         }
27052         
27053         
27054         
27055         // hides menus... - so this cant be on a menu...
27056         Roo.menu.MenuMgr.hideAll();
27057
27058         //this.editorsyncValue();
27059     },
27060    
27061     
27062     createFontOptions : function(){
27063         var buf = [], fs = this.fontFamilies, ff, lc;
27064         
27065         
27066         
27067         for(var i = 0, len = fs.length; i< len; i++){
27068             ff = fs[i];
27069             lc = ff.toLowerCase();
27070             buf.push(
27071                 '<option value="',lc,'" style="font-family:',ff,';"',
27072                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27073                     ff,
27074                 '</option>'
27075             );
27076         }
27077         return buf.join('');
27078     },
27079     
27080     toggleSourceEdit : function(sourceEditMode){
27081         if(sourceEditMode === undefined){
27082             sourceEditMode = !this.sourceEditMode;
27083         }
27084         this.sourceEditMode = sourceEditMode === true;
27085         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27086         // just toggle the button?
27087         if(btn.pressed !== this.editor.sourceEditMode){
27088             btn.toggle(this.editor.sourceEditMode);
27089             return;
27090         }
27091         
27092         if(this.sourceEditMode){
27093             this.tb.items.each(function(item){
27094                 if(item.cmd != 'sourceedit'){
27095                     item.disable();
27096                 }
27097             });
27098           
27099         }else{
27100             if(this.initialized){
27101                 this.tb.items.each(function(item){
27102                     item.enable();
27103                 });
27104             }
27105             
27106         }
27107         // tell the editor that it's been pressed..
27108         this.editor.toggleSourceEdit(sourceEditMode);
27109        
27110     },
27111      /**
27112      * Object collection of toolbar tooltips for the buttons in the editor. The key
27113      * is the command id associated with that button and the value is a valid QuickTips object.
27114      * For example:
27115 <pre><code>
27116 {
27117     bold : {
27118         title: 'Bold (Ctrl+B)',
27119         text: 'Make the selected text bold.',
27120         cls: 'x-html-editor-tip'
27121     },
27122     italic : {
27123         title: 'Italic (Ctrl+I)',
27124         text: 'Make the selected text italic.',
27125         cls: 'x-html-editor-tip'
27126     },
27127     ...
27128 </code></pre>
27129     * @type Object
27130      */
27131     buttonTips : {
27132         bold : {
27133             title: 'Bold (Ctrl+B)',
27134             text: 'Make the selected text bold.',
27135             cls: 'x-html-editor-tip'
27136         },
27137         italic : {
27138             title: 'Italic (Ctrl+I)',
27139             text: 'Make the selected text italic.',
27140             cls: 'x-html-editor-tip'
27141         },
27142         underline : {
27143             title: 'Underline (Ctrl+U)',
27144             text: 'Underline the selected text.',
27145             cls: 'x-html-editor-tip'
27146         },
27147         increasefontsize : {
27148             title: 'Grow Text',
27149             text: 'Increase the font size.',
27150             cls: 'x-html-editor-tip'
27151         },
27152         decreasefontsize : {
27153             title: 'Shrink Text',
27154             text: 'Decrease the font size.',
27155             cls: 'x-html-editor-tip'
27156         },
27157         backcolor : {
27158             title: 'Text Highlight Color',
27159             text: 'Change the background color of the selected text.',
27160             cls: 'x-html-editor-tip'
27161         },
27162         forecolor : {
27163             title: 'Font Color',
27164             text: 'Change the color of the selected text.',
27165             cls: 'x-html-editor-tip'
27166         },
27167         justifyleft : {
27168             title: 'Align Text Left',
27169             text: 'Align text to the left.',
27170             cls: 'x-html-editor-tip'
27171         },
27172         justifycenter : {
27173             title: 'Center Text',
27174             text: 'Center text in the editor.',
27175             cls: 'x-html-editor-tip'
27176         },
27177         justifyright : {
27178             title: 'Align Text Right',
27179             text: 'Align text to the right.',
27180             cls: 'x-html-editor-tip'
27181         },
27182         insertunorderedlist : {
27183             title: 'Bullet List',
27184             text: 'Start a bulleted list.',
27185             cls: 'x-html-editor-tip'
27186         },
27187         insertorderedlist : {
27188             title: 'Numbered List',
27189             text: 'Start a numbered list.',
27190             cls: 'x-html-editor-tip'
27191         },
27192         createlink : {
27193             title: 'Hyperlink',
27194             text: 'Make the selected text a hyperlink.',
27195             cls: 'x-html-editor-tip'
27196         },
27197         sourceedit : {
27198             title: 'Source Edit',
27199             text: 'Switch to source editing mode.',
27200             cls: 'x-html-editor-tip'
27201         }
27202     },
27203     // private
27204     onDestroy : function(){
27205         if(this.rendered){
27206             
27207             this.tb.items.each(function(item){
27208                 if(item.menu){
27209                     item.menu.removeAll();
27210                     if(item.menu.el){
27211                         item.menu.el.destroy();
27212                     }
27213                 }
27214                 item.destroy();
27215             });
27216              
27217         }
27218     },
27219     onFirstFocus: function() {
27220         this.tb.items.each(function(item){
27221            item.enable();
27222         });
27223     }
27224 });
27225
27226
27227
27228
27229 // <script type="text/javascript">
27230 /*
27231  * Based on
27232  * Ext JS Library 1.1.1
27233  * Copyright(c) 2006-2007, Ext JS, LLC.
27234  *  
27235  
27236  */
27237
27238  
27239 /**
27240  * @class Roo.form.HtmlEditor.ToolbarContext
27241  * Context Toolbar
27242  * 
27243  * Usage:
27244  *
27245  new Roo.form.HtmlEditor({
27246     ....
27247     toolbars : [
27248         { xtype: 'ToolbarStandard', styles : {} }
27249         { xtype: 'ToolbarContext', disable : {} }
27250     ]
27251 })
27252
27253      
27254  * 
27255  * @config : {Object} disable List of elements to disable.. (not done yet.)
27256  * @config : {Object} styles  Map of styles available.
27257  * 
27258  */
27259
27260 Roo.form.HtmlEditor.ToolbarContext = function(config)
27261 {
27262     
27263     Roo.apply(this, config);
27264     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27265     // dont call parent... till later.
27266     this.styles = this.styles || {};
27267 }
27268
27269  
27270
27271 Roo.form.HtmlEditor.ToolbarContext.types = {
27272     'IMG' : {
27273         width : {
27274             title: "Width",
27275             width: 40
27276         },
27277         height:  {
27278             title: "Height",
27279             width: 40
27280         },
27281         align: {
27282             title: "Align",
27283             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27284             width : 80
27285             
27286         },
27287         border: {
27288             title: "Border",
27289             width: 40
27290         },
27291         alt: {
27292             title: "Alt",
27293             width: 120
27294         },
27295         src : {
27296             title: "Src",
27297             width: 220
27298         }
27299         
27300     },
27301     'A' : {
27302         name : {
27303             title: "Name",
27304             width: 50
27305         },
27306         href:  {
27307             title: "Href",
27308             width: 220
27309         } // border?
27310         
27311     },
27312     'TABLE' : {
27313         rows : {
27314             title: "Rows",
27315             width: 20
27316         },
27317         cols : {
27318             title: "Cols",
27319             width: 20
27320         },
27321         width : {
27322             title: "Width",
27323             width: 40
27324         },
27325         height : {
27326             title: "Height",
27327             width: 40
27328         },
27329         border : {
27330             title: "Border",
27331             width: 20
27332         }
27333     },
27334     'TD' : {
27335         width : {
27336             title: "Width",
27337             width: 40
27338         },
27339         height : {
27340             title: "Height",
27341             width: 40
27342         },   
27343         align: {
27344             title: "Align",
27345             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27346             width: 80
27347         },
27348         valign: {
27349             title: "Valign",
27350             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27351             width: 80
27352         },
27353         colspan: {
27354             title: "Colspan",
27355             width: 20
27356             
27357         },
27358          'font-family'  : {
27359             title : "Font",
27360             style : 'fontFamily',
27361             displayField: 'display',
27362             optname : 'font-family',
27363             width: 140
27364         }
27365     },
27366     'INPUT' : {
27367         name : {
27368             title: "name",
27369             width: 120
27370         },
27371         value : {
27372             title: "Value",
27373             width: 120
27374         },
27375         width : {
27376             title: "Width",
27377             width: 40
27378         }
27379     },
27380     'LABEL' : {
27381         'for' : {
27382             title: "For",
27383             width: 120
27384         }
27385     },
27386     'TEXTAREA' : {
27387           name : {
27388             title: "name",
27389             width: 120
27390         },
27391         rows : {
27392             title: "Rows",
27393             width: 20
27394         },
27395         cols : {
27396             title: "Cols",
27397             width: 20
27398         }
27399     },
27400     'SELECT' : {
27401         name : {
27402             title: "name",
27403             width: 120
27404         },
27405         selectoptions : {
27406             title: "Options",
27407             width: 200
27408         }
27409     },
27410     
27411     // should we really allow this??
27412     // should this just be 
27413     'BODY' : {
27414         title : {
27415             title: "Title",
27416             width: 200,
27417             disabled : true
27418         }
27419     },
27420     'SPAN' : {
27421         'font-family'  : {
27422             title : "Font",
27423             style : 'fontFamily',
27424             displayField: 'display',
27425             optname : 'font-family',
27426             width: 140
27427         }
27428     },
27429     'DIV' : {
27430         'font-family'  : {
27431             title : "Font",
27432             style : 'fontFamily',
27433             displayField: 'display',
27434             optname : 'font-family',
27435             width: 140
27436         }
27437     },
27438      'P' : {
27439         'font-family'  : {
27440             title : "Font",
27441             style : 'fontFamily',
27442             displayField: 'display',
27443             optname : 'font-family',
27444             width: 140
27445         }
27446     },
27447     
27448     '*' : {
27449         // empty..
27450     }
27451
27452 };
27453
27454 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27455 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27456
27457 Roo.form.HtmlEditor.ToolbarContext.options = {
27458         'font-family'  : [ 
27459                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27460                 [ 'Courier New', 'Courier New'],
27461                 [ 'Tahoma', 'Tahoma'],
27462                 [ 'Times New Roman,serif', 'Times'],
27463                 [ 'Verdana','Verdana' ]
27464         ]
27465 };
27466
27467 // fixme - these need to be configurable..
27468  
27469
27470 Roo.form.HtmlEditor.ToolbarContext.types
27471
27472
27473 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27474     
27475     tb: false,
27476     
27477     rendered: false,
27478     
27479     editor : false,
27480     /**
27481      * @cfg {Object} disable  List of toolbar elements to disable
27482          
27483      */
27484     disable : false,
27485     /**
27486      * @cfg {Object} styles List of styles 
27487      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27488      *
27489      * These must be defined in the page, so they get rendered correctly..
27490      * .headline { }
27491      * TD.underline { }
27492      * 
27493      */
27494     styles : false,
27495     
27496     options: false,
27497     
27498     toolbars : false,
27499     
27500     init : function(editor)
27501     {
27502         this.editor = editor;
27503         
27504         
27505         var fid = editor.frameId;
27506         var etb = this;
27507         function btn(id, toggle, handler){
27508             var xid = fid + '-'+ id ;
27509             return {
27510                 id : xid,
27511                 cmd : id,
27512                 cls : 'x-btn-icon x-edit-'+id,
27513                 enableToggle:toggle !== false,
27514                 scope: editor, // was editor...
27515                 handler:handler||editor.relayBtnCmd,
27516                 clickEvent:'mousedown',
27517                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27518                 tabIndex:-1
27519             };
27520         }
27521         // create a new element.
27522         var wdiv = editor.wrap.createChild({
27523                 tag: 'div'
27524             }, editor.wrap.dom.firstChild.nextSibling, true);
27525         
27526         // can we do this more than once??
27527         
27528          // stop form submits
27529       
27530  
27531         // disable everything...
27532         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27533         this.toolbars = {};
27534            
27535         for (var i in  ty) {
27536           
27537             this.toolbars[i] = this.buildToolbar(ty[i],i);
27538         }
27539         this.tb = this.toolbars.BODY;
27540         this.tb.el.show();
27541         this.buildFooter();
27542         this.footer.show();
27543         editor.on('hide', function( ) { this.footer.hide() }, this);
27544         editor.on('show', function( ) { this.footer.show() }, this);
27545         
27546          
27547         this.rendered = true;
27548         
27549         // the all the btns;
27550         editor.on('editorevent', this.updateToolbar, this);
27551         // other toolbars need to implement this..
27552         //editor.on('editmodechange', this.updateToolbar, this);
27553     },
27554     
27555     
27556     
27557     /**
27558      * Protected method that will not generally be called directly. It triggers
27559      * a toolbar update by reading the markup state of the current selection in the editor.
27560      */
27561     updateToolbar: function(editor,ev,sel){
27562
27563         //Roo.log(ev);
27564         // capture mouse up - this is handy for selecting images..
27565         // perhaps should go somewhere else...
27566         if(!this.editor.activated){
27567              this.editor.onFirstFocus();
27568             return;
27569         }
27570         
27571         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27572         // selectNode - might want to handle IE?
27573         if (ev &&
27574             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27575             ev.target && ev.target.tagName == 'IMG') {
27576             // they have click on an image...
27577             // let's see if we can change the selection...
27578             sel = ev.target;
27579          
27580               var nodeRange = sel.ownerDocument.createRange();
27581             try {
27582                 nodeRange.selectNode(sel);
27583             } catch (e) {
27584                 nodeRange.selectNodeContents(sel);
27585             }
27586             //nodeRange.collapse(true);
27587             var s = editor.win.getSelection();
27588             s.removeAllRanges();
27589             s.addRange(nodeRange);
27590         }  
27591         
27592       
27593         var updateFooter = sel ? false : true;
27594         
27595         
27596         var ans = this.editor.getAllAncestors();
27597         
27598         // pick
27599         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27600         
27601         if (!sel) { 
27602             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27603             sel = sel ? sel : this.editor.doc.body;
27604             sel = sel.tagName.length ? sel : this.editor.doc.body;
27605             
27606         }
27607         // pick a menu that exists..
27608         var tn = sel.tagName.toUpperCase();
27609         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27610         
27611         tn = sel.tagName.toUpperCase();
27612         
27613         var lastSel = this.tb.selectedNode
27614         
27615         this.tb.selectedNode = sel;
27616         
27617         // if current menu does not match..
27618         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27619                 
27620             this.tb.el.hide();
27621             ///console.log("show: " + tn);
27622             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27623             this.tb.el.show();
27624             // update name
27625             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27626             
27627             
27628             // update attributes
27629             if (this.tb.fields) {
27630                 this.tb.fields.each(function(e) {
27631                     if (e.stylename) {
27632                         e.setValue(sel.style[e.stylename]);
27633                         return;
27634                     } 
27635                    e.setValue(sel.getAttribute(e.attrname));
27636                 });
27637             }
27638             
27639             var hasStyles = false;
27640             for(var i in this.styles) {
27641                 hasStyles = true;
27642                 break;
27643             }
27644             
27645             // update styles
27646             if (hasStyles) { 
27647                 var st = this.tb.fields.item(0);
27648                 
27649                 st.store.removeAll();
27650                
27651                 
27652                 var cn = sel.className.split(/\s+/);
27653                 
27654                 var avs = [];
27655                 if (this.styles['*']) {
27656                     
27657                     Roo.each(this.styles['*'], function(v) {
27658                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27659                     });
27660                 }
27661                 if (this.styles[tn]) { 
27662                     Roo.each(this.styles[tn], function(v) {
27663                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27664                     });
27665                 }
27666                 
27667                 st.store.loadData(avs);
27668                 st.collapse();
27669                 st.setValue(cn);
27670             }
27671             // flag our selected Node.
27672             this.tb.selectedNode = sel;
27673            
27674            
27675             Roo.menu.MenuMgr.hideAll();
27676
27677         }
27678         
27679         if (!updateFooter) {
27680             //this.footDisp.dom.innerHTML = ''; 
27681             return;
27682         }
27683         // update the footer
27684         //
27685         var html = '';
27686         
27687         this.footerEls = ans.reverse();
27688         Roo.each(this.footerEls, function(a,i) {
27689             if (!a) { return; }
27690             html += html.length ? ' &gt; '  :  '';
27691             
27692             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27693             
27694         });
27695        
27696         // 
27697         var sz = this.footDisp.up('td').getSize();
27698         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27699         this.footDisp.dom.style.marginLeft = '5px';
27700         
27701         this.footDisp.dom.style.overflow = 'hidden';
27702         
27703         this.footDisp.dom.innerHTML = html;
27704             
27705         //this.editorsyncValue();
27706     },
27707      
27708     
27709    
27710        
27711     // private
27712     onDestroy : function(){
27713         if(this.rendered){
27714             
27715             this.tb.items.each(function(item){
27716                 if(item.menu){
27717                     item.menu.removeAll();
27718                     if(item.menu.el){
27719                         item.menu.el.destroy();
27720                     }
27721                 }
27722                 item.destroy();
27723             });
27724              
27725         }
27726     },
27727     onFirstFocus: function() {
27728         // need to do this for all the toolbars..
27729         this.tb.items.each(function(item){
27730            item.enable();
27731         });
27732     },
27733     buildToolbar: function(tlist, nm)
27734     {
27735         var editor = this.editor;
27736          // create a new element.
27737         var wdiv = editor.wrap.createChild({
27738                 tag: 'div'
27739             }, editor.wrap.dom.firstChild.nextSibling, true);
27740         
27741        
27742         var tb = new Roo.Toolbar(wdiv);
27743         // add the name..
27744         
27745         tb.add(nm+ ":&nbsp;");
27746         
27747         var styles = [];
27748         for(var i in this.styles) {
27749             styles.push(i);
27750         }
27751         
27752         // styles...
27753         if (styles && styles.length) {
27754             
27755             // this needs a multi-select checkbox...
27756             tb.addField( new Roo.form.ComboBox({
27757                 store: new Roo.data.SimpleStore({
27758                     id : 'val',
27759                     fields: ['val', 'selected'],
27760                     data : [] 
27761                 }),
27762                 name : '-roo-edit-className',
27763                 attrname : 'className',
27764                 displayField: 'val',
27765                 typeAhead: false,
27766                 mode: 'local',
27767                 editable : false,
27768                 triggerAction: 'all',
27769                 emptyText:'Select Style',
27770                 selectOnFocus:true,
27771                 width: 130,
27772                 listeners : {
27773                     'select': function(c, r, i) {
27774                         // initial support only for on class per el..
27775                         tb.selectedNode.className =  r ? r.get('val') : '';
27776                         editor.syncValue();
27777                     }
27778                 }
27779     
27780             }));
27781         }
27782         
27783         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27784         var tbops = tbc.options;
27785         
27786         for (var i in tlist) {
27787             
27788             var item = tlist[i];
27789             tb.add(item.title + ":&nbsp;");
27790             
27791             
27792             //optname == used so you can configure the options available..
27793             var opts = item.opts ? item.opts : false;
27794             if (item.optname) {
27795                 opts = tbops[item.optname];
27796            
27797             }
27798             
27799             if (opts) {
27800                 // opts == pulldown..
27801                 tb.addField( new Roo.form.ComboBox({
27802                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27803                         id : 'val',
27804                         fields: ['val', 'display'],
27805                         data : opts  
27806                     }),
27807                     name : '-roo-edit-' + i,
27808                     attrname : i,
27809                     stylename : item.style ? item.style : false,
27810                     displayField: item.displayField ? item.displayField : 'val',
27811                     valueField :  'val',
27812                     typeAhead: false,
27813                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27814                     editable : false,
27815                     triggerAction: 'all',
27816                     emptyText:'Select',
27817                     selectOnFocus:true,
27818                     width: item.width ? item.width  : 130,
27819                     listeners : {
27820                         'select': function(c, r, i) {
27821                             if (c.stylename) {
27822                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27823                                 return;
27824                             }
27825                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27826                         }
27827                     }
27828
27829                 }));
27830                 continue;
27831                     
27832                  
27833                 
27834                 tb.addField( new Roo.form.TextField({
27835                     name: i,
27836                     width: 100,
27837                     //allowBlank:false,
27838                     value: ''
27839                 }));
27840                 continue;
27841             }
27842             tb.addField( new Roo.form.TextField({
27843                 name: '-roo-edit-' + i,
27844                 attrname : i,
27845                 
27846                 width: item.width,
27847                 //allowBlank:true,
27848                 value: '',
27849                 listeners: {
27850                     'change' : function(f, nv, ov) {
27851                         tb.selectedNode.setAttribute(f.attrname, nv);
27852                     }
27853                 }
27854             }));
27855              
27856         }
27857         tb.addFill();
27858         var _this = this;
27859         tb.addButton( {
27860             text: 'Remove Tag',
27861     
27862             listeners : {
27863                 click : function ()
27864                 {
27865                     // remove
27866                     // undo does not work.
27867                      
27868                     var sn = tb.selectedNode;
27869                     
27870                     var pn = sn.parentNode;
27871                     
27872                     var stn =  sn.childNodes[0];
27873                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27874                     while (sn.childNodes.length) {
27875                         var node = sn.childNodes[0];
27876                         sn.removeChild(node);
27877                         //Roo.log(node);
27878                         pn.insertBefore(node, sn);
27879                         
27880                     }
27881                     pn.removeChild(sn);
27882                     var range = editor.createRange();
27883         
27884                     range.setStart(stn,0);
27885                     range.setEnd(en,0); //????
27886                     //range.selectNode(sel);
27887                     
27888                     
27889                     var selection = editor.getSelection();
27890                     selection.removeAllRanges();
27891                     selection.addRange(range);
27892                     
27893                     
27894                     
27895                     //_this.updateToolbar(null, null, pn);
27896                     _this.updateToolbar(null, null, null);
27897                     _this.footDisp.dom.innerHTML = ''; 
27898                 }
27899             }
27900             
27901                     
27902                 
27903             
27904         });
27905         
27906         
27907         tb.el.on('click', function(e){
27908             e.preventDefault(); // what does this do?
27909         });
27910         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27911         tb.el.hide();
27912         tb.name = nm;
27913         // dont need to disable them... as they will get hidden
27914         return tb;
27915          
27916         
27917     },
27918     buildFooter : function()
27919     {
27920         
27921         var fel = this.editor.wrap.createChild();
27922         this.footer = new Roo.Toolbar(fel);
27923         // toolbar has scrolly on left / right?
27924         var footDisp= new Roo.Toolbar.Fill();
27925         var _t = this;
27926         this.footer.add(
27927             {
27928                 text : '&lt;',
27929                 xtype: 'Button',
27930                 handler : function() {
27931                     _t.footDisp.scrollTo('left',0,true)
27932                 }
27933             }
27934         );
27935         this.footer.add( footDisp );
27936         this.footer.add( 
27937             {
27938                 text : '&gt;',
27939                 xtype: 'Button',
27940                 handler : function() {
27941                     // no animation..
27942                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27943                 }
27944             }
27945         );
27946         var fel = Roo.get(footDisp.el);
27947         fel.addClass('x-editor-context');
27948         this.footDispWrap = fel; 
27949         this.footDispWrap.overflow  = 'hidden';
27950         
27951         this.footDisp = fel.createChild();
27952         this.footDispWrap.on('click', this.onContextClick, this)
27953         
27954         
27955     },
27956     onContextClick : function (ev,dom)
27957     {
27958         ev.preventDefault();
27959         var  cn = dom.className;
27960         //Roo.log(cn);
27961         if (!cn.match(/x-ed-loc-/)) {
27962             return;
27963         }
27964         var n = cn.split('-').pop();
27965         var ans = this.footerEls;
27966         var sel = ans[n];
27967         
27968          // pick
27969         var range = this.editor.createRange();
27970         
27971         range.selectNodeContents(sel);
27972         //range.selectNode(sel);
27973         
27974         
27975         var selection = this.editor.getSelection();
27976         selection.removeAllRanges();
27977         selection.addRange(range);
27978         
27979         
27980         
27981         this.updateToolbar(null, null, sel);
27982         
27983         
27984     }
27985     
27986     
27987     
27988     
27989     
27990 });
27991
27992
27993
27994
27995
27996 /*
27997  * Based on:
27998  * Ext JS Library 1.1.1
27999  * Copyright(c) 2006-2007, Ext JS, LLC.
28000  *
28001  * Originally Released Under LGPL - original licence link has changed is not relivant.
28002  *
28003  * Fork - LGPL
28004  * <script type="text/javascript">
28005  */
28006  
28007 /**
28008  * @class Roo.form.BasicForm
28009  * @extends Roo.util.Observable
28010  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28011  * @constructor
28012  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28013  * @param {Object} config Configuration options
28014  */
28015 Roo.form.BasicForm = function(el, config){
28016     this.allItems = [];
28017     this.childForms = [];
28018     Roo.apply(this, config);
28019     /*
28020      * The Roo.form.Field items in this form.
28021      * @type MixedCollection
28022      */
28023      
28024      
28025     this.items = new Roo.util.MixedCollection(false, function(o){
28026         return o.id || (o.id = Roo.id());
28027     });
28028     this.addEvents({
28029         /**
28030          * @event beforeaction
28031          * Fires before any action is performed. Return false to cancel the action.
28032          * @param {Form} this
28033          * @param {Action} action The action to be performed
28034          */
28035         beforeaction: true,
28036         /**
28037          * @event actionfailed
28038          * Fires when an action fails.
28039          * @param {Form} this
28040          * @param {Action} action The action that failed
28041          */
28042         actionfailed : true,
28043         /**
28044          * @event actioncomplete
28045          * Fires when an action is completed.
28046          * @param {Form} this
28047          * @param {Action} action The action that completed
28048          */
28049         actioncomplete : true
28050     });
28051     if(el){
28052         this.initEl(el);
28053     }
28054     Roo.form.BasicForm.superclass.constructor.call(this);
28055 };
28056
28057 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28058     /**
28059      * @cfg {String} method
28060      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28061      */
28062     /**
28063      * @cfg {DataReader} reader
28064      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28065      * This is optional as there is built-in support for processing JSON.
28066      */
28067     /**
28068      * @cfg {DataReader} errorReader
28069      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28070      * This is completely optional as there is built-in support for processing JSON.
28071      */
28072     /**
28073      * @cfg {String} url
28074      * The URL to use for form actions if one isn't supplied in the action options.
28075      */
28076     /**
28077      * @cfg {Boolean} fileUpload
28078      * Set to true if this form is a file upload.
28079      */
28080      
28081     /**
28082      * @cfg {Object} baseParams
28083      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28084      */
28085      /**
28086      
28087     /**
28088      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28089      */
28090     timeout: 30,
28091
28092     // private
28093     activeAction : null,
28094
28095     /**
28096      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28097      * or setValues() data instead of when the form was first created.
28098      */
28099     trackResetOnLoad : false,
28100     
28101     
28102     /**
28103      * childForms - used for multi-tab forms
28104      * @type {Array}
28105      */
28106     childForms : false,
28107     
28108     /**
28109      * allItems - full list of fields.
28110      * @type {Array}
28111      */
28112     allItems : false,
28113     
28114     /**
28115      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28116      * element by passing it or its id or mask the form itself by passing in true.
28117      * @type Mixed
28118      */
28119     waitMsgTarget : false,
28120
28121     // private
28122     initEl : function(el){
28123         this.el = Roo.get(el);
28124         this.id = this.el.id || Roo.id();
28125         this.el.on('submit', this.onSubmit, this);
28126         this.el.addClass('x-form');
28127     },
28128
28129     // private
28130     onSubmit : function(e){
28131         e.stopEvent();
28132     },
28133
28134     /**
28135      * Returns true if client-side validation on the form is successful.
28136      * @return Boolean
28137      */
28138     isValid : function(){
28139         var valid = true;
28140         this.items.each(function(f){
28141            if(!f.validate()){
28142                valid = false;
28143            }
28144         });
28145         return valid;
28146     },
28147
28148     /**
28149      * Returns true if any fields in this form have changed since their original load.
28150      * @return Boolean
28151      */
28152     isDirty : function(){
28153         var dirty = false;
28154         this.items.each(function(f){
28155            if(f.isDirty()){
28156                dirty = true;
28157                return false;
28158            }
28159         });
28160         return dirty;
28161     },
28162
28163     /**
28164      * Performs a predefined action (submit or load) or custom actions you define on this form.
28165      * @param {String} actionName The name of the action type
28166      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28167      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28168      * accept other config options):
28169      * <pre>
28170 Property          Type             Description
28171 ----------------  ---------------  ----------------------------------------------------------------------------------
28172 url               String           The url for the action (defaults to the form's url)
28173 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28174 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28175 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28176                                    validate the form on the client (defaults to false)
28177      * </pre>
28178      * @return {BasicForm} this
28179      */
28180     doAction : function(action, options){
28181         if(typeof action == 'string'){
28182             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28183         }
28184         if(this.fireEvent('beforeaction', this, action) !== false){
28185             this.beforeAction(action);
28186             action.run.defer(100, action);
28187         }
28188         return this;
28189     },
28190
28191     /**
28192      * Shortcut to do a submit action.
28193      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28194      * @return {BasicForm} this
28195      */
28196     submit : function(options){
28197         this.doAction('submit', options);
28198         return this;
28199     },
28200
28201     /**
28202      * Shortcut to do a load action.
28203      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28204      * @return {BasicForm} this
28205      */
28206     load : function(options){
28207         this.doAction('load', options);
28208         return this;
28209     },
28210
28211     /**
28212      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28213      * @param {Record} record The record to edit
28214      * @return {BasicForm} this
28215      */
28216     updateRecord : function(record){
28217         record.beginEdit();
28218         var fs = record.fields;
28219         fs.each(function(f){
28220             var field = this.findField(f.name);
28221             if(field){
28222                 record.set(f.name, field.getValue());
28223             }
28224         }, this);
28225         record.endEdit();
28226         return this;
28227     },
28228
28229     /**
28230      * Loads an Roo.data.Record into this form.
28231      * @param {Record} record The record to load
28232      * @return {BasicForm} this
28233      */
28234     loadRecord : function(record){
28235         this.setValues(record.data);
28236         return this;
28237     },
28238
28239     // private
28240     beforeAction : function(action){
28241         var o = action.options;
28242         
28243        
28244         if(this.waitMsgTarget === true){
28245             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28246         }else if(this.waitMsgTarget){
28247             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28248             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28249         }else {
28250             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28251         }
28252          
28253     },
28254
28255     // private
28256     afterAction : function(action, success){
28257         this.activeAction = null;
28258         var o = action.options;
28259         
28260         if(this.waitMsgTarget === true){
28261             this.el.unmask();
28262         }else if(this.waitMsgTarget){
28263             this.waitMsgTarget.unmask();
28264         }else{
28265             Roo.MessageBox.updateProgress(1);
28266             Roo.MessageBox.hide();
28267         }
28268          
28269         if(success){
28270             if(o.reset){
28271                 this.reset();
28272             }
28273             Roo.callback(o.success, o.scope, [this, action]);
28274             this.fireEvent('actioncomplete', this, action);
28275             
28276         }else{
28277             
28278             // failure condition..
28279             // we have a scenario where updates need confirming.
28280             // eg. if a locking scenario exists..
28281             // we look for { errors : { needs_confirm : true }} in the response.
28282             if (
28283                 (typeof(action.result) != 'undefined')  &&
28284                 (typeof(action.result.errors) != 'undefined')  &&
28285                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28286            ){
28287                 var _t = this;
28288                 Roo.MessageBox.confirm(
28289                     "Change requires confirmation",
28290                     action.result.errorMsg,
28291                     function(r) {
28292                         if (r != 'yes') {
28293                             return;
28294                         }
28295                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28296                     }
28297                     
28298                 );
28299                 
28300                 
28301                 
28302                 return;
28303             }
28304             
28305             Roo.callback(o.failure, o.scope, [this, action]);
28306             // show an error message if no failed handler is set..
28307             if (!this.hasListener('actionfailed')) {
28308                 Roo.MessageBox.alert("Error",
28309                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28310                         action.result.errorMsg :
28311                         "Saving Failed, please check your entries or try again"
28312                 );
28313             }
28314             
28315             this.fireEvent('actionfailed', this, action);
28316         }
28317         
28318     },
28319
28320     /**
28321      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28322      * @param {String} id The value to search for
28323      * @return Field
28324      */
28325     findField : function(id){
28326         var field = this.items.get(id);
28327         if(!field){
28328             this.items.each(function(f){
28329                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28330                     field = f;
28331                     return false;
28332                 }
28333             });
28334         }
28335         return field || null;
28336     },
28337
28338     /**
28339      * Add a secondary form to this one, 
28340      * Used to provide tabbed forms. One form is primary, with hidden values 
28341      * which mirror the elements from the other forms.
28342      * 
28343      * @param {Roo.form.Form} form to add.
28344      * 
28345      */
28346     addForm : function(form)
28347     {
28348        
28349         if (this.childForms.indexOf(form) > -1) {
28350             // already added..
28351             return;
28352         }
28353         this.childForms.push(form);
28354         var n = '';
28355         Roo.each(form.allItems, function (fe) {
28356             
28357             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28358             if (this.findField(n)) { // already added..
28359                 return;
28360             }
28361             var add = new Roo.form.Hidden({
28362                 name : n
28363             });
28364             add.render(this.el);
28365             
28366             this.add( add );
28367         }, this);
28368         
28369     },
28370     /**
28371      * Mark fields in this form invalid in bulk.
28372      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28373      * @return {BasicForm} this
28374      */
28375     markInvalid : function(errors){
28376         if(errors instanceof Array){
28377             for(var i = 0, len = errors.length; i < len; i++){
28378                 var fieldError = errors[i];
28379                 var f = this.findField(fieldError.id);
28380                 if(f){
28381                     f.markInvalid(fieldError.msg);
28382                 }
28383             }
28384         }else{
28385             var field, id;
28386             for(id in errors){
28387                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28388                     field.markInvalid(errors[id]);
28389                 }
28390             }
28391         }
28392         Roo.each(this.childForms || [], function (f) {
28393             f.markInvalid(errors);
28394         });
28395         
28396         return this;
28397     },
28398
28399     /**
28400      * Set values for fields in this form in bulk.
28401      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28402      * @return {BasicForm} this
28403      */
28404     setValues : function(values){
28405         if(values instanceof Array){ // array of objects
28406             for(var i = 0, len = values.length; i < len; i++){
28407                 var v = values[i];
28408                 var f = this.findField(v.id);
28409                 if(f){
28410                     f.setValue(v.value);
28411                     if(this.trackResetOnLoad){
28412                         f.originalValue = f.getValue();
28413                     }
28414                 }
28415             }
28416         }else{ // object hash
28417             var field, id;
28418             for(id in values){
28419                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28420                     
28421                     if (field.setFromData && 
28422                         field.valueField && 
28423                         field.displayField &&
28424                         // combos' with local stores can 
28425                         // be queried via setValue()
28426                         // to set their value..
28427                         (field.store && !field.store.isLocal)
28428                         ) {
28429                         // it's a combo
28430                         var sd = { };
28431                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28432                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28433                         field.setFromData(sd);
28434                         
28435                     } else {
28436                         field.setValue(values[id]);
28437                     }
28438                     
28439                     
28440                     if(this.trackResetOnLoad){
28441                         field.originalValue = field.getValue();
28442                     }
28443                 }
28444             }
28445         }
28446          
28447         Roo.each(this.childForms || [], function (f) {
28448             f.setValues(values);
28449         });
28450                 
28451         return this;
28452     },
28453
28454     /**
28455      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28456      * they are returned as an array.
28457      * @param {Boolean} asString
28458      * @return {Object}
28459      */
28460     getValues : function(asString){
28461         if (this.childForms) {
28462             // copy values from the child forms
28463             Roo.each(this.childForms, function (f) {
28464                 this.setValues(f.getValues());
28465             }, this);
28466         }
28467         
28468         
28469         
28470         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28471         if(asString === true){
28472             return fs;
28473         }
28474         return Roo.urlDecode(fs);
28475     },
28476     
28477     /**
28478      * Returns the fields in this form as an object with key/value pairs. 
28479      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28480      * @return {Object}
28481      */
28482     getFieldValues : function(with_hidden)
28483     {
28484         if (this.childForms) {
28485             // copy values from the child forms
28486             // should this call getFieldValues - probably not as we do not currently copy
28487             // hidden fields when we generate..
28488             Roo.each(this.childForms, function (f) {
28489                 this.setValues(f.getValues());
28490             }, this);
28491         }
28492         
28493         var ret = {};
28494         this.items.each(function(f){
28495             if (!f.getName()) {
28496                 return;
28497             }
28498             var v = f.getValue();
28499             if (f.inputType =='radio') {
28500                 if (typeof(ret[f.getName()]) == 'undefined') {
28501                     ret[f.getName()] = ''; // empty..
28502                 }
28503                 
28504                 if (!f.el.dom.checked) {
28505                     return;
28506                     
28507                 }
28508                 v = f.el.dom.value;
28509                 
28510             }
28511             
28512             // not sure if this supported any more..
28513             if ((typeof(v) == 'object') && f.getRawValue) {
28514                 v = f.getRawValue() ; // dates..
28515             }
28516             // combo boxes where name != hiddenName...
28517             if (f.name != f.getName()) {
28518                 ret[f.name] = f.getRawValue();
28519             }
28520             ret[f.getName()] = v;
28521         });
28522         
28523         return ret;
28524     },
28525
28526     /**
28527      * Clears all invalid messages in this form.
28528      * @return {BasicForm} this
28529      */
28530     clearInvalid : function(){
28531         this.items.each(function(f){
28532            f.clearInvalid();
28533         });
28534         
28535         Roo.each(this.childForms || [], function (f) {
28536             f.clearInvalid();
28537         });
28538         
28539         
28540         return this;
28541     },
28542
28543     /**
28544      * Resets this form.
28545      * @return {BasicForm} this
28546      */
28547     reset : function(){
28548         this.items.each(function(f){
28549             f.reset();
28550         });
28551         
28552         Roo.each(this.childForms || [], function (f) {
28553             f.reset();
28554         });
28555        
28556         
28557         return this;
28558     },
28559
28560     /**
28561      * Add Roo.form components to this form.
28562      * @param {Field} field1
28563      * @param {Field} field2 (optional)
28564      * @param {Field} etc (optional)
28565      * @return {BasicForm} this
28566      */
28567     add : function(){
28568         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28569         return this;
28570     },
28571
28572
28573     /**
28574      * Removes a field from the items collection (does NOT remove its markup).
28575      * @param {Field} field
28576      * @return {BasicForm} this
28577      */
28578     remove : function(field){
28579         this.items.remove(field);
28580         return this;
28581     },
28582
28583     /**
28584      * Looks at the fields in this form, checks them for an id attribute,
28585      * and calls applyTo on the existing dom element with that id.
28586      * @return {BasicForm} this
28587      */
28588     render : function(){
28589         this.items.each(function(f){
28590             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28591                 f.applyTo(f.id);
28592             }
28593         });
28594         return this;
28595     },
28596
28597     /**
28598      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28599      * @param {Object} values
28600      * @return {BasicForm} this
28601      */
28602     applyToFields : function(o){
28603         this.items.each(function(f){
28604            Roo.apply(f, o);
28605         });
28606         return this;
28607     },
28608
28609     /**
28610      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28611      * @param {Object} values
28612      * @return {BasicForm} this
28613      */
28614     applyIfToFields : function(o){
28615         this.items.each(function(f){
28616            Roo.applyIf(f, o);
28617         });
28618         return this;
28619     }
28620 });
28621
28622 // back compat
28623 Roo.BasicForm = Roo.form.BasicForm;/*
28624  * Based on:
28625  * Ext JS Library 1.1.1
28626  * Copyright(c) 2006-2007, Ext JS, LLC.
28627  *
28628  * Originally Released Under LGPL - original licence link has changed is not relivant.
28629  *
28630  * Fork - LGPL
28631  * <script type="text/javascript">
28632  */
28633
28634 /**
28635  * @class Roo.form.Form
28636  * @extends Roo.form.BasicForm
28637  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28638  * @constructor
28639  * @param {Object} config Configuration options
28640  */
28641 Roo.form.Form = function(config){
28642     var xitems =  [];
28643     if (config.items) {
28644         xitems = config.items;
28645         delete config.items;
28646     }
28647    
28648     
28649     Roo.form.Form.superclass.constructor.call(this, null, config);
28650     this.url = this.url || this.action;
28651     if(!this.root){
28652         this.root = new Roo.form.Layout(Roo.applyIf({
28653             id: Roo.id()
28654         }, config));
28655     }
28656     this.active = this.root;
28657     /**
28658      * Array of all the buttons that have been added to this form via {@link addButton}
28659      * @type Array
28660      */
28661     this.buttons = [];
28662     this.allItems = [];
28663     this.addEvents({
28664         /**
28665          * @event clientvalidation
28666          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28667          * @param {Form} this
28668          * @param {Boolean} valid true if the form has passed client-side validation
28669          */
28670         clientvalidation: true,
28671         /**
28672          * @event rendered
28673          * Fires when the form is rendered
28674          * @param {Roo.form.Form} form
28675          */
28676         rendered : true
28677     });
28678     
28679     if (this.progressUrl) {
28680             // push a hidden field onto the list of fields..
28681             this.addxtype( {
28682                     xns: Roo.form, 
28683                     xtype : 'Hidden', 
28684                     name : 'UPLOAD_IDENTIFIER' 
28685             });
28686         }
28687         
28688     
28689     Roo.each(xitems, this.addxtype, this);
28690     
28691     
28692     
28693 };
28694
28695 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28696     /**
28697      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28698      */
28699     /**
28700      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28701      */
28702     /**
28703      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28704      */
28705     buttonAlign:'center',
28706
28707     /**
28708      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28709      */
28710     minButtonWidth:75,
28711
28712     /**
28713      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28714      * This property cascades to child containers if not set.
28715      */
28716     labelAlign:'left',
28717
28718     /**
28719      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28720      * fires a looping event with that state. This is required to bind buttons to the valid
28721      * state using the config value formBind:true on the button.
28722      */
28723     monitorValid : false,
28724
28725     /**
28726      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28727      */
28728     monitorPoll : 200,
28729     
28730     /**
28731      * @cfg {String} progressUrl - Url to return progress data 
28732      */
28733     
28734     progressUrl : false,
28735   
28736     /**
28737      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28738      * fields are added and the column is closed. If no fields are passed the column remains open
28739      * until end() is called.
28740      * @param {Object} config The config to pass to the column
28741      * @param {Field} field1 (optional)
28742      * @param {Field} field2 (optional)
28743      * @param {Field} etc (optional)
28744      * @return Column The column container object
28745      */
28746     column : function(c){
28747         var col = new Roo.form.Column(c);
28748         this.start(col);
28749         if(arguments.length > 1){ // duplicate code required because of Opera
28750             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28751             this.end();
28752         }
28753         return col;
28754     },
28755
28756     /**
28757      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28758      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28759      * until end() is called.
28760      * @param {Object} config The config to pass to the fieldset
28761      * @param {Field} field1 (optional)
28762      * @param {Field} field2 (optional)
28763      * @param {Field} etc (optional)
28764      * @return FieldSet The fieldset container object
28765      */
28766     fieldset : function(c){
28767         var fs = new Roo.form.FieldSet(c);
28768         this.start(fs);
28769         if(arguments.length > 1){ // duplicate code required because of Opera
28770             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28771             this.end();
28772         }
28773         return fs;
28774     },
28775
28776     /**
28777      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28778      * fields are added and the container is closed. If no fields are passed the container remains open
28779      * until end() is called.
28780      * @param {Object} config The config to pass to the Layout
28781      * @param {Field} field1 (optional)
28782      * @param {Field} field2 (optional)
28783      * @param {Field} etc (optional)
28784      * @return Layout The container object
28785      */
28786     container : function(c){
28787         var l = new Roo.form.Layout(c);
28788         this.start(l);
28789         if(arguments.length > 1){ // duplicate code required because of Opera
28790             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28791             this.end();
28792         }
28793         return l;
28794     },
28795
28796     /**
28797      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28798      * @param {Object} container A Roo.form.Layout or subclass of Layout
28799      * @return {Form} this
28800      */
28801     start : function(c){
28802         // cascade label info
28803         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28804         this.active.stack.push(c);
28805         c.ownerCt = this.active;
28806         this.active = c;
28807         return this;
28808     },
28809
28810     /**
28811      * Closes the current open container
28812      * @return {Form} this
28813      */
28814     end : function(){
28815         if(this.active == this.root){
28816             return this;
28817         }
28818         this.active = this.active.ownerCt;
28819         return this;
28820     },
28821
28822     /**
28823      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28824      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28825      * as the label of the field.
28826      * @param {Field} field1
28827      * @param {Field} field2 (optional)
28828      * @param {Field} etc. (optional)
28829      * @return {Form} this
28830      */
28831     add : function(){
28832         this.active.stack.push.apply(this.active.stack, arguments);
28833         this.allItems.push.apply(this.allItems,arguments);
28834         var r = [];
28835         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28836             if(a[i].isFormField){
28837                 r.push(a[i]);
28838             }
28839         }
28840         if(r.length > 0){
28841             Roo.form.Form.superclass.add.apply(this, r);
28842         }
28843         return this;
28844     },
28845     
28846
28847     
28848     
28849     
28850      /**
28851      * Find any element that has been added to a form, using it's ID or name
28852      * This can include framesets, columns etc. along with regular fields..
28853      * @param {String} id - id or name to find.
28854      
28855      * @return {Element} e - or false if nothing found.
28856      */
28857     findbyId : function(id)
28858     {
28859         var ret = false;
28860         if (!id) {
28861             return ret;
28862         }
28863         Roo.each(this.allItems, function(f){
28864             if (f.id == id || f.name == id ){
28865                 ret = f;
28866                 return false;
28867             }
28868         });
28869         return ret;
28870     },
28871
28872     
28873     
28874     /**
28875      * Render this form into the passed container. This should only be called once!
28876      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28877      * @return {Form} this
28878      */
28879     render : function(ct)
28880     {
28881         
28882         
28883         
28884         ct = Roo.get(ct);
28885         var o = this.autoCreate || {
28886             tag: 'form',
28887             method : this.method || 'POST',
28888             id : this.id || Roo.id()
28889         };
28890         this.initEl(ct.createChild(o));
28891
28892         this.root.render(this.el);
28893         
28894        
28895              
28896         this.items.each(function(f){
28897             f.render('x-form-el-'+f.id);
28898         });
28899
28900         if(this.buttons.length > 0){
28901             // tables are required to maintain order and for correct IE layout
28902             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28903                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28904                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28905             }}, null, true);
28906             var tr = tb.getElementsByTagName('tr')[0];
28907             for(var i = 0, len = this.buttons.length; i < len; i++) {
28908                 var b = this.buttons[i];
28909                 var td = document.createElement('td');
28910                 td.className = 'x-form-btn-td';
28911                 b.render(tr.appendChild(td));
28912             }
28913         }
28914         if(this.monitorValid){ // initialize after render
28915             this.startMonitoring();
28916         }
28917         this.fireEvent('rendered', this);
28918         return this;
28919     },
28920
28921     /**
28922      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28923      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28924      * object or a valid Roo.DomHelper element config
28925      * @param {Function} handler The function called when the button is clicked
28926      * @param {Object} scope (optional) The scope of the handler function
28927      * @return {Roo.Button}
28928      */
28929     addButton : function(config, handler, scope){
28930         var bc = {
28931             handler: handler,
28932             scope: scope,
28933             minWidth: this.minButtonWidth,
28934             hideParent:true
28935         };
28936         if(typeof config == "string"){
28937             bc.text = config;
28938         }else{
28939             Roo.apply(bc, config);
28940         }
28941         var btn = new Roo.Button(null, bc);
28942         this.buttons.push(btn);
28943         return btn;
28944     },
28945
28946      /**
28947      * Adds a series of form elements (using the xtype property as the factory method.
28948      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28949      * @param {Object} config 
28950      */
28951     
28952     addxtype : function()
28953     {
28954         var ar = Array.prototype.slice.call(arguments, 0);
28955         var ret = false;
28956         for(var i = 0; i < ar.length; i++) {
28957             if (!ar[i]) {
28958                 continue; // skip -- if this happends something invalid got sent, we 
28959                 // should ignore it, as basically that interface element will not show up
28960                 // and that should be pretty obvious!!
28961             }
28962             
28963             if (Roo.form[ar[i].xtype]) {
28964                 ar[i].form = this;
28965                 var fe = Roo.factory(ar[i], Roo.form);
28966                 if (!ret) {
28967                     ret = fe;
28968                 }
28969                 fe.form = this;
28970                 if (fe.store) {
28971                     fe.store.form = this;
28972                 }
28973                 if (fe.isLayout) {  
28974                          
28975                     this.start(fe);
28976                     this.allItems.push(fe);
28977                     if (fe.items && fe.addxtype) {
28978                         fe.addxtype.apply(fe, fe.items);
28979                         delete fe.items;
28980                     }
28981                      this.end();
28982                     continue;
28983                 }
28984                 
28985                 
28986                  
28987                 this.add(fe);
28988               //  console.log('adding ' + ar[i].xtype);
28989             }
28990             if (ar[i].xtype == 'Button') {  
28991                 //console.log('adding button');
28992                 //console.log(ar[i]);
28993                 this.addButton(ar[i]);
28994                 this.allItems.push(fe);
28995                 continue;
28996             }
28997             
28998             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28999                 alert('end is not supported on xtype any more, use items');
29000             //    this.end();
29001             //    //console.log('adding end');
29002             }
29003             
29004         }
29005         return ret;
29006     },
29007     
29008     /**
29009      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29010      * option "monitorValid"
29011      */
29012     startMonitoring : function(){
29013         if(!this.bound){
29014             this.bound = true;
29015             Roo.TaskMgr.start({
29016                 run : this.bindHandler,
29017                 interval : this.monitorPoll || 200,
29018                 scope: this
29019             });
29020         }
29021     },
29022
29023     /**
29024      * Stops monitoring of the valid state of this form
29025      */
29026     stopMonitoring : function(){
29027         this.bound = false;
29028     },
29029
29030     // private
29031     bindHandler : function(){
29032         if(!this.bound){
29033             return false; // stops binding
29034         }
29035         var valid = true;
29036         this.items.each(function(f){
29037             if(!f.isValid(true)){
29038                 valid = false;
29039                 return false;
29040             }
29041         });
29042         for(var i = 0, len = this.buttons.length; i < len; i++){
29043             var btn = this.buttons[i];
29044             if(btn.formBind === true && btn.disabled === valid){
29045                 btn.setDisabled(!valid);
29046             }
29047         }
29048         this.fireEvent('clientvalidation', this, valid);
29049     }
29050     
29051     
29052     
29053     
29054     
29055     
29056     
29057     
29058 });
29059
29060
29061 // back compat
29062 Roo.Form = Roo.form.Form;
29063 /*
29064  * Based on:
29065  * Ext JS Library 1.1.1
29066  * Copyright(c) 2006-2007, Ext JS, LLC.
29067  *
29068  * Originally Released Under LGPL - original licence link has changed is not relivant.
29069  *
29070  * Fork - LGPL
29071  * <script type="text/javascript">
29072  */
29073  
29074  /**
29075  * @class Roo.form.Action
29076  * Internal Class used to handle form actions
29077  * @constructor
29078  * @param {Roo.form.BasicForm} el The form element or its id
29079  * @param {Object} config Configuration options
29080  */
29081  
29082  
29083 // define the action interface
29084 Roo.form.Action = function(form, options){
29085     this.form = form;
29086     this.options = options || {};
29087 };
29088 /**
29089  * Client Validation Failed
29090  * @const 
29091  */
29092 Roo.form.Action.CLIENT_INVALID = 'client';
29093 /**
29094  * Server Validation Failed
29095  * @const 
29096  */
29097  Roo.form.Action.SERVER_INVALID = 'server';
29098  /**
29099  * Connect to Server Failed
29100  * @const 
29101  */
29102 Roo.form.Action.CONNECT_FAILURE = 'connect';
29103 /**
29104  * Reading Data from Server Failed
29105  * @const 
29106  */
29107 Roo.form.Action.LOAD_FAILURE = 'load';
29108
29109 Roo.form.Action.prototype = {
29110     type : 'default',
29111     failureType : undefined,
29112     response : undefined,
29113     result : undefined,
29114
29115     // interface method
29116     run : function(options){
29117
29118     },
29119
29120     // interface method
29121     success : function(response){
29122
29123     },
29124
29125     // interface method
29126     handleResponse : function(response){
29127
29128     },
29129
29130     // default connection failure
29131     failure : function(response){
29132         
29133         this.response = response;
29134         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29135         this.form.afterAction(this, false);
29136     },
29137
29138     processResponse : function(response){
29139         this.response = response;
29140         if(!response.responseText){
29141             return true;
29142         }
29143         this.result = this.handleResponse(response);
29144         return this.result;
29145     },
29146
29147     // utility functions used internally
29148     getUrl : function(appendParams){
29149         var url = this.options.url || this.form.url || this.form.el.dom.action;
29150         if(appendParams){
29151             var p = this.getParams();
29152             if(p){
29153                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29154             }
29155         }
29156         return url;
29157     },
29158
29159     getMethod : function(){
29160         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29161     },
29162
29163     getParams : function(){
29164         var bp = this.form.baseParams;
29165         var p = this.options.params;
29166         if(p){
29167             if(typeof p == "object"){
29168                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29169             }else if(typeof p == 'string' && bp){
29170                 p += '&' + Roo.urlEncode(bp);
29171             }
29172         }else if(bp){
29173             p = Roo.urlEncode(bp);
29174         }
29175         return p;
29176     },
29177
29178     createCallback : function(){
29179         return {
29180             success: this.success,
29181             failure: this.failure,
29182             scope: this,
29183             timeout: (this.form.timeout*1000),
29184             upload: this.form.fileUpload ? this.success : undefined
29185         };
29186     }
29187 };
29188
29189 Roo.form.Action.Submit = function(form, options){
29190     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29191 };
29192
29193 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29194     type : 'submit',
29195
29196     haveProgress : false,
29197     uploadComplete : false,
29198     
29199     // uploadProgress indicator.
29200     uploadProgress : function()
29201     {
29202         if (!this.form.progressUrl) {
29203             return;
29204         }
29205         
29206         if (!this.haveProgress) {
29207             Roo.MessageBox.progress("Uploading", "Uploading");
29208         }
29209         if (this.uploadComplete) {
29210            Roo.MessageBox.hide();
29211            return;
29212         }
29213         
29214         this.haveProgress = true;
29215    
29216         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29217         
29218         var c = new Roo.data.Connection();
29219         c.request({
29220             url : this.form.progressUrl,
29221             params: {
29222                 id : uid
29223             },
29224             method: 'GET',
29225             success : function(req){
29226                //console.log(data);
29227                 var rdata = false;
29228                 var edata;
29229                 try  {
29230                    rdata = Roo.decode(req.responseText)
29231                 } catch (e) {
29232                     Roo.log("Invalid data from server..");
29233                     Roo.log(edata);
29234                     return;
29235                 }
29236                 if (!rdata || !rdata.success) {
29237                     Roo.log(rdata);
29238                     Roo.MessageBox.alert(Roo.encode(rdata));
29239                     return;
29240                 }
29241                 var data = rdata.data;
29242                 
29243                 if (this.uploadComplete) {
29244                    Roo.MessageBox.hide();
29245                    return;
29246                 }
29247                    
29248                 if (data){
29249                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29250                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29251                     );
29252                 }
29253                 this.uploadProgress.defer(2000,this);
29254             },
29255        
29256             failure: function(data) {
29257                 Roo.log('progress url failed ');
29258                 Roo.log(data);
29259             },
29260             scope : this
29261         });
29262            
29263     },
29264     
29265     
29266     run : function()
29267     {
29268         // run get Values on the form, so it syncs any secondary forms.
29269         this.form.getValues();
29270         
29271         var o = this.options;
29272         var method = this.getMethod();
29273         var isPost = method == 'POST';
29274         if(o.clientValidation === false || this.form.isValid()){
29275             
29276             if (this.form.progressUrl) {
29277                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29278                     (new Date() * 1) + '' + Math.random());
29279                     
29280             } 
29281             
29282             
29283             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29284                 form:this.form.el.dom,
29285                 url:this.getUrl(!isPost),
29286                 method: method,
29287                 params:isPost ? this.getParams() : null,
29288                 isUpload: this.form.fileUpload
29289             }));
29290             
29291             this.uploadProgress();
29292
29293         }else if (o.clientValidation !== false){ // client validation failed
29294             this.failureType = Roo.form.Action.CLIENT_INVALID;
29295             this.form.afterAction(this, false);
29296         }
29297     },
29298
29299     success : function(response)
29300     {
29301         this.uploadComplete= true;
29302         if (this.haveProgress) {
29303             Roo.MessageBox.hide();
29304         }
29305         
29306         
29307         var result = this.processResponse(response);
29308         if(result === true || result.success){
29309             this.form.afterAction(this, true);
29310             return;
29311         }
29312         if(result.errors){
29313             this.form.markInvalid(result.errors);
29314             this.failureType = Roo.form.Action.SERVER_INVALID;
29315         }
29316         this.form.afterAction(this, false);
29317     },
29318     failure : function(response)
29319     {
29320         this.uploadComplete= true;
29321         if (this.haveProgress) {
29322             Roo.MessageBox.hide();
29323         }
29324         
29325         this.response = response;
29326         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29327         this.form.afterAction(this, false);
29328     },
29329     
29330     handleResponse : function(response){
29331         if(this.form.errorReader){
29332             var rs = this.form.errorReader.read(response);
29333             var errors = [];
29334             if(rs.records){
29335                 for(var i = 0, len = rs.records.length; i < len; i++) {
29336                     var r = rs.records[i];
29337                     errors[i] = r.data;
29338                 }
29339             }
29340             if(errors.length < 1){
29341                 errors = null;
29342             }
29343             return {
29344                 success : rs.success,
29345                 errors : errors
29346             };
29347         }
29348         var ret = false;
29349         try {
29350             ret = Roo.decode(response.responseText);
29351         } catch (e) {
29352             ret = {
29353                 success: false,
29354                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29355                 errors : []
29356             };
29357         }
29358         return ret;
29359         
29360     }
29361 });
29362
29363
29364 Roo.form.Action.Load = function(form, options){
29365     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29366     this.reader = this.form.reader;
29367 };
29368
29369 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29370     type : 'load',
29371
29372     run : function(){
29373         
29374         Roo.Ajax.request(Roo.apply(
29375                 this.createCallback(), {
29376                     method:this.getMethod(),
29377                     url:this.getUrl(false),
29378                     params:this.getParams()
29379         }));
29380     },
29381
29382     success : function(response){
29383         
29384         var result = this.processResponse(response);
29385         if(result === true || !result.success || !result.data){
29386             this.failureType = Roo.form.Action.LOAD_FAILURE;
29387             this.form.afterAction(this, false);
29388             return;
29389         }
29390         this.form.clearInvalid();
29391         this.form.setValues(result.data);
29392         this.form.afterAction(this, true);
29393     },
29394
29395     handleResponse : function(response){
29396         if(this.form.reader){
29397             var rs = this.form.reader.read(response);
29398             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29399             return {
29400                 success : rs.success,
29401                 data : data
29402             };
29403         }
29404         return Roo.decode(response.responseText);
29405     }
29406 });
29407
29408 Roo.form.Action.ACTION_TYPES = {
29409     'load' : Roo.form.Action.Load,
29410     'submit' : Roo.form.Action.Submit
29411 };/*
29412  * Based on:
29413  * Ext JS Library 1.1.1
29414  * Copyright(c) 2006-2007, Ext JS, LLC.
29415  *
29416  * Originally Released Under LGPL - original licence link has changed is not relivant.
29417  *
29418  * Fork - LGPL
29419  * <script type="text/javascript">
29420  */
29421  
29422 /**
29423  * @class Roo.form.Layout
29424  * @extends Roo.Component
29425  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29426  * @constructor
29427  * @param {Object} config Configuration options
29428  */
29429 Roo.form.Layout = function(config){
29430     var xitems = [];
29431     if (config.items) {
29432         xitems = config.items;
29433         delete config.items;
29434     }
29435     Roo.form.Layout.superclass.constructor.call(this, config);
29436     this.stack = [];
29437     Roo.each(xitems, this.addxtype, this);
29438      
29439 };
29440
29441 Roo.extend(Roo.form.Layout, Roo.Component, {
29442     /**
29443      * @cfg {String/Object} autoCreate
29444      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29445      */
29446     /**
29447      * @cfg {String/Object/Function} style
29448      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29449      * a function which returns such a specification.
29450      */
29451     /**
29452      * @cfg {String} labelAlign
29453      * Valid values are "left," "top" and "right" (defaults to "left")
29454      */
29455     /**
29456      * @cfg {Number} labelWidth
29457      * Fixed width in pixels of all field labels (defaults to undefined)
29458      */
29459     /**
29460      * @cfg {Boolean} clear
29461      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29462      */
29463     clear : true,
29464     /**
29465      * @cfg {String} labelSeparator
29466      * The separator to use after field labels (defaults to ':')
29467      */
29468     labelSeparator : ':',
29469     /**
29470      * @cfg {Boolean} hideLabels
29471      * True to suppress the display of field labels in this layout (defaults to false)
29472      */
29473     hideLabels : false,
29474
29475     // private
29476     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29477     
29478     isLayout : true,
29479     
29480     // private
29481     onRender : function(ct, position){
29482         if(this.el){ // from markup
29483             this.el = Roo.get(this.el);
29484         }else {  // generate
29485             var cfg = this.getAutoCreate();
29486             this.el = ct.createChild(cfg, position);
29487         }
29488         if(this.style){
29489             this.el.applyStyles(this.style);
29490         }
29491         if(this.labelAlign){
29492             this.el.addClass('x-form-label-'+this.labelAlign);
29493         }
29494         if(this.hideLabels){
29495             this.labelStyle = "display:none";
29496             this.elementStyle = "padding-left:0;";
29497         }else{
29498             if(typeof this.labelWidth == 'number'){
29499                 this.labelStyle = "width:"+this.labelWidth+"px;";
29500                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29501             }
29502             if(this.labelAlign == 'top'){
29503                 this.labelStyle = "width:auto;";
29504                 this.elementStyle = "padding-left:0;";
29505             }
29506         }
29507         var stack = this.stack;
29508         var slen = stack.length;
29509         if(slen > 0){
29510             if(!this.fieldTpl){
29511                 var t = new Roo.Template(
29512                     '<div class="x-form-item {5}">',
29513                         '<label for="{0}" style="{2}">{1}{4}</label>',
29514                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29515                         '</div>',
29516                     '</div><div class="x-form-clear-left"></div>'
29517                 );
29518                 t.disableFormats = true;
29519                 t.compile();
29520                 Roo.form.Layout.prototype.fieldTpl = t;
29521             }
29522             for(var i = 0; i < slen; i++) {
29523                 if(stack[i].isFormField){
29524                     this.renderField(stack[i]);
29525                 }else{
29526                     this.renderComponent(stack[i]);
29527                 }
29528             }
29529         }
29530         if(this.clear){
29531             this.el.createChild({cls:'x-form-clear'});
29532         }
29533     },
29534
29535     // private
29536     renderField : function(f){
29537         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29538                f.id, //0
29539                f.fieldLabel, //1
29540                f.labelStyle||this.labelStyle||'', //2
29541                this.elementStyle||'', //3
29542                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29543                f.itemCls||this.itemCls||''  //5
29544        ], true).getPrevSibling());
29545     },
29546
29547     // private
29548     renderComponent : function(c){
29549         c.render(c.isLayout ? this.el : this.el.createChild());    
29550     },
29551     /**
29552      * Adds a object form elements (using the xtype property as the factory method.)
29553      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29554      * @param {Object} config 
29555      */
29556     addxtype : function(o)
29557     {
29558         // create the lement.
29559         o.form = this.form;
29560         var fe = Roo.factory(o, Roo.form);
29561         this.form.allItems.push(fe);
29562         this.stack.push(fe);
29563         
29564         if (fe.isFormField) {
29565             this.form.items.add(fe);
29566         }
29567          
29568         return fe;
29569     }
29570 });
29571
29572 /**
29573  * @class Roo.form.Column
29574  * @extends Roo.form.Layout
29575  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29576  * @constructor
29577  * @param {Object} config Configuration options
29578  */
29579 Roo.form.Column = function(config){
29580     Roo.form.Column.superclass.constructor.call(this, config);
29581 };
29582
29583 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29584     /**
29585      * @cfg {Number/String} width
29586      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29587      */
29588     /**
29589      * @cfg {String/Object} autoCreate
29590      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29591      */
29592
29593     // private
29594     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29595
29596     // private
29597     onRender : function(ct, position){
29598         Roo.form.Column.superclass.onRender.call(this, ct, position);
29599         if(this.width){
29600             this.el.setWidth(this.width);
29601         }
29602     }
29603 });
29604
29605
29606 /**
29607  * @class Roo.form.Row
29608  * @extends Roo.form.Layout
29609  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29610  * @constructor
29611  * @param {Object} config Configuration options
29612  */
29613
29614  
29615 Roo.form.Row = function(config){
29616     Roo.form.Row.superclass.constructor.call(this, config);
29617 };
29618  
29619 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29620       /**
29621      * @cfg {Number/String} width
29622      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29623      */
29624     /**
29625      * @cfg {Number/String} height
29626      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29627      */
29628     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29629     
29630     padWidth : 20,
29631     // private
29632     onRender : function(ct, position){
29633         //console.log('row render');
29634         if(!this.rowTpl){
29635             var t = new Roo.Template(
29636                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29637                     '<label for="{0}" style="{2}">{1}{4}</label>',
29638                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29639                     '</div>',
29640                 '</div>'
29641             );
29642             t.disableFormats = true;
29643             t.compile();
29644             Roo.form.Layout.prototype.rowTpl = t;
29645         }
29646         this.fieldTpl = this.rowTpl;
29647         
29648         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29649         var labelWidth = 100;
29650         
29651         if ((this.labelAlign != 'top')) {
29652             if (typeof this.labelWidth == 'number') {
29653                 labelWidth = this.labelWidth
29654             }
29655             this.padWidth =  20 + labelWidth;
29656             
29657         }
29658         
29659         Roo.form.Column.superclass.onRender.call(this, ct, position);
29660         if(this.width){
29661             this.el.setWidth(this.width);
29662         }
29663         if(this.height){
29664             this.el.setHeight(this.height);
29665         }
29666     },
29667     
29668     // private
29669     renderField : function(f){
29670         f.fieldEl = this.fieldTpl.append(this.el, [
29671                f.id, f.fieldLabel,
29672                f.labelStyle||this.labelStyle||'',
29673                this.elementStyle||'',
29674                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29675                f.itemCls||this.itemCls||'',
29676                f.width ? f.width + this.padWidth : 160 + this.padWidth
29677        ],true);
29678     }
29679 });
29680  
29681
29682 /**
29683  * @class Roo.form.FieldSet
29684  * @extends Roo.form.Layout
29685  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29686  * @constructor
29687  * @param {Object} config Configuration options
29688  */
29689 Roo.form.FieldSet = function(config){
29690     Roo.form.FieldSet.superclass.constructor.call(this, config);
29691 };
29692
29693 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29694     /**
29695      * @cfg {String} legend
29696      * The text to display as the legend for the FieldSet (defaults to '')
29697      */
29698     /**
29699      * @cfg {String/Object} autoCreate
29700      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29701      */
29702
29703     // private
29704     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29705
29706     // private
29707     onRender : function(ct, position){
29708         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29709         if(this.legend){
29710             this.setLegend(this.legend);
29711         }
29712     },
29713
29714     // private
29715     setLegend : function(text){
29716         if(this.rendered){
29717             this.el.child('legend').update(text);
29718         }
29719     }
29720 });/*
29721  * Based on:
29722  * Ext JS Library 1.1.1
29723  * Copyright(c) 2006-2007, Ext JS, LLC.
29724  *
29725  * Originally Released Under LGPL - original licence link has changed is not relivant.
29726  *
29727  * Fork - LGPL
29728  * <script type="text/javascript">
29729  */
29730 /**
29731  * @class Roo.form.VTypes
29732  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29733  * @singleton
29734  */
29735 Roo.form.VTypes = function(){
29736     // closure these in so they are only created once.
29737     var alpha = /^[a-zA-Z_]+$/;
29738     var alphanum = /^[a-zA-Z0-9_]+$/;
29739     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29740     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29741
29742     // All these messages and functions are configurable
29743     return {
29744         /**
29745          * The function used to validate email addresses
29746          * @param {String} value The email address
29747          */
29748         'email' : function(v){
29749             return email.test(v);
29750         },
29751         /**
29752          * The error text to display when the email validation function returns false
29753          * @type String
29754          */
29755         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29756         /**
29757          * The keystroke filter mask to be applied on email input
29758          * @type RegExp
29759          */
29760         'emailMask' : /[a-z0-9_\.\-@]/i,
29761
29762         /**
29763          * The function used to validate URLs
29764          * @param {String} value The URL
29765          */
29766         'url' : function(v){
29767             return url.test(v);
29768         },
29769         /**
29770          * The error text to display when the url validation function returns false
29771          * @type String
29772          */
29773         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29774         
29775         /**
29776          * The function used to validate alpha values
29777          * @param {String} value The value
29778          */
29779         'alpha' : function(v){
29780             return alpha.test(v);
29781         },
29782         /**
29783          * The error text to display when the alpha validation function returns false
29784          * @type String
29785          */
29786         'alphaText' : 'This field should only contain letters and _',
29787         /**
29788          * The keystroke filter mask to be applied on alpha input
29789          * @type RegExp
29790          */
29791         'alphaMask' : /[a-z_]/i,
29792
29793         /**
29794          * The function used to validate alphanumeric values
29795          * @param {String} value The value
29796          */
29797         'alphanum' : function(v){
29798             return alphanum.test(v);
29799         },
29800         /**
29801          * The error text to display when the alphanumeric validation function returns false
29802          * @type String
29803          */
29804         'alphanumText' : 'This field should only contain letters, numbers and _',
29805         /**
29806          * The keystroke filter mask to be applied on alphanumeric input
29807          * @type RegExp
29808          */
29809         'alphanumMask' : /[a-z0-9_]/i
29810     };
29811 }();//<script type="text/javascript">
29812
29813 /**
29814  * @class Roo.form.FCKeditor
29815  * @extends Roo.form.TextArea
29816  * Wrapper around the FCKEditor http://www.fckeditor.net
29817  * @constructor
29818  * Creates a new FCKeditor
29819  * @param {Object} config Configuration options
29820  */
29821 Roo.form.FCKeditor = function(config){
29822     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29823     this.addEvents({
29824          /**
29825          * @event editorinit
29826          * Fired when the editor is initialized - you can add extra handlers here..
29827          * @param {FCKeditor} this
29828          * @param {Object} the FCK object.
29829          */
29830         editorinit : true
29831     });
29832     
29833     
29834 };
29835 Roo.form.FCKeditor.editors = { };
29836 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29837 {
29838     //defaultAutoCreate : {
29839     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29840     //},
29841     // private
29842     /**
29843      * @cfg {Object} fck options - see fck manual for details.
29844      */
29845     fckconfig : false,
29846     
29847     /**
29848      * @cfg {Object} fck toolbar set (Basic or Default)
29849      */
29850     toolbarSet : 'Basic',
29851     /**
29852      * @cfg {Object} fck BasePath
29853      */ 
29854     basePath : '/fckeditor/',
29855     
29856     
29857     frame : false,
29858     
29859     value : '',
29860     
29861    
29862     onRender : function(ct, position)
29863     {
29864         if(!this.el){
29865             this.defaultAutoCreate = {
29866                 tag: "textarea",
29867                 style:"width:300px;height:60px;",
29868                 autocomplete: "off"
29869             };
29870         }
29871         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29872         /*
29873         if(this.grow){
29874             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29875             if(this.preventScrollbars){
29876                 this.el.setStyle("overflow", "hidden");
29877             }
29878             this.el.setHeight(this.growMin);
29879         }
29880         */
29881         //console.log('onrender' + this.getId() );
29882         Roo.form.FCKeditor.editors[this.getId()] = this;
29883          
29884
29885         this.replaceTextarea() ;
29886         
29887     },
29888     
29889     getEditor : function() {
29890         return this.fckEditor;
29891     },
29892     /**
29893      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29894      * @param {Mixed} value The value to set
29895      */
29896     
29897     
29898     setValue : function(value)
29899     {
29900         //console.log('setValue: ' + value);
29901         
29902         if(typeof(value) == 'undefined') { // not sure why this is happending...
29903             return;
29904         }
29905         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29906         
29907         //if(!this.el || !this.getEditor()) {
29908         //    this.value = value;
29909             //this.setValue.defer(100,this,[value]);    
29910         //    return;
29911         //} 
29912         
29913         if(!this.getEditor()) {
29914             return;
29915         }
29916         
29917         this.getEditor().SetData(value);
29918         
29919         //
29920
29921     },
29922
29923     /**
29924      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29925      * @return {Mixed} value The field value
29926      */
29927     getValue : function()
29928     {
29929         
29930         if (this.frame && this.frame.dom.style.display == 'none') {
29931             return Roo.form.FCKeditor.superclass.getValue.call(this);
29932         }
29933         
29934         if(!this.el || !this.getEditor()) {
29935            
29936            // this.getValue.defer(100,this); 
29937             return this.value;
29938         }
29939        
29940         
29941         var value=this.getEditor().GetData();
29942         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29943         return Roo.form.FCKeditor.superclass.getValue.call(this);
29944         
29945
29946     },
29947
29948     /**
29949      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29950      * @return {Mixed} value The field value
29951      */
29952     getRawValue : function()
29953     {
29954         if (this.frame && this.frame.dom.style.display == 'none') {
29955             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29956         }
29957         
29958         if(!this.el || !this.getEditor()) {
29959             //this.getRawValue.defer(100,this); 
29960             return this.value;
29961             return;
29962         }
29963         
29964         
29965         
29966         var value=this.getEditor().GetData();
29967         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29968         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29969          
29970     },
29971     
29972     setSize : function(w,h) {
29973         
29974         
29975         
29976         //if (this.frame && this.frame.dom.style.display == 'none') {
29977         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29978         //    return;
29979         //}
29980         //if(!this.el || !this.getEditor()) {
29981         //    this.setSize.defer(100,this, [w,h]); 
29982         //    return;
29983         //}
29984         
29985         
29986         
29987         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29988         
29989         this.frame.dom.setAttribute('width', w);
29990         this.frame.dom.setAttribute('height', h);
29991         this.frame.setSize(w,h);
29992         
29993     },
29994     
29995     toggleSourceEdit : function(value) {
29996         
29997       
29998          
29999         this.el.dom.style.display = value ? '' : 'none';
30000         this.frame.dom.style.display = value ?  'none' : '';
30001         
30002     },
30003     
30004     
30005     focus: function(tag)
30006     {
30007         if (this.frame.dom.style.display == 'none') {
30008             return Roo.form.FCKeditor.superclass.focus.call(this);
30009         }
30010         if(!this.el || !this.getEditor()) {
30011             this.focus.defer(100,this, [tag]); 
30012             return;
30013         }
30014         
30015         
30016         
30017         
30018         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30019         this.getEditor().Focus();
30020         if (tgs.length) {
30021             if (!this.getEditor().Selection.GetSelection()) {
30022                 this.focus.defer(100,this, [tag]); 
30023                 return;
30024             }
30025             
30026             
30027             var r = this.getEditor().EditorDocument.createRange();
30028             r.setStart(tgs[0],0);
30029             r.setEnd(tgs[0],0);
30030             this.getEditor().Selection.GetSelection().removeAllRanges();
30031             this.getEditor().Selection.GetSelection().addRange(r);
30032             this.getEditor().Focus();
30033         }
30034         
30035     },
30036     
30037     
30038     
30039     replaceTextarea : function()
30040     {
30041         if ( document.getElementById( this.getId() + '___Frame' ) )
30042             return ;
30043         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30044         //{
30045             // We must check the elements firstly using the Id and then the name.
30046         var oTextarea = document.getElementById( this.getId() );
30047         
30048         var colElementsByName = document.getElementsByName( this.getId() ) ;
30049          
30050         oTextarea.style.display = 'none' ;
30051
30052         if ( oTextarea.tabIndex ) {            
30053             this.TabIndex = oTextarea.tabIndex ;
30054         }
30055         
30056         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30057         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30058         this.frame = Roo.get(this.getId() + '___Frame')
30059     },
30060     
30061     _getConfigHtml : function()
30062     {
30063         var sConfig = '' ;
30064
30065         for ( var o in this.fckconfig ) {
30066             sConfig += sConfig.length > 0  ? '&amp;' : '';
30067             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30068         }
30069
30070         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30071     },
30072     
30073     
30074     _getIFrameHtml : function()
30075     {
30076         var sFile = 'fckeditor.html' ;
30077         /* no idea what this is about..
30078         try
30079         {
30080             if ( (/fcksource=true/i).test( window.top.location.search ) )
30081                 sFile = 'fckeditor.original.html' ;
30082         }
30083         catch (e) { 
30084         */
30085
30086         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30087         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30088         
30089         
30090         var html = '<iframe id="' + this.getId() +
30091             '___Frame" src="' + sLink +
30092             '" width="' + this.width +
30093             '" height="' + this.height + '"' +
30094             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30095             ' frameborder="0" scrolling="no"></iframe>' ;
30096
30097         return html ;
30098     },
30099     
30100     _insertHtmlBefore : function( html, element )
30101     {
30102         if ( element.insertAdjacentHTML )       {
30103             // IE
30104             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30105         } else { // Gecko
30106             var oRange = document.createRange() ;
30107             oRange.setStartBefore( element ) ;
30108             var oFragment = oRange.createContextualFragment( html );
30109             element.parentNode.insertBefore( oFragment, element ) ;
30110         }
30111     }
30112     
30113     
30114   
30115     
30116     
30117     
30118     
30119
30120 });
30121
30122 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30123
30124 function FCKeditor_OnComplete(editorInstance){
30125     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30126     f.fckEditor = editorInstance;
30127     //console.log("loaded");
30128     f.fireEvent('editorinit', f, editorInstance);
30129
30130   
30131
30132  
30133
30134
30135
30136
30137
30138
30139
30140
30141
30142
30143
30144
30145
30146
30147
30148 //<script type="text/javascript">
30149 /**
30150  * @class Roo.form.GridField
30151  * @extends Roo.form.Field
30152  * Embed a grid (or editable grid into a form)
30153  * STATUS ALPHA
30154  * 
30155  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30156  * it needs 
30157  * xgrid.store = Roo.data.Store
30158  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30159  * xgrid.store.reader = Roo.data.JsonReader 
30160  * 
30161  * 
30162  * @constructor
30163  * Creates a new GridField
30164  * @param {Object} config Configuration options
30165  */
30166 Roo.form.GridField = function(config){
30167     Roo.form.GridField.superclass.constructor.call(this, config);
30168      
30169 };
30170
30171 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30172     /**
30173      * @cfg {Number} width  - used to restrict width of grid..
30174      */
30175     width : 100,
30176     /**
30177      * @cfg {Number} height - used to restrict height of grid..
30178      */
30179     height : 50,
30180      /**
30181      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30182          * 
30183          *}
30184      */
30185     xgrid : false, 
30186     /**
30187      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30188      * {tag: "input", type: "checkbox", autocomplete: "off"})
30189      */
30190    // defaultAutoCreate : { tag: 'div' },
30191     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30192     /**
30193      * @cfg {String} addTitle Text to include for adding a title.
30194      */
30195     addTitle : false,
30196     //
30197     onResize : function(){
30198         Roo.form.Field.superclass.onResize.apply(this, arguments);
30199     },
30200
30201     initEvents : function(){
30202         // Roo.form.Checkbox.superclass.initEvents.call(this);
30203         // has no events...
30204        
30205     },
30206
30207
30208     getResizeEl : function(){
30209         return this.wrap;
30210     },
30211
30212     getPositionEl : function(){
30213         return this.wrap;
30214     },
30215
30216     // private
30217     onRender : function(ct, position){
30218         
30219         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30220         var style = this.style;
30221         delete this.style;
30222         
30223         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30224         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30225         this.viewEl = this.wrap.createChild({ tag: 'div' });
30226         if (style) {
30227             this.viewEl.applyStyles(style);
30228         }
30229         if (this.width) {
30230             this.viewEl.setWidth(this.width);
30231         }
30232         if (this.height) {
30233             this.viewEl.setHeight(this.height);
30234         }
30235         //if(this.inputValue !== undefined){
30236         //this.setValue(this.value);
30237         
30238         
30239         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30240         
30241         
30242         this.grid.render();
30243         this.grid.getDataSource().on('remove', this.refreshValue, this);
30244         this.grid.getDataSource().on('update', this.refreshValue, this);
30245         this.grid.on('afteredit', this.refreshValue, this);
30246  
30247     },
30248      
30249     
30250     /**
30251      * Sets the value of the item. 
30252      * @param {String} either an object  or a string..
30253      */
30254     setValue : function(v){
30255         //this.value = v;
30256         v = v || []; // empty set..
30257         // this does not seem smart - it really only affects memoryproxy grids..
30258         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30259             var ds = this.grid.getDataSource();
30260             // assumes a json reader..
30261             var data = {}
30262             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30263             ds.loadData( data);
30264         }
30265         // clear selection so it does not get stale.
30266         if (this.grid.sm) { 
30267             this.grid.sm.clearSelections();
30268         }
30269         
30270         Roo.form.GridField.superclass.setValue.call(this, v);
30271         this.refreshValue();
30272         // should load data in the grid really....
30273     },
30274     
30275     // private
30276     refreshValue: function() {
30277          var val = [];
30278         this.grid.getDataSource().each(function(r) {
30279             val.push(r.data);
30280         });
30281         this.el.dom.value = Roo.encode(val);
30282     }
30283     
30284      
30285     
30286     
30287 });/*
30288  * Based on:
30289  * Ext JS Library 1.1.1
30290  * Copyright(c) 2006-2007, Ext JS, LLC.
30291  *
30292  * Originally Released Under LGPL - original licence link has changed is not relivant.
30293  *
30294  * Fork - LGPL
30295  * <script type="text/javascript">
30296  */
30297 /**
30298  * @class Roo.form.DisplayField
30299  * @extends Roo.form.Field
30300  * A generic Field to display non-editable data.
30301  * @constructor
30302  * Creates a new Display Field item.
30303  * @param {Object} config Configuration options
30304  */
30305 Roo.form.DisplayField = function(config){
30306     Roo.form.DisplayField.superclass.constructor.call(this, config);
30307     
30308 };
30309
30310 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30311     inputType:      'hidden',
30312     allowBlank:     true,
30313     readOnly:         true,
30314     
30315  
30316     /**
30317      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30318      */
30319     focusClass : undefined,
30320     /**
30321      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30322      */
30323     fieldClass: 'x-form-field',
30324     
30325      /**
30326      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30327      */
30328     valueRenderer: undefined,
30329     
30330     width: 100,
30331     /**
30332      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30333      * {tag: "input", type: "checkbox", autocomplete: "off"})
30334      */
30335      
30336  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30337
30338     onResize : function(){
30339         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30340         
30341     },
30342
30343     initEvents : function(){
30344         // Roo.form.Checkbox.superclass.initEvents.call(this);
30345         // has no events...
30346        
30347     },
30348
30349
30350     getResizeEl : function(){
30351         return this.wrap;
30352     },
30353
30354     getPositionEl : function(){
30355         return this.wrap;
30356     },
30357
30358     // private
30359     onRender : function(ct, position){
30360         
30361         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30362         //if(this.inputValue !== undefined){
30363         this.wrap = this.el.wrap();
30364         
30365         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30366         
30367         if (this.bodyStyle) {
30368             this.viewEl.applyStyles(this.bodyStyle);
30369         }
30370         //this.viewEl.setStyle('padding', '2px');
30371         
30372         this.setValue(this.value);
30373         
30374     },
30375 /*
30376     // private
30377     initValue : Roo.emptyFn,
30378
30379   */
30380
30381         // private
30382     onClick : function(){
30383         
30384     },
30385
30386     /**
30387      * Sets the checked state of the checkbox.
30388      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30389      */
30390     setValue : function(v){
30391         this.value = v;
30392         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30393         // this might be called before we have a dom element..
30394         if (!this.viewEl) {
30395             return;
30396         }
30397         this.viewEl.dom.innerHTML = html;
30398         Roo.form.DisplayField.superclass.setValue.call(this, v);
30399
30400     }
30401 });/*
30402  * 
30403  * Licence- LGPL
30404  * 
30405  */
30406
30407 /**
30408  * @class Roo.form.DayPicker
30409  * @extends Roo.form.Field
30410  * A Day picker show [M] [T] [W] ....
30411  * @constructor
30412  * Creates a new Day Picker
30413  * @param {Object} config Configuration options
30414  */
30415 Roo.form.DayPicker= function(config){
30416     Roo.form.DayPicker.superclass.constructor.call(this, config);
30417      
30418 };
30419
30420 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30421     /**
30422      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30423      */
30424     focusClass : undefined,
30425     /**
30426      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30427      */
30428     fieldClass: "x-form-field",
30429    
30430     /**
30431      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30432      * {tag: "input", type: "checkbox", autocomplete: "off"})
30433      */
30434     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30435     
30436    
30437     actionMode : 'viewEl', 
30438     //
30439     // private
30440  
30441     inputType : 'hidden',
30442     
30443      
30444     inputElement: false, // real input element?
30445     basedOn: false, // ????
30446     
30447     isFormField: true, // not sure where this is needed!!!!
30448
30449     onResize : function(){
30450         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30451         if(!this.boxLabel){
30452             this.el.alignTo(this.wrap, 'c-c');
30453         }
30454     },
30455
30456     initEvents : function(){
30457         Roo.form.Checkbox.superclass.initEvents.call(this);
30458         this.el.on("click", this.onClick,  this);
30459         this.el.on("change", this.onClick,  this);
30460     },
30461
30462
30463     getResizeEl : function(){
30464         return this.wrap;
30465     },
30466
30467     getPositionEl : function(){
30468         return this.wrap;
30469     },
30470
30471     
30472     // private
30473     onRender : function(ct, position){
30474         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30475        
30476         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30477         
30478         var r1 = '<table><tr>';
30479         var r2 = '<tr class="x-form-daypick-icons">';
30480         for (var i=0; i < 7; i++) {
30481             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30482             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30483         }
30484         
30485         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30486         viewEl.select('img').on('click', this.onClick, this);
30487         this.viewEl = viewEl;   
30488         
30489         
30490         // this will not work on Chrome!!!
30491         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30492         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30493         
30494         
30495           
30496
30497     },
30498
30499     // private
30500     initValue : Roo.emptyFn,
30501
30502     /**
30503      * Returns the checked state of the checkbox.
30504      * @return {Boolean} True if checked, else false
30505      */
30506     getValue : function(){
30507         return this.el.dom.value;
30508         
30509     },
30510
30511         // private
30512     onClick : function(e){ 
30513         //this.setChecked(!this.checked);
30514         Roo.get(e.target).toggleClass('x-menu-item-checked');
30515         this.refreshValue();
30516         //if(this.el.dom.checked != this.checked){
30517         //    this.setValue(this.el.dom.checked);
30518        // }
30519     },
30520     
30521     // private
30522     refreshValue : function()
30523     {
30524         var val = '';
30525         this.viewEl.select('img',true).each(function(e,i,n)  {
30526             val += e.is(".x-menu-item-checked") ? String(n) : '';
30527         });
30528         this.setValue(val, true);
30529     },
30530
30531     /**
30532      * Sets the checked state of the checkbox.
30533      * On is always based on a string comparison between inputValue and the param.
30534      * @param {Boolean/String} value - the value to set 
30535      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30536      */
30537     setValue : function(v,suppressEvent){
30538         if (!this.el.dom) {
30539             return;
30540         }
30541         var old = this.el.dom.value ;
30542         this.el.dom.value = v;
30543         if (suppressEvent) {
30544             return ;
30545         }
30546          
30547         // update display..
30548         this.viewEl.select('img',true).each(function(e,i,n)  {
30549             
30550             var on = e.is(".x-menu-item-checked");
30551             var newv = v.indexOf(String(n)) > -1;
30552             if (on != newv) {
30553                 e.toggleClass('x-menu-item-checked');
30554             }
30555             
30556         });
30557         
30558         
30559         this.fireEvent('change', this, v, old);
30560         
30561         
30562     },
30563    
30564     // handle setting of hidden value by some other method!!?!?
30565     setFromHidden: function()
30566     {
30567         if(!this.el){
30568             return;
30569         }
30570         //console.log("SET FROM HIDDEN");
30571         //alert('setFrom hidden');
30572         this.setValue(this.el.dom.value);
30573     },
30574     
30575     onDestroy : function()
30576     {
30577         if(this.viewEl){
30578             Roo.get(this.viewEl).remove();
30579         }
30580          
30581         Roo.form.DayPicker.superclass.onDestroy.call(this);
30582     }
30583
30584 });/*
30585  * RooJS Library 1.1.1
30586  * Copyright(c) 2008-2011  Alan Knowles
30587  *
30588  * License - LGPL
30589  */
30590  
30591
30592 /**
30593  * @class Roo.form.ComboCheck
30594  * @extends Roo.form.ComboBox
30595  * A combobox for multiple select items.
30596  *
30597  * FIXME - could do with a reset button..
30598  * 
30599  * @constructor
30600  * Create a new ComboCheck
30601  * @param {Object} config Configuration options
30602  */
30603 Roo.form.ComboCheck = function(config){
30604     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30605     // should verify some data...
30606     // like
30607     // hiddenName = required..
30608     // displayField = required
30609     // valudField == required
30610     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30611     var _t = this;
30612     Roo.each(req, function(e) {
30613         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30614             throw "Roo.form.ComboCheck : missing value for: " + e;
30615         }
30616     });
30617     
30618     
30619 };
30620
30621 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30622      
30623      
30624     editable : false,
30625      
30626     selectedClass: 'x-menu-item-checked', 
30627     
30628     // private
30629     onRender : function(ct, position){
30630         var _t = this;
30631         
30632         
30633         
30634         if(!this.tpl){
30635             var cls = 'x-combo-list';
30636
30637             
30638             this.tpl =  new Roo.Template({
30639                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30640                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30641                    '<span>{' + this.displayField + '}</span>' +
30642                     '</div>' 
30643                 
30644             });
30645         }
30646  
30647         
30648         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30649         this.view.singleSelect = false;
30650         this.view.multiSelect = true;
30651         this.view.toggleSelect = true;
30652         this.pageTb.add(new Roo.Toolbar.Fill(), {
30653             
30654             text: 'Done',
30655             handler: function()
30656             {
30657                 _t.collapse();
30658             }
30659         });
30660     },
30661     
30662     onViewOver : function(e, t){
30663         // do nothing...
30664         return;
30665         
30666     },
30667     
30668     onViewClick : function(doFocus,index){
30669         return;
30670         
30671     },
30672     select: function () {
30673         //Roo.log("SELECT CALLED");
30674     },
30675      
30676     selectByValue : function(xv, scrollIntoView){
30677         var ar = this.getValueArray();
30678         var sels = [];
30679         
30680         Roo.each(ar, function(v) {
30681             if(v === undefined || v === null){
30682                 return;
30683             }
30684             var r = this.findRecord(this.valueField, v);
30685             if(r){
30686                 sels.push(this.store.indexOf(r))
30687                 
30688             }
30689         },this);
30690         this.view.select(sels);
30691         return false;
30692     },
30693     
30694     
30695     
30696     onSelect : function(record, index){
30697        // Roo.log("onselect Called");
30698        // this is only called by the clear button now..
30699         this.view.clearSelections();
30700         this.setValue('[]');
30701         if (this.value != this.valueBefore) {
30702             this.fireEvent('change', this, this.value, this.valueBefore);
30703             this.valueBefore = this.value;
30704         }
30705     },
30706     getValueArray : function()
30707     {
30708         var ar = [] ;
30709         
30710         try {
30711             //Roo.log(this.value);
30712             if (typeof(this.value) == 'undefined') {
30713                 return [];
30714             }
30715             var ar = Roo.decode(this.value);
30716             return  ar instanceof Array ? ar : []; //?? valid?
30717             
30718         } catch(e) {
30719             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30720             return [];
30721         }
30722          
30723     },
30724     expand : function ()
30725     {
30726         
30727         Roo.form.ComboCheck.superclass.expand.call(this);
30728         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30729         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30730         
30731
30732     },
30733     
30734     collapse : function(){
30735         Roo.form.ComboCheck.superclass.collapse.call(this);
30736         var sl = this.view.getSelectedIndexes();
30737         var st = this.store;
30738         var nv = [];
30739         var tv = [];
30740         var r;
30741         Roo.each(sl, function(i) {
30742             r = st.getAt(i);
30743             nv.push(r.get(this.valueField));
30744         },this);
30745         this.setValue(Roo.encode(nv));
30746         if (this.value != this.valueBefore) {
30747
30748             this.fireEvent('change', this, this.value, this.valueBefore);
30749             this.valueBefore = this.value;
30750         }
30751         
30752     },
30753     
30754     setValue : function(v){
30755         // Roo.log(v);
30756         this.value = v;
30757         
30758         var vals = this.getValueArray();
30759         var tv = [];
30760         Roo.each(vals, function(k) {
30761             var r = this.findRecord(this.valueField, k);
30762             if(r){
30763                 tv.push(r.data[this.displayField]);
30764             }else if(this.valueNotFoundText !== undefined){
30765                 tv.push( this.valueNotFoundText );
30766             }
30767         },this);
30768        // Roo.log(tv);
30769         
30770         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30771         this.hiddenField.value = v;
30772         this.value = v;
30773     }
30774     
30775 });/*
30776  * Based on:
30777  * Ext JS Library 1.1.1
30778  * Copyright(c) 2006-2007, Ext JS, LLC.
30779  *
30780  * Originally Released Under LGPL - original licence link has changed is not relivant.
30781  *
30782  * Fork - LGPL
30783  * <script type="text/javascript">
30784  */
30785  
30786 /**
30787  * @class Roo.form.Signature
30788  * @extends Roo.form.Field
30789  * Signature field.  
30790  * @constructor
30791  * 
30792  * @param {Object} config Configuration options
30793  */
30794
30795 Roo.form.Signature = function(config){
30796     Roo.form.Signature.superclass.constructor.call(this, config);
30797     
30798     this.addEvents({// not in used??
30799          /**
30800          * @event confirm
30801          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30802              * @param {Roo.form.Signature} combo This combo box
30803              */
30804         'confirm' : true,
30805         /**
30806          * @event reset
30807          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30808              * @param {Roo.form.ComboBox} combo This combo box
30809              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30810              */
30811         'reset' : true
30812     });
30813 };
30814
30815 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30816     /**
30817      * @cfg {Object} labels Label to use when rendering a form.
30818      * defaults to 
30819      * labels : { 
30820      *      clear : "Clear",
30821      *      confirm : "Confirm"
30822      *  }
30823      */
30824     labels : { 
30825         clear : "Clear",
30826         confirm : "Confirm"
30827     },
30828     /**
30829      * @cfg {Number} width The signature panel width (defaults to 300)
30830      */
30831     width: 300,
30832     /**
30833      * @cfg {Number} height The signature panel height (defaults to 100)
30834      */
30835     height : 100,
30836     /**
30837      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30838      */
30839     allowBlank : false,
30840     
30841     //private
30842     // {Object} signPanel The signature SVG panel element (defaults to {})
30843     signPanel : {},
30844     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30845     isMouseDown : false,
30846     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30847     isConfirmed : false,
30848     // {String} signatureTmp SVG mapping string (defaults to empty string)
30849     signatureTmp : '',
30850     
30851     
30852     defaultAutoCreate : { // modified by initCompnoent..
30853         tag: "input",
30854         type:"hidden"
30855     },
30856
30857     // private
30858     onRender : function(ct, position){
30859         
30860         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30861         
30862         this.wrap = this.el.wrap({
30863             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30864         });
30865         
30866         this.createToolbar(this);
30867         this.signPanel = this.wrap.createChild({
30868                 tag: 'div',
30869                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30870             }, this.el
30871         );
30872             
30873         this.svgID = Roo.id();
30874         this.svgEl = this.signPanel.createChild({
30875               xmlns : 'http://www.w3.org/2000/svg',
30876               tag : 'svg',
30877               id : this.svgID + "-svg",
30878               width: this.width,
30879               height: this.height,
30880               viewBox: '0 0 '+this.width+' '+this.height,
30881               cn : [
30882                 {
30883                     tag: "rect",
30884                     id: this.svgID + "-svg-r",
30885                     width: this.width,
30886                     height: this.height,
30887                     fill: "#ffa"
30888                 },
30889                 {
30890                     tag: "line",
30891                     id: this.svgID + "-svg-l",
30892                     x1: "0", // start
30893                     y1: (this.height*0.8), // start set the line in 80% of height
30894                     x2: this.width, // end
30895                     y2: (this.height*0.8), // end set the line in 80% of height
30896                     'stroke': "#666",
30897                     'stroke-width': "1",
30898                     'stroke-dasharray': "3",
30899                     'shape-rendering': "crispEdges",
30900                     'pointer-events': "none"
30901                 },
30902                 {
30903                     tag: "path",
30904                     id: this.svgID + "-svg-p",
30905                     'stroke': "navy",
30906                     'stroke-width': "3",
30907                     'fill': "none",
30908                     'pointer-events': 'none'
30909                 }
30910               ]
30911         });
30912         this.createSVG();
30913         this.svgBox = this.svgEl.dom.getScreenCTM();
30914     },
30915     createSVG : function(){ 
30916         var svg = this.signPanel;
30917         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30918         var t = this;
30919
30920         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30921         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30922         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30923         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30924         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30925         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30926         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30927         
30928     },
30929     isTouchEvent : function(e){
30930         return e.type.match(/^touch/);
30931     },
30932     getCoords : function (e) {
30933         var pt    = this.svgEl.dom.createSVGPoint();
30934         pt.x = e.clientX; 
30935         pt.y = e.clientY;
30936         if (this.isTouchEvent(e)) {
30937             pt.x =  e.targetTouches[0].clientX 
30938             pt.y = e.targetTouches[0].clientY;
30939         }
30940         var a = this.svgEl.dom.getScreenCTM();
30941         var b = a.inverse();
30942         var mx = pt.matrixTransform(b);
30943         return mx.x + ',' + mx.y;
30944     },
30945     //mouse event headler 
30946     down : function (e) {
30947         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30948         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30949         
30950         this.isMouseDown = true;
30951         
30952         e.preventDefault();
30953     },
30954     move : function (e) {
30955         if (this.isMouseDown) {
30956             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30957             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30958         }
30959         
30960         e.preventDefault();
30961     },
30962     up : function (e) {
30963         this.isMouseDown = false;
30964         var sp = this.signatureTmp.split(' ');
30965         
30966         if(sp.length > 1){
30967             if(!sp[sp.length-2].match(/^L/)){
30968                 sp.pop();
30969                 sp.pop();
30970                 sp.push("");
30971                 this.signatureTmp = sp.join(" ");
30972             }
30973         }
30974         if(this.getValue() != this.signatureTmp){
30975             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30976             this.isConfirmed = false;
30977         }
30978         e.preventDefault();
30979     },
30980     
30981     /**
30982      * Protected method that will not generally be called directly. It
30983      * is called when the editor creates its toolbar. Override this method if you need to
30984      * add custom toolbar buttons.
30985      * @param {HtmlEditor} editor
30986      */
30987     createToolbar : function(editor){
30988          function btn(id, toggle, handler){
30989             var xid = fid + '-'+ id ;
30990             return {
30991                 id : xid,
30992                 cmd : id,
30993                 cls : 'x-btn-icon x-edit-'+id,
30994                 enableToggle:toggle !== false,
30995                 scope: editor, // was editor...
30996                 handler:handler||editor.relayBtnCmd,
30997                 clickEvent:'mousedown',
30998                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
30999                 tabIndex:-1
31000             };
31001         }
31002         
31003         
31004         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31005         this.tb = tb;
31006         this.tb.add(
31007            {
31008                 cls : ' x-signature-btn x-signature-'+id,
31009                 scope: editor, // was editor...
31010                 handler: this.reset,
31011                 clickEvent:'mousedown',
31012                 text: this.labels.clear
31013             },
31014             {
31015                  xtype : 'Fill',
31016                  xns: Roo.Toolbar
31017             }, 
31018             {
31019                 cls : '  x-signature-btn x-signature-'+id,
31020                 scope: editor, // was editor...
31021                 handler: this.confirmHandler,
31022                 clickEvent:'mousedown',
31023                 text: this.labels.confirm
31024             }
31025         );
31026     
31027     },
31028     //public
31029     /**
31030      * when user is clicked confirm then show this image.....
31031      * 
31032      * @return {String} Image Data URI
31033      */
31034     getImageDataURI : function(){
31035         var svg = this.svgEl.dom.parentNode.innerHTML;
31036         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31037         return src; 
31038     },
31039     /**
31040      * 
31041      * @return {Boolean} this.isConfirmed
31042      */
31043     getConfirmed : function(){
31044         return this.isConfirmed;
31045     },
31046     /**
31047      * 
31048      * @return {Number} this.width
31049      */
31050     getWidth : function(){
31051         return this.width;
31052     },
31053     /**
31054      * 
31055      * @return {Number} this.height
31056      */
31057     getHeight : function(){
31058         return this.height;
31059     },
31060     // private
31061     getSignature : function(){
31062         return this.signatureTmp;
31063     },
31064     // private
31065     reset : function(){
31066         this.signatureTmp = '';
31067         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31068         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31069         this.isConfirmed = false;
31070         Roo.form.Signature.superclass.reset.call(this);
31071     },
31072     setSignature : function(s){
31073         this.signatureTmp = s;
31074         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31075         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31076         this.setValue(s);
31077         this.isConfirmed = false;
31078         Roo.form.Signature.superclass.reset.call(this);
31079     }, 
31080     test : function(){
31081 //        Roo.log(this.signPanel.dom.contentWindow.up())
31082     },
31083     //private
31084     setConfirmed : function(){
31085         
31086         
31087         
31088 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31089     },
31090     // private
31091     confirmHandler : function(){
31092         if(!this.getSignature()){
31093             return;
31094         }
31095         
31096         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31097         this.setValue(this.getSignature());
31098         this.isConfirmed = true;
31099         
31100         this.fireEvent('confirm', this);
31101     },
31102     // private
31103     // Subclasses should provide the validation implementation by overriding this
31104     validateValue : function(value){
31105         if(this.allowBlank){
31106             return true;
31107         }
31108         
31109         if(this.isConfirmed){
31110             return true;
31111         }
31112         return false;
31113     }
31114 });/*
31115  * Based on:
31116  * Ext JS Library 1.1.1
31117  * Copyright(c) 2006-2007, Ext JS, LLC.
31118  *
31119  * Originally Released Under LGPL - original licence link has changed is not relivant.
31120  *
31121  * Fork - LGPL
31122  * <script type="text/javascript">
31123  */
31124  
31125
31126 /**
31127  * @class Roo.form.ComboBox
31128  * @extends Roo.form.TriggerField
31129  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31130  * @constructor
31131  * Create a new ComboBox.
31132  * @param {Object} config Configuration options
31133  */
31134 Roo.form.Select = function(config){
31135     Roo.form.Select.superclass.constructor.call(this, config);
31136      
31137 };
31138
31139 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31140     /**
31141      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31142      */
31143     /**
31144      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31145      * rendering into an Roo.Editor, defaults to false)
31146      */
31147     /**
31148      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31149      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31150      */
31151     /**
31152      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31153      */
31154     /**
31155      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31156      * the dropdown list (defaults to undefined, with no header element)
31157      */
31158
31159      /**
31160      * @cfg {String/Roo.Template} tpl The template to use to render the output
31161      */
31162      
31163     // private
31164     defaultAutoCreate : {tag: "select"  },
31165     /**
31166      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31167      */
31168     listWidth: undefined,
31169     /**
31170      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31171      * mode = 'remote' or 'text' if mode = 'local')
31172      */
31173     displayField: undefined,
31174     /**
31175      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31176      * mode = 'remote' or 'value' if mode = 'local'). 
31177      * Note: use of a valueField requires the user make a selection
31178      * in order for a value to be mapped.
31179      */
31180     valueField: undefined,
31181     
31182     
31183     /**
31184      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31185      * field's data value (defaults to the underlying DOM element's name)
31186      */
31187     hiddenName: undefined,
31188     /**
31189      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31190      */
31191     listClass: '',
31192     /**
31193      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31194      */
31195     selectedClass: 'x-combo-selected',
31196     /**
31197      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31198      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31199      * which displays a downward arrow icon).
31200      */
31201     triggerClass : 'x-form-arrow-trigger',
31202     /**
31203      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31204      */
31205     shadow:'sides',
31206     /**
31207      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31208      * anchor positions (defaults to 'tl-bl')
31209      */
31210     listAlign: 'tl-bl?',
31211     /**
31212      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31213      */
31214     maxHeight: 300,
31215     /**
31216      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31217      * query specified by the allQuery config option (defaults to 'query')
31218      */
31219     triggerAction: 'query',
31220     /**
31221      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31222      * (defaults to 4, does not apply if editable = false)
31223      */
31224     minChars : 4,
31225     /**
31226      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31227      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31228      */
31229     typeAhead: false,
31230     /**
31231      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31232      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31233      */
31234     queryDelay: 500,
31235     /**
31236      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31237      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31238      */
31239     pageSize: 0,
31240     /**
31241      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31242      * when editable = true (defaults to false)
31243      */
31244     selectOnFocus:false,
31245     /**
31246      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31247      */
31248     queryParam: 'query',
31249     /**
31250      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31251      * when mode = 'remote' (defaults to 'Loading...')
31252      */
31253     loadingText: 'Loading...',
31254     /**
31255      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31256      */
31257     resizable: false,
31258     /**
31259      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31260      */
31261     handleHeight : 8,
31262     /**
31263      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31264      * traditional select (defaults to true)
31265      */
31266     editable: true,
31267     /**
31268      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31269      */
31270     allQuery: '',
31271     /**
31272      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31273      */
31274     mode: 'remote',
31275     /**
31276      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31277      * listWidth has a higher value)
31278      */
31279     minListWidth : 70,
31280     /**
31281      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31282      * allow the user to set arbitrary text into the field (defaults to false)
31283      */
31284     forceSelection:false,
31285     /**
31286      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31287      * if typeAhead = true (defaults to 250)
31288      */
31289     typeAheadDelay : 250,
31290     /**
31291      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31292      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31293      */
31294     valueNotFoundText : undefined,
31295     
31296     /**
31297      * @cfg {String} defaultValue The value displayed after loading the store.
31298      */
31299     defaultValue: '',
31300     
31301     /**
31302      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31303      */
31304     blockFocus : false,
31305     
31306     /**
31307      * @cfg {Boolean} disableClear Disable showing of clear button.
31308      */
31309     disableClear : false,
31310     /**
31311      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31312      */
31313     alwaysQuery : false,
31314     
31315     //private
31316     addicon : false,
31317     editicon: false,
31318     
31319     // element that contains real text value.. (when hidden is used..)
31320      
31321     // private
31322     onRender : function(ct, position){
31323         Roo.form.Field.prototype.onRender.call(this, ct, position);
31324         
31325         if(this.store){
31326             this.store.on('beforeload', this.onBeforeLoad, this);
31327             this.store.on('load', this.onLoad, this);
31328             this.store.on('loadexception', this.onLoadException, this);
31329             this.store.load({});
31330         }
31331         
31332         
31333         
31334     },
31335
31336     // private
31337     initEvents : function(){
31338         //Roo.form.ComboBox.superclass.initEvents.call(this);
31339  
31340     },
31341
31342     onDestroy : function(){
31343        
31344         if(this.store){
31345             this.store.un('beforeload', this.onBeforeLoad, this);
31346             this.store.un('load', this.onLoad, this);
31347             this.store.un('loadexception', this.onLoadException, this);
31348         }
31349         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31350     },
31351
31352     // private
31353     fireKey : function(e){
31354         if(e.isNavKeyPress() && !this.list.isVisible()){
31355             this.fireEvent("specialkey", this, e);
31356         }
31357     },
31358
31359     // private
31360     onResize: function(w, h){
31361         
31362         return; 
31363     
31364         
31365     },
31366
31367     /**
31368      * Allow or prevent the user from directly editing the field text.  If false is passed,
31369      * the user will only be able to select from the items defined in the dropdown list.  This method
31370      * is the runtime equivalent of setting the 'editable' config option at config time.
31371      * @param {Boolean} value True to allow the user to directly edit the field text
31372      */
31373     setEditable : function(value){
31374          
31375     },
31376
31377     // private
31378     onBeforeLoad : function(){
31379         
31380         Roo.log("Select before load");
31381         return;
31382     
31383         this.innerList.update(this.loadingText ?
31384                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31385         //this.restrictHeight();
31386         this.selectedIndex = -1;
31387     },
31388
31389     // private
31390     onLoad : function(){
31391
31392     
31393         var dom = this.el.dom;
31394         dom.innerHTML = '';
31395          var od = dom.ownerDocument;
31396          
31397         if (this.emptyText) {
31398             var op = od.createElement('option');
31399             op.setAttribute('value', '');
31400             op.innerHTML = String.format('{0}', this.emptyText);
31401             dom.appendChild(op);
31402         }
31403         if(this.store.getCount() > 0){
31404            
31405             var vf = this.valueField;
31406             var df = this.displayField;
31407             this.store.data.each(function(r) {
31408                 // which colmsn to use... testing - cdoe / title..
31409                 var op = od.createElement('option');
31410                 op.setAttribute('value', r.data[vf]);
31411                 op.innerHTML = String.format('{0}', r.data[df]);
31412                 dom.appendChild(op);
31413             });
31414             if (typeof(this.defaultValue != 'undefined')) {
31415                 this.setValue(this.defaultValue);
31416             }
31417             
31418              
31419         }else{
31420             //this.onEmptyResults();
31421         }
31422         //this.el.focus();
31423     },
31424     // private
31425     onLoadException : function()
31426     {
31427         dom.innerHTML = '';
31428             
31429         Roo.log("Select on load exception");
31430         return;
31431     
31432         this.collapse();
31433         Roo.log(this.store.reader.jsonData);
31434         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31435             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31436         }
31437         
31438         
31439     },
31440     // private
31441     onTypeAhead : function(){
31442          
31443     },
31444
31445     // private
31446     onSelect : function(record, index){
31447         Roo.log('on select?');
31448         return;
31449         if(this.fireEvent('beforeselect', this, record, index) !== false){
31450             this.setFromData(index > -1 ? record.data : false);
31451             this.collapse();
31452             this.fireEvent('select', this, record, index);
31453         }
31454     },
31455
31456     /**
31457      * Returns the currently selected field value or empty string if no value is set.
31458      * @return {String} value The selected value
31459      */
31460     getValue : function(){
31461         var dom = this.el.dom;
31462         this.value = dom.options[dom.selectedIndex].value;
31463         return this.value;
31464         
31465     },
31466
31467     /**
31468      * Clears any text/value currently set in the field
31469      */
31470     clearValue : function(){
31471         this.value = '';
31472         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31473         
31474     },
31475
31476     /**
31477      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31478      * will be displayed in the field.  If the value does not match the data value of an existing item,
31479      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31480      * Otherwise the field will be blank (although the value will still be set).
31481      * @param {String} value The value to match
31482      */
31483     setValue : function(v){
31484         var d = this.el.dom;
31485         for (var i =0; i < d.options.length;i++) {
31486             if (v == d.options[i].value) {
31487                 d.selectedIndex = i;
31488                 this.value = v;
31489                 return;
31490             }
31491         }
31492         this.clearValue();
31493     },
31494     /**
31495      * @property {Object} the last set data for the element
31496      */
31497     
31498     lastData : false,
31499     /**
31500      * Sets the value of the field based on a object which is related to the record format for the store.
31501      * @param {Object} value the value to set as. or false on reset?
31502      */
31503     setFromData : function(o){
31504         Roo.log('setfrom data?');
31505          
31506         
31507         
31508     },
31509     // private
31510     reset : function(){
31511         this.clearValue();
31512     },
31513     // private
31514     findRecord : function(prop, value){
31515         
31516         return false;
31517     
31518         var record;
31519         if(this.store.getCount() > 0){
31520             this.store.each(function(r){
31521                 if(r.data[prop] == value){
31522                     record = r;
31523                     return false;
31524                 }
31525                 return true;
31526             });
31527         }
31528         return record;
31529     },
31530     
31531     getName: function()
31532     {
31533         // returns hidden if it's set..
31534         if (!this.rendered) {return ''};
31535         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31536         
31537     },
31538      
31539
31540     
31541
31542     // private
31543     onEmptyResults : function(){
31544         Roo.log('empty results');
31545         //this.collapse();
31546     },
31547
31548     /**
31549      * Returns true if the dropdown list is expanded, else false.
31550      */
31551     isExpanded : function(){
31552         return false;
31553     },
31554
31555     /**
31556      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31557      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31558      * @param {String} value The data value of the item to select
31559      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31560      * selected item if it is not currently in view (defaults to true)
31561      * @return {Boolean} True if the value matched an item in the list, else false
31562      */
31563     selectByValue : function(v, scrollIntoView){
31564         Roo.log('select By Value');
31565         return false;
31566     
31567         if(v !== undefined && v !== null){
31568             var r = this.findRecord(this.valueField || this.displayField, v);
31569             if(r){
31570                 this.select(this.store.indexOf(r), scrollIntoView);
31571                 return true;
31572             }
31573         }
31574         return false;
31575     },
31576
31577     /**
31578      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31579      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31580      * @param {Number} index The zero-based index of the list item to select
31581      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31582      * selected item if it is not currently in view (defaults to true)
31583      */
31584     select : function(index, scrollIntoView){
31585         Roo.log('select ');
31586         return  ;
31587         
31588         this.selectedIndex = index;
31589         this.view.select(index);
31590         if(scrollIntoView !== false){
31591             var el = this.view.getNode(index);
31592             if(el){
31593                 this.innerList.scrollChildIntoView(el, false);
31594             }
31595         }
31596     },
31597
31598       
31599
31600     // private
31601     validateBlur : function(){
31602         
31603         return;
31604         
31605     },
31606
31607     // private
31608     initQuery : function(){
31609         this.doQuery(this.getRawValue());
31610     },
31611
31612     // private
31613     doForce : function(){
31614         if(this.el.dom.value.length > 0){
31615             this.el.dom.value =
31616                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31617              
31618         }
31619     },
31620
31621     /**
31622      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31623      * query allowing the query action to be canceled if needed.
31624      * @param {String} query The SQL query to execute
31625      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31626      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31627      * saved in the current store (defaults to false)
31628      */
31629     doQuery : function(q, forceAll){
31630         
31631         Roo.log('doQuery?');
31632         if(q === undefined || q === null){
31633             q = '';
31634         }
31635         var qe = {
31636             query: q,
31637             forceAll: forceAll,
31638             combo: this,
31639             cancel:false
31640         };
31641         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31642             return false;
31643         }
31644         q = qe.query;
31645         forceAll = qe.forceAll;
31646         if(forceAll === true || (q.length >= this.minChars)){
31647             if(this.lastQuery != q || this.alwaysQuery){
31648                 this.lastQuery = q;
31649                 if(this.mode == 'local'){
31650                     this.selectedIndex = -1;
31651                     if(forceAll){
31652                         this.store.clearFilter();
31653                     }else{
31654                         this.store.filter(this.displayField, q);
31655                     }
31656                     this.onLoad();
31657                 }else{
31658                     this.store.baseParams[this.queryParam] = q;
31659                     this.store.load({
31660                         params: this.getParams(q)
31661                     });
31662                     this.expand();
31663                 }
31664             }else{
31665                 this.selectedIndex = -1;
31666                 this.onLoad();   
31667             }
31668         }
31669     },
31670
31671     // private
31672     getParams : function(q){
31673         var p = {};
31674         //p[this.queryParam] = q;
31675         if(this.pageSize){
31676             p.start = 0;
31677             p.limit = this.pageSize;
31678         }
31679         return p;
31680     },
31681
31682     /**
31683      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31684      */
31685     collapse : function(){
31686         
31687     },
31688
31689     // private
31690     collapseIf : function(e){
31691         
31692     },
31693
31694     /**
31695      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31696      */
31697     expand : function(){
31698         
31699     } ,
31700
31701     // private
31702      
31703
31704     /** 
31705     * @cfg {Boolean} grow 
31706     * @hide 
31707     */
31708     /** 
31709     * @cfg {Number} growMin 
31710     * @hide 
31711     */
31712     /** 
31713     * @cfg {Number} growMax 
31714     * @hide 
31715     */
31716     /**
31717      * @hide
31718      * @method autoSize
31719      */
31720     
31721     setWidth : function()
31722     {
31723         
31724     },
31725     getResizeEl : function(){
31726         return this.el;
31727     }
31728 });//<script type="text/javasscript">
31729  
31730
31731 /**
31732  * @class Roo.DDView
31733  * A DnD enabled version of Roo.View.
31734  * @param {Element/String} container The Element in which to create the View.
31735  * @param {String} tpl The template string used to create the markup for each element of the View
31736  * @param {Object} config The configuration properties. These include all the config options of
31737  * {@link Roo.View} plus some specific to this class.<br>
31738  * <p>
31739  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31740  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31741  * <p>
31742  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31743 .x-view-drag-insert-above {
31744         border-top:1px dotted #3366cc;
31745 }
31746 .x-view-drag-insert-below {
31747         border-bottom:1px dotted #3366cc;
31748 }
31749 </code></pre>
31750  * 
31751  */
31752  
31753 Roo.DDView = function(container, tpl, config) {
31754     Roo.DDView.superclass.constructor.apply(this, arguments);
31755     this.getEl().setStyle("outline", "0px none");
31756     this.getEl().unselectable();
31757     if (this.dragGroup) {
31758                 this.setDraggable(this.dragGroup.split(","));
31759     }
31760     if (this.dropGroup) {
31761                 this.setDroppable(this.dropGroup.split(","));
31762     }
31763     if (this.deletable) {
31764         this.setDeletable();
31765     }
31766     this.isDirtyFlag = false;
31767         this.addEvents({
31768                 "drop" : true
31769         });
31770 };
31771
31772 Roo.extend(Roo.DDView, Roo.View, {
31773 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31774 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31775 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31776 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31777
31778         isFormField: true,
31779
31780         reset: Roo.emptyFn,
31781         
31782         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31783
31784         validate: function() {
31785                 return true;
31786         },
31787         
31788         destroy: function() {
31789                 this.purgeListeners();
31790                 this.getEl.removeAllListeners();
31791                 this.getEl().remove();
31792                 if (this.dragZone) {
31793                         if (this.dragZone.destroy) {
31794                                 this.dragZone.destroy();
31795                         }
31796                 }
31797                 if (this.dropZone) {
31798                         if (this.dropZone.destroy) {
31799                                 this.dropZone.destroy();
31800                         }
31801                 }
31802         },
31803
31804 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31805         getName: function() {
31806                 return this.name;
31807         },
31808
31809 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31810         setValue: function(v) {
31811                 if (!this.store) {
31812                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31813                 }
31814                 var data = {};
31815                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31816                 this.store.proxy = new Roo.data.MemoryProxy(data);
31817                 this.store.load();
31818         },
31819
31820 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31821         getValue: function() {
31822                 var result = '(';
31823                 this.store.each(function(rec) {
31824                         result += rec.id + ',';
31825                 });
31826                 return result.substr(0, result.length - 1) + ')';
31827         },
31828         
31829         getIds: function() {
31830                 var i = 0, result = new Array(this.store.getCount());
31831                 this.store.each(function(rec) {
31832                         result[i++] = rec.id;
31833                 });
31834                 return result;
31835         },
31836         
31837         isDirty: function() {
31838                 return this.isDirtyFlag;
31839         },
31840
31841 /**
31842  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31843  *      whole Element becomes the target, and this causes the drop gesture to append.
31844  */
31845     getTargetFromEvent : function(e) {
31846                 var target = e.getTarget();
31847                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31848                 target = target.parentNode;
31849                 }
31850                 if (!target) {
31851                         target = this.el.dom.lastChild || this.el.dom;
31852                 }
31853                 return target;
31854     },
31855
31856 /**
31857  *      Create the drag data which consists of an object which has the property "ddel" as
31858  *      the drag proxy element. 
31859  */
31860     getDragData : function(e) {
31861         var target = this.findItemFromChild(e.getTarget());
31862                 if(target) {
31863                         this.handleSelection(e);
31864                         var selNodes = this.getSelectedNodes();
31865             var dragData = {
31866                 source: this,
31867                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31868                 nodes: selNodes,
31869                 records: []
31870                         };
31871                         var selectedIndices = this.getSelectedIndexes();
31872                         for (var i = 0; i < selectedIndices.length; i++) {
31873                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31874                         }
31875                         if (selNodes.length == 1) {
31876                                 dragData.ddel = target.cloneNode(true); // the div element
31877                         } else {
31878                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31879                                 div.className = 'multi-proxy';
31880                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31881                                         div.appendChild(selNodes[i].cloneNode(true));
31882                                 }
31883                                 dragData.ddel = div;
31884                         }
31885             //console.log(dragData)
31886             //console.log(dragData.ddel.innerHTML)
31887                         return dragData;
31888                 }
31889         //console.log('nodragData')
31890                 return false;
31891     },
31892     
31893 /**     Specify to which ddGroup items in this DDView may be dragged. */
31894     setDraggable: function(ddGroup) {
31895         if (ddGroup instanceof Array) {
31896                 Roo.each(ddGroup, this.setDraggable, this);
31897                 return;
31898         }
31899         if (this.dragZone) {
31900                 this.dragZone.addToGroup(ddGroup);
31901         } else {
31902                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31903                                 containerScroll: true,
31904                                 ddGroup: ddGroup 
31905
31906                         });
31907 //                      Draggability implies selection. DragZone's mousedown selects the element.
31908                         if (!this.multiSelect) { this.singleSelect = true; }
31909
31910 //                      Wire the DragZone's handlers up to methods in *this*
31911                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31912                 }
31913     },
31914
31915 /**     Specify from which ddGroup this DDView accepts drops. */
31916     setDroppable: function(ddGroup) {
31917         if (ddGroup instanceof Array) {
31918                 Roo.each(ddGroup, this.setDroppable, this);
31919                 return;
31920         }
31921         if (this.dropZone) {
31922                 this.dropZone.addToGroup(ddGroup);
31923         } else {
31924                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31925                                 containerScroll: true,
31926                                 ddGroup: ddGroup
31927                         });
31928
31929 //                      Wire the DropZone's handlers up to methods in *this*
31930                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31931                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31932                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31933                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31934                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31935                 }
31936     },
31937
31938 /**     Decide whether to drop above or below a View node. */
31939     getDropPoint : function(e, n, dd){
31940         if (n == this.el.dom) { return "above"; }
31941                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31942                 var c = t + (b - t) / 2;
31943                 var y = Roo.lib.Event.getPageY(e);
31944                 if(y <= c) {
31945                         return "above";
31946                 }else{
31947                         return "below";
31948                 }
31949     },
31950
31951     onNodeEnter : function(n, dd, e, data){
31952                 return false;
31953     },
31954     
31955     onNodeOver : function(n, dd, e, data){
31956                 var pt = this.getDropPoint(e, n, dd);
31957                 // set the insert point style on the target node
31958                 var dragElClass = this.dropNotAllowed;
31959                 if (pt) {
31960                         var targetElClass;
31961                         if (pt == "above"){
31962                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31963                                 targetElClass = "x-view-drag-insert-above";
31964                         } else {
31965                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31966                                 targetElClass = "x-view-drag-insert-below";
31967                         }
31968                         if (this.lastInsertClass != targetElClass){
31969                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31970                                 this.lastInsertClass = targetElClass;
31971                         }
31972                 }
31973                 return dragElClass;
31974         },
31975
31976     onNodeOut : function(n, dd, e, data){
31977                 this.removeDropIndicators(n);
31978     },
31979
31980     onNodeDrop : function(n, dd, e, data){
31981         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31982                 return false;
31983         }
31984         var pt = this.getDropPoint(e, n, dd);
31985                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
31986                 if (pt == "below") { insertAt++; }
31987                 for (var i = 0; i < data.records.length; i++) {
31988                         var r = data.records[i];
31989                         var dup = this.store.getById(r.id);
31990                         if (dup && (dd != this.dragZone)) {
31991                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
31992                         } else {
31993                                 if (data.copy) {
31994                                         this.store.insert(insertAt++, r.copy());
31995                                 } else {
31996                                         data.source.isDirtyFlag = true;
31997                                         r.store.remove(r);
31998                                         this.store.insert(insertAt++, r);
31999                                 }
32000                                 this.isDirtyFlag = true;
32001                         }
32002                 }
32003                 this.dragZone.cachedTarget = null;
32004                 return true;
32005     },
32006
32007     removeDropIndicators : function(n){
32008                 if(n){
32009                         Roo.fly(n).removeClass([
32010                                 "x-view-drag-insert-above",
32011                                 "x-view-drag-insert-below"]);
32012                         this.lastInsertClass = "_noclass";
32013                 }
32014     },
32015
32016 /**
32017  *      Utility method. Add a delete option to the DDView's context menu.
32018  *      @param {String} imageUrl The URL of the "delete" icon image.
32019  */
32020         setDeletable: function(imageUrl) {
32021                 if (!this.singleSelect && !this.multiSelect) {
32022                         this.singleSelect = true;
32023                 }
32024                 var c = this.getContextMenu();
32025                 this.contextMenu.on("itemclick", function(item) {
32026                         switch (item.id) {
32027                                 case "delete":
32028                                         this.remove(this.getSelectedIndexes());
32029                                         break;
32030                         }
32031                 }, this);
32032                 this.contextMenu.add({
32033                         icon: imageUrl,
32034                         id: "delete",
32035                         text: 'Delete'
32036                 });
32037         },
32038         
32039 /**     Return the context menu for this DDView. */
32040         getContextMenu: function() {
32041                 if (!this.contextMenu) {
32042 //                      Create the View's context menu
32043                         this.contextMenu = new Roo.menu.Menu({
32044                                 id: this.id + "-contextmenu"
32045                         });
32046                         this.el.on("contextmenu", this.showContextMenu, this);
32047                 }
32048                 return this.contextMenu;
32049         },
32050         
32051         disableContextMenu: function() {
32052                 if (this.contextMenu) {
32053                         this.el.un("contextmenu", this.showContextMenu, this);
32054                 }
32055         },
32056
32057         showContextMenu: function(e, item) {
32058         item = this.findItemFromChild(e.getTarget());
32059                 if (item) {
32060                         e.stopEvent();
32061                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32062                         this.contextMenu.showAt(e.getXY());
32063             }
32064     },
32065
32066 /**
32067  *      Remove {@link Roo.data.Record}s at the specified indices.
32068  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32069  */
32070     remove: function(selectedIndices) {
32071                 selectedIndices = [].concat(selectedIndices);
32072                 for (var i = 0; i < selectedIndices.length; i++) {
32073                         var rec = this.store.getAt(selectedIndices[i]);
32074                         this.store.remove(rec);
32075                 }
32076     },
32077
32078 /**
32079  *      Double click fires the event, but also, if this is draggable, and there is only one other
32080  *      related DropZone, it transfers the selected node.
32081  */
32082     onDblClick : function(e){
32083         var item = this.findItemFromChild(e.getTarget());
32084         if(item){
32085             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32086                 return false;
32087             }
32088             if (this.dragGroup) {
32089                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32090                     while (targets.indexOf(this.dropZone) > -1) {
32091                             targets.remove(this.dropZone);
32092                                 }
32093                     if (targets.length == 1) {
32094                                         this.dragZone.cachedTarget = null;
32095                         var el = Roo.get(targets[0].getEl());
32096                         var box = el.getBox(true);
32097                         targets[0].onNodeDrop(el.dom, {
32098                                 target: el.dom,
32099                                 xy: [box.x, box.y + box.height - 1]
32100                         }, null, this.getDragData(e));
32101                     }
32102                 }
32103         }
32104     },
32105     
32106     handleSelection: function(e) {
32107                 this.dragZone.cachedTarget = null;
32108         var item = this.findItemFromChild(e.getTarget());
32109         if (!item) {
32110                 this.clearSelections(true);
32111                 return;
32112         }
32113                 if (item && (this.multiSelect || this.singleSelect)){
32114                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32115                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32116                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32117                                 this.unselect(item);
32118                         } else {
32119                                 this.select(item, this.multiSelect && e.ctrlKey);
32120                                 this.lastSelection = item;
32121                         }
32122                 }
32123     },
32124
32125     onItemClick : function(item, index, e){
32126                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32127                         return false;
32128                 }
32129                 return true;
32130     },
32131
32132     unselect : function(nodeInfo, suppressEvent){
32133                 var node = this.getNode(nodeInfo);
32134                 if(node && this.isSelected(node)){
32135                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32136                                 Roo.fly(node).removeClass(this.selectedClass);
32137                                 this.selections.remove(node);
32138                                 if(!suppressEvent){
32139                                         this.fireEvent("selectionchange", this, this.selections);
32140                                 }
32141                         }
32142                 }
32143     }
32144 });
32145 /*
32146  * Based on:
32147  * Ext JS Library 1.1.1
32148  * Copyright(c) 2006-2007, Ext JS, LLC.
32149  *
32150  * Originally Released Under LGPL - original licence link has changed is not relivant.
32151  *
32152  * Fork - LGPL
32153  * <script type="text/javascript">
32154  */
32155  
32156 /**
32157  * @class Roo.LayoutManager
32158  * @extends Roo.util.Observable
32159  * Base class for layout managers.
32160  */
32161 Roo.LayoutManager = function(container, config){
32162     Roo.LayoutManager.superclass.constructor.call(this);
32163     this.el = Roo.get(container);
32164     // ie scrollbar fix
32165     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32166         document.body.scroll = "no";
32167     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32168         this.el.position('relative');
32169     }
32170     this.id = this.el.id;
32171     this.el.addClass("x-layout-container");
32172     /** false to disable window resize monitoring @type Boolean */
32173     this.monitorWindowResize = true;
32174     this.regions = {};
32175     this.addEvents({
32176         /**
32177          * @event layout
32178          * Fires when a layout is performed. 
32179          * @param {Roo.LayoutManager} this
32180          */
32181         "layout" : true,
32182         /**
32183          * @event regionresized
32184          * Fires when the user resizes a region. 
32185          * @param {Roo.LayoutRegion} region The resized region
32186          * @param {Number} newSize The new size (width for east/west, height for north/south)
32187          */
32188         "regionresized" : true,
32189         /**
32190          * @event regioncollapsed
32191          * Fires when a region is collapsed. 
32192          * @param {Roo.LayoutRegion} region The collapsed region
32193          */
32194         "regioncollapsed" : true,
32195         /**
32196          * @event regionexpanded
32197          * Fires when a region is expanded.  
32198          * @param {Roo.LayoutRegion} region The expanded region
32199          */
32200         "regionexpanded" : true
32201     });
32202     this.updating = false;
32203     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32204 };
32205
32206 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32207     /**
32208      * Returns true if this layout is currently being updated
32209      * @return {Boolean}
32210      */
32211     isUpdating : function(){
32212         return this.updating; 
32213     },
32214     
32215     /**
32216      * Suspend the LayoutManager from doing auto-layouts while
32217      * making multiple add or remove calls
32218      */
32219     beginUpdate : function(){
32220         this.updating = true;    
32221     },
32222     
32223     /**
32224      * Restore auto-layouts and optionally disable the manager from performing a layout
32225      * @param {Boolean} noLayout true to disable a layout update 
32226      */
32227     endUpdate : function(noLayout){
32228         this.updating = false;
32229         if(!noLayout){
32230             this.layout();
32231         }    
32232     },
32233     
32234     layout: function(){
32235         
32236     },
32237     
32238     onRegionResized : function(region, newSize){
32239         this.fireEvent("regionresized", region, newSize);
32240         this.layout();
32241     },
32242     
32243     onRegionCollapsed : function(region){
32244         this.fireEvent("regioncollapsed", region);
32245     },
32246     
32247     onRegionExpanded : function(region){
32248         this.fireEvent("regionexpanded", region);
32249     },
32250         
32251     /**
32252      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32253      * performs box-model adjustments.
32254      * @return {Object} The size as an object {width: (the width), height: (the height)}
32255      */
32256     getViewSize : function(){
32257         var size;
32258         if(this.el.dom != document.body){
32259             size = this.el.getSize();
32260         }else{
32261             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32262         }
32263         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32264         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32265         return size;
32266     },
32267     
32268     /**
32269      * Returns the Element this layout is bound to.
32270      * @return {Roo.Element}
32271      */
32272     getEl : function(){
32273         return this.el;
32274     },
32275     
32276     /**
32277      * Returns the specified region.
32278      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32279      * @return {Roo.LayoutRegion}
32280      */
32281     getRegion : function(target){
32282         return this.regions[target.toLowerCase()];
32283     },
32284     
32285     onWindowResize : function(){
32286         if(this.monitorWindowResize){
32287             this.layout();
32288         }
32289     }
32290 });/*
32291  * Based on:
32292  * Ext JS Library 1.1.1
32293  * Copyright(c) 2006-2007, Ext JS, LLC.
32294  *
32295  * Originally Released Under LGPL - original licence link has changed is not relivant.
32296  *
32297  * Fork - LGPL
32298  * <script type="text/javascript">
32299  */
32300 /**
32301  * @class Roo.BorderLayout
32302  * @extends Roo.LayoutManager
32303  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32304  * please see: <br><br>
32305  * <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>
32306  * <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>
32307  * Example:
32308  <pre><code>
32309  var layout = new Roo.BorderLayout(document.body, {
32310     north: {
32311         initialSize: 25,
32312         titlebar: false
32313     },
32314     west: {
32315         split:true,
32316         initialSize: 200,
32317         minSize: 175,
32318         maxSize: 400,
32319         titlebar: true,
32320         collapsible: true
32321     },
32322     east: {
32323         split:true,
32324         initialSize: 202,
32325         minSize: 175,
32326         maxSize: 400,
32327         titlebar: true,
32328         collapsible: true
32329     },
32330     south: {
32331         split:true,
32332         initialSize: 100,
32333         minSize: 100,
32334         maxSize: 200,
32335         titlebar: true,
32336         collapsible: true
32337     },
32338     center: {
32339         titlebar: true,
32340         autoScroll:true,
32341         resizeTabs: true,
32342         minTabWidth: 50,
32343         preferredTabWidth: 150
32344     }
32345 });
32346
32347 // shorthand
32348 var CP = Roo.ContentPanel;
32349
32350 layout.beginUpdate();
32351 layout.add("north", new CP("north", "North"));
32352 layout.add("south", new CP("south", {title: "South", closable: true}));
32353 layout.add("west", new CP("west", {title: "West"}));
32354 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32355 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32356 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32357 layout.getRegion("center").showPanel("center1");
32358 layout.endUpdate();
32359 </code></pre>
32360
32361 <b>The container the layout is rendered into can be either the body element or any other element.
32362 If it is not the body element, the container needs to either be an absolute positioned element,
32363 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32364 the container size if it is not the body element.</b>
32365
32366 * @constructor
32367 * Create a new BorderLayout
32368 * @param {String/HTMLElement/Element} container The container this layout is bound to
32369 * @param {Object} config Configuration options
32370  */
32371 Roo.BorderLayout = function(container, config){
32372     config = config || {};
32373     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32374     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32375     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32376         var target = this.factory.validRegions[i];
32377         if(config[target]){
32378             this.addRegion(target, config[target]);
32379         }
32380     }
32381 };
32382
32383 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32384     /**
32385      * Creates and adds a new region if it doesn't already exist.
32386      * @param {String} target The target region key (north, south, east, west or center).
32387      * @param {Object} config The regions config object
32388      * @return {BorderLayoutRegion} The new region
32389      */
32390     addRegion : function(target, config){
32391         if(!this.regions[target]){
32392             var r = this.factory.create(target, this, config);
32393             this.bindRegion(target, r);
32394         }
32395         return this.regions[target];
32396     },
32397
32398     // private (kinda)
32399     bindRegion : function(name, r){
32400         this.regions[name] = r;
32401         r.on("visibilitychange", this.layout, this);
32402         r.on("paneladded", this.layout, this);
32403         r.on("panelremoved", this.layout, this);
32404         r.on("invalidated", this.layout, this);
32405         r.on("resized", this.onRegionResized, this);
32406         r.on("collapsed", this.onRegionCollapsed, this);
32407         r.on("expanded", this.onRegionExpanded, this);
32408     },
32409
32410     /**
32411      * Performs a layout update.
32412      */
32413     layout : function(){
32414         if(this.updating) return;
32415         var size = this.getViewSize();
32416         var w = size.width;
32417         var h = size.height;
32418         var centerW = w;
32419         var centerH = h;
32420         var centerY = 0;
32421         var centerX = 0;
32422         //var x = 0, y = 0;
32423
32424         var rs = this.regions;
32425         var north = rs["north"];
32426         var south = rs["south"]; 
32427         var west = rs["west"];
32428         var east = rs["east"];
32429         var center = rs["center"];
32430         //if(this.hideOnLayout){ // not supported anymore
32431             //c.el.setStyle("display", "none");
32432         //}
32433         if(north && north.isVisible()){
32434             var b = north.getBox();
32435             var m = north.getMargins();
32436             b.width = w - (m.left+m.right);
32437             b.x = m.left;
32438             b.y = m.top;
32439             centerY = b.height + b.y + m.bottom;
32440             centerH -= centerY;
32441             north.updateBox(this.safeBox(b));
32442         }
32443         if(south && south.isVisible()){
32444             var b = south.getBox();
32445             var m = south.getMargins();
32446             b.width = w - (m.left+m.right);
32447             b.x = m.left;
32448             var totalHeight = (b.height + m.top + m.bottom);
32449             b.y = h - totalHeight + m.top;
32450             centerH -= totalHeight;
32451             south.updateBox(this.safeBox(b));
32452         }
32453         if(west && west.isVisible()){
32454             var b = west.getBox();
32455             var m = west.getMargins();
32456             b.height = centerH - (m.top+m.bottom);
32457             b.x = m.left;
32458             b.y = centerY + m.top;
32459             var totalWidth = (b.width + m.left + m.right);
32460             centerX += totalWidth;
32461             centerW -= totalWidth;
32462             west.updateBox(this.safeBox(b));
32463         }
32464         if(east && east.isVisible()){
32465             var b = east.getBox();
32466             var m = east.getMargins();
32467             b.height = centerH - (m.top+m.bottom);
32468             var totalWidth = (b.width + m.left + m.right);
32469             b.x = w - totalWidth + m.left;
32470             b.y = centerY + m.top;
32471             centerW -= totalWidth;
32472             east.updateBox(this.safeBox(b));
32473         }
32474         if(center){
32475             var m = center.getMargins();
32476             var centerBox = {
32477                 x: centerX + m.left,
32478                 y: centerY + m.top,
32479                 width: centerW - (m.left+m.right),
32480                 height: centerH - (m.top+m.bottom)
32481             };
32482             //if(this.hideOnLayout){
32483                 //center.el.setStyle("display", "block");
32484             //}
32485             center.updateBox(this.safeBox(centerBox));
32486         }
32487         this.el.repaint();
32488         this.fireEvent("layout", this);
32489     },
32490
32491     // private
32492     safeBox : function(box){
32493         box.width = Math.max(0, box.width);
32494         box.height = Math.max(0, box.height);
32495         return box;
32496     },
32497
32498     /**
32499      * Adds a ContentPanel (or subclass) to this layout.
32500      * @param {String} target The target region key (north, south, east, west or center).
32501      * @param {Roo.ContentPanel} panel The panel to add
32502      * @return {Roo.ContentPanel} The added panel
32503      */
32504     add : function(target, panel){
32505          
32506         target = target.toLowerCase();
32507         return this.regions[target].add(panel);
32508     },
32509
32510     /**
32511      * Remove a ContentPanel (or subclass) to this layout.
32512      * @param {String} target The target region key (north, south, east, west or center).
32513      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32514      * @return {Roo.ContentPanel} The removed panel
32515      */
32516     remove : function(target, panel){
32517         target = target.toLowerCase();
32518         return this.regions[target].remove(panel);
32519     },
32520
32521     /**
32522      * Searches all regions for a panel with the specified id
32523      * @param {String} panelId
32524      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32525      */
32526     findPanel : function(panelId){
32527         var rs = this.regions;
32528         for(var target in rs){
32529             if(typeof rs[target] != "function"){
32530                 var p = rs[target].getPanel(panelId);
32531                 if(p){
32532                     return p;
32533                 }
32534             }
32535         }
32536         return null;
32537     },
32538
32539     /**
32540      * Searches all regions for a panel with the specified id and activates (shows) it.
32541      * @param {String/ContentPanel} panelId The panels id or the panel itself
32542      * @return {Roo.ContentPanel} The shown panel or null
32543      */
32544     showPanel : function(panelId) {
32545       var rs = this.regions;
32546       for(var target in rs){
32547          var r = rs[target];
32548          if(typeof r != "function"){
32549             if(r.hasPanel(panelId)){
32550                return r.showPanel(panelId);
32551             }
32552          }
32553       }
32554       return null;
32555    },
32556
32557    /**
32558      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32559      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32560      */
32561     restoreState : function(provider){
32562         if(!provider){
32563             provider = Roo.state.Manager;
32564         }
32565         var sm = new Roo.LayoutStateManager();
32566         sm.init(this, provider);
32567     },
32568
32569     /**
32570      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32571      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32572      * a valid ContentPanel config object.  Example:
32573      * <pre><code>
32574 // Create the main layout
32575 var layout = new Roo.BorderLayout('main-ct', {
32576     west: {
32577         split:true,
32578         minSize: 175,
32579         titlebar: true
32580     },
32581     center: {
32582         title:'Components'
32583     }
32584 }, 'main-ct');
32585
32586 // Create and add multiple ContentPanels at once via configs
32587 layout.batchAdd({
32588    west: {
32589        id: 'source-files',
32590        autoCreate:true,
32591        title:'Ext Source Files',
32592        autoScroll:true,
32593        fitToFrame:true
32594    },
32595    center : {
32596        el: cview,
32597        autoScroll:true,
32598        fitToFrame:true,
32599        toolbar: tb,
32600        resizeEl:'cbody'
32601    }
32602 });
32603 </code></pre>
32604      * @param {Object} regions An object containing ContentPanel configs by region name
32605      */
32606     batchAdd : function(regions){
32607         this.beginUpdate();
32608         for(var rname in regions){
32609             var lr = this.regions[rname];
32610             if(lr){
32611                 this.addTypedPanels(lr, regions[rname]);
32612             }
32613         }
32614         this.endUpdate();
32615     },
32616
32617     // private
32618     addTypedPanels : function(lr, ps){
32619         if(typeof ps == 'string'){
32620             lr.add(new Roo.ContentPanel(ps));
32621         }
32622         else if(ps instanceof Array){
32623             for(var i =0, len = ps.length; i < len; i++){
32624                 this.addTypedPanels(lr, ps[i]);
32625             }
32626         }
32627         else if(!ps.events){ // raw config?
32628             var el = ps.el;
32629             delete ps.el; // prevent conflict
32630             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32631         }
32632         else {  // panel object assumed!
32633             lr.add(ps);
32634         }
32635     },
32636     /**
32637      * Adds a xtype elements to the layout.
32638      * <pre><code>
32639
32640 layout.addxtype({
32641        xtype : 'ContentPanel',
32642        region: 'west',
32643        items: [ .... ]
32644    }
32645 );
32646
32647 layout.addxtype({
32648         xtype : 'NestedLayoutPanel',
32649         region: 'west',
32650         layout: {
32651            center: { },
32652            west: { }   
32653         },
32654         items : [ ... list of content panels or nested layout panels.. ]
32655    }
32656 );
32657 </code></pre>
32658      * @param {Object} cfg Xtype definition of item to add.
32659      */
32660     addxtype : function(cfg)
32661     {
32662         // basically accepts a pannel...
32663         // can accept a layout region..!?!?
32664         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32665         
32666         if (!cfg.xtype.match(/Panel$/)) {
32667             return false;
32668         }
32669         var ret = false;
32670         
32671         if (typeof(cfg.region) == 'undefined') {
32672             Roo.log("Failed to add Panel, region was not set");
32673             Roo.log(cfg);
32674             return false;
32675         }
32676         var region = cfg.region;
32677         delete cfg.region;
32678         
32679           
32680         var xitems = [];
32681         if (cfg.items) {
32682             xitems = cfg.items;
32683             delete cfg.items;
32684         }
32685         var nb = false;
32686         
32687         switch(cfg.xtype) 
32688         {
32689             case 'ContentPanel':  // ContentPanel (el, cfg)
32690             case 'ScrollPanel':  // ContentPanel (el, cfg)
32691             case 'ViewPanel': 
32692                 if(cfg.autoCreate) {
32693                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32694                 } else {
32695                     var el = this.el.createChild();
32696                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32697                 }
32698                 
32699                 this.add(region, ret);
32700                 break;
32701             
32702             
32703             case 'TreePanel': // our new panel!
32704                 cfg.el = this.el.createChild();
32705                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32706                 this.add(region, ret);
32707                 break;
32708             
32709             case 'NestedLayoutPanel': 
32710                 // create a new Layout (which is  a Border Layout...
32711                 var el = this.el.createChild();
32712                 var clayout = cfg.layout;
32713                 delete cfg.layout;
32714                 clayout.items   = clayout.items  || [];
32715                 // replace this exitems with the clayout ones..
32716                 xitems = clayout.items;
32717                  
32718                 
32719                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32720                     cfg.background = false;
32721                 }
32722                 var layout = new Roo.BorderLayout(el, clayout);
32723                 
32724                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32725                 //console.log('adding nested layout panel '  + cfg.toSource());
32726                 this.add(region, ret);
32727                 nb = {}; /// find first...
32728                 break;
32729                 
32730             case 'GridPanel': 
32731             
32732                 // needs grid and region
32733                 
32734                 //var el = this.getRegion(region).el.createChild();
32735                 var el = this.el.createChild();
32736                 // create the grid first...
32737                 
32738                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32739                 delete cfg.grid;
32740                 if (region == 'center' && this.active ) {
32741                     cfg.background = false;
32742                 }
32743                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32744                 
32745                 this.add(region, ret);
32746                 if (cfg.background) {
32747                     ret.on('activate', function(gp) {
32748                         if (!gp.grid.rendered) {
32749                             gp.grid.render();
32750                         }
32751                     });
32752                 } else {
32753                     grid.render();
32754                 }
32755                 break;
32756            
32757            
32758            
32759                 
32760                 
32761                 
32762             default: 
32763                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32764                 return null;
32765              // GridPanel (grid, cfg)
32766             
32767         }
32768         this.beginUpdate();
32769         // add children..
32770         var region = '';
32771         var abn = {};
32772         Roo.each(xitems, function(i)  {
32773             region = nb && i.region ? i.region : false;
32774             
32775             var add = ret.addxtype(i);
32776            
32777             if (region) {
32778                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32779                 if (!i.background) {
32780                     abn[region] = nb[region] ;
32781                 }
32782             }
32783             
32784         });
32785         this.endUpdate();
32786
32787         // make the last non-background panel active..
32788         //if (nb) { Roo.log(abn); }
32789         if (nb) {
32790             
32791             for(var r in abn) {
32792                 region = this.getRegion(r);
32793                 if (region) {
32794                     // tried using nb[r], but it does not work..
32795                      
32796                     region.showPanel(abn[r]);
32797                    
32798                 }
32799             }
32800         }
32801         return ret;
32802         
32803     }
32804 });
32805
32806 /**
32807  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32808  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32809  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32810  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32811  * <pre><code>
32812 // shorthand
32813 var CP = Roo.ContentPanel;
32814
32815 var layout = Roo.BorderLayout.create({
32816     north: {
32817         initialSize: 25,
32818         titlebar: false,
32819         panels: [new CP("north", "North")]
32820     },
32821     west: {
32822         split:true,
32823         initialSize: 200,
32824         minSize: 175,
32825         maxSize: 400,
32826         titlebar: true,
32827         collapsible: true,
32828         panels: [new CP("west", {title: "West"})]
32829     },
32830     east: {
32831         split:true,
32832         initialSize: 202,
32833         minSize: 175,
32834         maxSize: 400,
32835         titlebar: true,
32836         collapsible: true,
32837         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32838     },
32839     south: {
32840         split:true,
32841         initialSize: 100,
32842         minSize: 100,
32843         maxSize: 200,
32844         titlebar: true,
32845         collapsible: true,
32846         panels: [new CP("south", {title: "South", closable: true})]
32847     },
32848     center: {
32849         titlebar: true,
32850         autoScroll:true,
32851         resizeTabs: true,
32852         minTabWidth: 50,
32853         preferredTabWidth: 150,
32854         panels: [
32855             new CP("center1", {title: "Close Me", closable: true}),
32856             new CP("center2", {title: "Center Panel", closable: false})
32857         ]
32858     }
32859 }, document.body);
32860
32861 layout.getRegion("center").showPanel("center1");
32862 </code></pre>
32863  * @param config
32864  * @param targetEl
32865  */
32866 Roo.BorderLayout.create = function(config, targetEl){
32867     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32868     layout.beginUpdate();
32869     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32870     for(var j = 0, jlen = regions.length; j < jlen; j++){
32871         var lr = regions[j];
32872         if(layout.regions[lr] && config[lr].panels){
32873             var r = layout.regions[lr];
32874             var ps = config[lr].panels;
32875             layout.addTypedPanels(r, ps);
32876         }
32877     }
32878     layout.endUpdate();
32879     return layout;
32880 };
32881
32882 // private
32883 Roo.BorderLayout.RegionFactory = {
32884     // private
32885     validRegions : ["north","south","east","west","center"],
32886
32887     // private
32888     create : function(target, mgr, config){
32889         target = target.toLowerCase();
32890         if(config.lightweight || config.basic){
32891             return new Roo.BasicLayoutRegion(mgr, config, target);
32892         }
32893         switch(target){
32894             case "north":
32895                 return new Roo.NorthLayoutRegion(mgr, config);
32896             case "south":
32897                 return new Roo.SouthLayoutRegion(mgr, config);
32898             case "east":
32899                 return new Roo.EastLayoutRegion(mgr, config);
32900             case "west":
32901                 return new Roo.WestLayoutRegion(mgr, config);
32902             case "center":
32903                 return new Roo.CenterLayoutRegion(mgr, config);
32904         }
32905         throw 'Layout region "'+target+'" not supported.';
32906     }
32907 };/*
32908  * Based on:
32909  * Ext JS Library 1.1.1
32910  * Copyright(c) 2006-2007, Ext JS, LLC.
32911  *
32912  * Originally Released Under LGPL - original licence link has changed is not relivant.
32913  *
32914  * Fork - LGPL
32915  * <script type="text/javascript">
32916  */
32917  
32918 /**
32919  * @class Roo.BasicLayoutRegion
32920  * @extends Roo.util.Observable
32921  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32922  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32923  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32924  */
32925 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32926     this.mgr = mgr;
32927     this.position  = pos;
32928     this.events = {
32929         /**
32930          * @scope Roo.BasicLayoutRegion
32931          */
32932         
32933         /**
32934          * @event beforeremove
32935          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32936          * @param {Roo.LayoutRegion} this
32937          * @param {Roo.ContentPanel} panel The panel
32938          * @param {Object} e The cancel event object
32939          */
32940         "beforeremove" : true,
32941         /**
32942          * @event invalidated
32943          * Fires when the layout for this region is changed.
32944          * @param {Roo.LayoutRegion} this
32945          */
32946         "invalidated" : true,
32947         /**
32948          * @event visibilitychange
32949          * Fires when this region is shown or hidden 
32950          * @param {Roo.LayoutRegion} this
32951          * @param {Boolean} visibility true or false
32952          */
32953         "visibilitychange" : true,
32954         /**
32955          * @event paneladded
32956          * Fires when a panel is added. 
32957          * @param {Roo.LayoutRegion} this
32958          * @param {Roo.ContentPanel} panel The panel
32959          */
32960         "paneladded" : true,
32961         /**
32962          * @event panelremoved
32963          * Fires when a panel is removed. 
32964          * @param {Roo.LayoutRegion} this
32965          * @param {Roo.ContentPanel} panel The panel
32966          */
32967         "panelremoved" : true,
32968         /**
32969          * @event collapsed
32970          * Fires when this region is collapsed.
32971          * @param {Roo.LayoutRegion} this
32972          */
32973         "collapsed" : true,
32974         /**
32975          * @event expanded
32976          * Fires when this region is expanded.
32977          * @param {Roo.LayoutRegion} this
32978          */
32979         "expanded" : true,
32980         /**
32981          * @event slideshow
32982          * Fires when this region is slid into view.
32983          * @param {Roo.LayoutRegion} this
32984          */
32985         "slideshow" : true,
32986         /**
32987          * @event slidehide
32988          * Fires when this region slides out of view. 
32989          * @param {Roo.LayoutRegion} this
32990          */
32991         "slidehide" : true,
32992         /**
32993          * @event panelactivated
32994          * Fires when a panel is activated. 
32995          * @param {Roo.LayoutRegion} this
32996          * @param {Roo.ContentPanel} panel The activated panel
32997          */
32998         "panelactivated" : true,
32999         /**
33000          * @event resized
33001          * Fires when the user resizes this region. 
33002          * @param {Roo.LayoutRegion} this
33003          * @param {Number} newSize The new size (width for east/west, height for north/south)
33004          */
33005         "resized" : true
33006     };
33007     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33008     this.panels = new Roo.util.MixedCollection();
33009     this.panels.getKey = this.getPanelId.createDelegate(this);
33010     this.box = null;
33011     this.activePanel = null;
33012     // ensure listeners are added...
33013     
33014     if (config.listeners || config.events) {
33015         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33016             listeners : config.listeners || {},
33017             events : config.events || {}
33018         });
33019     }
33020     
33021     if(skipConfig !== true){
33022         this.applyConfig(config);
33023     }
33024 };
33025
33026 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33027     getPanelId : function(p){
33028         return p.getId();
33029     },
33030     
33031     applyConfig : function(config){
33032         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33033         this.config = config;
33034         
33035     },
33036     
33037     /**
33038      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33039      * the width, for horizontal (north, south) the height.
33040      * @param {Number} newSize The new width or height
33041      */
33042     resizeTo : function(newSize){
33043         var el = this.el ? this.el :
33044                  (this.activePanel ? this.activePanel.getEl() : null);
33045         if(el){
33046             switch(this.position){
33047                 case "east":
33048                 case "west":
33049                     el.setWidth(newSize);
33050                     this.fireEvent("resized", this, newSize);
33051                 break;
33052                 case "north":
33053                 case "south":
33054                     el.setHeight(newSize);
33055                     this.fireEvent("resized", this, newSize);
33056                 break;                
33057             }
33058         }
33059     },
33060     
33061     getBox : function(){
33062         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33063     },
33064     
33065     getMargins : function(){
33066         return this.margins;
33067     },
33068     
33069     updateBox : function(box){
33070         this.box = box;
33071         var el = this.activePanel.getEl();
33072         el.dom.style.left = box.x + "px";
33073         el.dom.style.top = box.y + "px";
33074         this.activePanel.setSize(box.width, box.height);
33075     },
33076     
33077     /**
33078      * Returns the container element for this region.
33079      * @return {Roo.Element}
33080      */
33081     getEl : function(){
33082         return this.activePanel;
33083     },
33084     
33085     /**
33086      * Returns true if this region is currently visible.
33087      * @return {Boolean}
33088      */
33089     isVisible : function(){
33090         return this.activePanel ? true : false;
33091     },
33092     
33093     setActivePanel : function(panel){
33094         panel = this.getPanel(panel);
33095         if(this.activePanel && this.activePanel != panel){
33096             this.activePanel.setActiveState(false);
33097             this.activePanel.getEl().setLeftTop(-10000,-10000);
33098         }
33099         this.activePanel = panel;
33100         panel.setActiveState(true);
33101         if(this.box){
33102             panel.setSize(this.box.width, this.box.height);
33103         }
33104         this.fireEvent("panelactivated", this, panel);
33105         this.fireEvent("invalidated");
33106     },
33107     
33108     /**
33109      * Show the specified panel.
33110      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33111      * @return {Roo.ContentPanel} The shown panel or null
33112      */
33113     showPanel : function(panel){
33114         if(panel = this.getPanel(panel)){
33115             this.setActivePanel(panel);
33116         }
33117         return panel;
33118     },
33119     
33120     /**
33121      * Get the active panel for this region.
33122      * @return {Roo.ContentPanel} The active panel or null
33123      */
33124     getActivePanel : function(){
33125         return this.activePanel;
33126     },
33127     
33128     /**
33129      * Add the passed ContentPanel(s)
33130      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33131      * @return {Roo.ContentPanel} The panel added (if only one was added)
33132      */
33133     add : function(panel){
33134         if(arguments.length > 1){
33135             for(var i = 0, len = arguments.length; i < len; i++) {
33136                 this.add(arguments[i]);
33137             }
33138             return null;
33139         }
33140         if(this.hasPanel(panel)){
33141             this.showPanel(panel);
33142             return panel;
33143         }
33144         var el = panel.getEl();
33145         if(el.dom.parentNode != this.mgr.el.dom){
33146             this.mgr.el.dom.appendChild(el.dom);
33147         }
33148         if(panel.setRegion){
33149             panel.setRegion(this);
33150         }
33151         this.panels.add(panel);
33152         el.setStyle("position", "absolute");
33153         if(!panel.background){
33154             this.setActivePanel(panel);
33155             if(this.config.initialSize && this.panels.getCount()==1){
33156                 this.resizeTo(this.config.initialSize);
33157             }
33158         }
33159         this.fireEvent("paneladded", this, panel);
33160         return panel;
33161     },
33162     
33163     /**
33164      * Returns true if the panel is in this region.
33165      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33166      * @return {Boolean}
33167      */
33168     hasPanel : function(panel){
33169         if(typeof panel == "object"){ // must be panel obj
33170             panel = panel.getId();
33171         }
33172         return this.getPanel(panel) ? true : false;
33173     },
33174     
33175     /**
33176      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33177      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33178      * @param {Boolean} preservePanel Overrides the config preservePanel option
33179      * @return {Roo.ContentPanel} The panel that was removed
33180      */
33181     remove : function(panel, preservePanel){
33182         panel = this.getPanel(panel);
33183         if(!panel){
33184             return null;
33185         }
33186         var e = {};
33187         this.fireEvent("beforeremove", this, panel, e);
33188         if(e.cancel === true){
33189             return null;
33190         }
33191         var panelId = panel.getId();
33192         this.panels.removeKey(panelId);
33193         return panel;
33194     },
33195     
33196     /**
33197      * Returns the panel specified or null if it's not in this region.
33198      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33199      * @return {Roo.ContentPanel}
33200      */
33201     getPanel : function(id){
33202         if(typeof id == "object"){ // must be panel obj
33203             return id;
33204         }
33205         return this.panels.get(id);
33206     },
33207     
33208     /**
33209      * Returns this regions position (north/south/east/west/center).
33210      * @return {String} 
33211      */
33212     getPosition: function(){
33213         return this.position;    
33214     }
33215 });/*
33216  * Based on:
33217  * Ext JS Library 1.1.1
33218  * Copyright(c) 2006-2007, Ext JS, LLC.
33219  *
33220  * Originally Released Under LGPL - original licence link has changed is not relivant.
33221  *
33222  * Fork - LGPL
33223  * <script type="text/javascript">
33224  */
33225  
33226 /**
33227  * @class Roo.LayoutRegion
33228  * @extends Roo.BasicLayoutRegion
33229  * This class represents a region in a layout manager.
33230  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33231  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33232  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33233  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33234  * @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})
33235  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33236  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33237  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33238  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33239  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33240  * @cfg {String}    title           The title for the region (overrides panel titles)
33241  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33242  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33243  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33244  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33245  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33246  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33247  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33248  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33249  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33250  * @cfg {Boolean}   showPin         True to show a pin button
33251  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33252  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33253  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33254  * @cfg {Number}    width           For East/West panels
33255  * @cfg {Number}    height          For North/South panels
33256  * @cfg {Boolean}   split           To show the splitter
33257  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33258  */
33259 Roo.LayoutRegion = function(mgr, config, pos){
33260     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33261     var dh = Roo.DomHelper;
33262     /** This region's container element 
33263     * @type Roo.Element */
33264     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33265     /** This region's title element 
33266     * @type Roo.Element */
33267
33268     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33269         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33270         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33271     ]}, true);
33272     this.titleEl.enableDisplayMode();
33273     /** This region's title text element 
33274     * @type HTMLElement */
33275     this.titleTextEl = this.titleEl.dom.firstChild;
33276     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33277     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33278     this.closeBtn.enableDisplayMode();
33279     this.closeBtn.on("click", this.closeClicked, this);
33280     this.closeBtn.hide();
33281
33282     this.createBody(config);
33283     this.visible = true;
33284     this.collapsed = false;
33285
33286     if(config.hideWhenEmpty){
33287         this.hide();
33288         this.on("paneladded", this.validateVisibility, this);
33289         this.on("panelremoved", this.validateVisibility, this);
33290     }
33291     this.applyConfig(config);
33292 };
33293
33294 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33295
33296     createBody : function(){
33297         /** This region's body element 
33298         * @type Roo.Element */
33299         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33300     },
33301
33302     applyConfig : function(c){
33303         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33304             var dh = Roo.DomHelper;
33305             if(c.titlebar !== false){
33306                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33307                 this.collapseBtn.on("click", this.collapse, this);
33308                 this.collapseBtn.enableDisplayMode();
33309
33310                 if(c.showPin === true || this.showPin){
33311                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33312                     this.stickBtn.enableDisplayMode();
33313                     this.stickBtn.on("click", this.expand, this);
33314                     this.stickBtn.hide();
33315                 }
33316             }
33317             /** This region's collapsed element
33318             * @type Roo.Element */
33319             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33320                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33321             ]}, true);
33322             if(c.floatable !== false){
33323                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33324                this.collapsedEl.on("click", this.collapseClick, this);
33325             }
33326
33327             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33328                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33329                    id: "message", unselectable: "on", style:{"float":"left"}});
33330                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33331              }
33332             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33333             this.expandBtn.on("click", this.expand, this);
33334         }
33335         if(this.collapseBtn){
33336             this.collapseBtn.setVisible(c.collapsible == true);
33337         }
33338         this.cmargins = c.cmargins || this.cmargins ||
33339                          (this.position == "west" || this.position == "east" ?
33340                              {top: 0, left: 2, right:2, bottom: 0} :
33341                              {top: 2, left: 0, right:0, bottom: 2});
33342         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33343         this.bottomTabs = c.tabPosition != "top";
33344         this.autoScroll = c.autoScroll || false;
33345         if(this.autoScroll){
33346             this.bodyEl.setStyle("overflow", "auto");
33347         }else{
33348             this.bodyEl.setStyle("overflow", "hidden");
33349         }
33350         //if(c.titlebar !== false){
33351             if((!c.titlebar && !c.title) || c.titlebar === false){
33352                 this.titleEl.hide();
33353             }else{
33354                 this.titleEl.show();
33355                 if(c.title){
33356                     this.titleTextEl.innerHTML = c.title;
33357                 }
33358             }
33359         //}
33360         this.duration = c.duration || .30;
33361         this.slideDuration = c.slideDuration || .45;
33362         this.config = c;
33363         if(c.collapsed){
33364             this.collapse(true);
33365         }
33366         if(c.hidden){
33367             this.hide();
33368         }
33369     },
33370     /**
33371      * Returns true if this region is currently visible.
33372      * @return {Boolean}
33373      */
33374     isVisible : function(){
33375         return this.visible;
33376     },
33377
33378     /**
33379      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33380      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33381      */
33382     setCollapsedTitle : function(title){
33383         title = title || "&#160;";
33384         if(this.collapsedTitleTextEl){
33385             this.collapsedTitleTextEl.innerHTML = title;
33386         }
33387     },
33388
33389     getBox : function(){
33390         var b;
33391         if(!this.collapsed){
33392             b = this.el.getBox(false, true);
33393         }else{
33394             b = this.collapsedEl.getBox(false, true);
33395         }
33396         return b;
33397     },
33398
33399     getMargins : function(){
33400         return this.collapsed ? this.cmargins : this.margins;
33401     },
33402
33403     highlight : function(){
33404         this.el.addClass("x-layout-panel-dragover");
33405     },
33406
33407     unhighlight : function(){
33408         this.el.removeClass("x-layout-panel-dragover");
33409     },
33410
33411     updateBox : function(box){
33412         this.box = box;
33413         if(!this.collapsed){
33414             this.el.dom.style.left = box.x + "px";
33415             this.el.dom.style.top = box.y + "px";
33416             this.updateBody(box.width, box.height);
33417         }else{
33418             this.collapsedEl.dom.style.left = box.x + "px";
33419             this.collapsedEl.dom.style.top = box.y + "px";
33420             this.collapsedEl.setSize(box.width, box.height);
33421         }
33422         if(this.tabs){
33423             this.tabs.autoSizeTabs();
33424         }
33425     },
33426
33427     updateBody : function(w, h){
33428         if(w !== null){
33429             this.el.setWidth(w);
33430             w -= this.el.getBorderWidth("rl");
33431             if(this.config.adjustments){
33432                 w += this.config.adjustments[0];
33433             }
33434         }
33435         if(h !== null){
33436             this.el.setHeight(h);
33437             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33438             h -= this.el.getBorderWidth("tb");
33439             if(this.config.adjustments){
33440                 h += this.config.adjustments[1];
33441             }
33442             this.bodyEl.setHeight(h);
33443             if(this.tabs){
33444                 h = this.tabs.syncHeight(h);
33445             }
33446         }
33447         if(this.panelSize){
33448             w = w !== null ? w : this.panelSize.width;
33449             h = h !== null ? h : this.panelSize.height;
33450         }
33451         if(this.activePanel){
33452             var el = this.activePanel.getEl();
33453             w = w !== null ? w : el.getWidth();
33454             h = h !== null ? h : el.getHeight();
33455             this.panelSize = {width: w, height: h};
33456             this.activePanel.setSize(w, h);
33457         }
33458         if(Roo.isIE && this.tabs){
33459             this.tabs.el.repaint();
33460         }
33461     },
33462
33463     /**
33464      * Returns the container element for this region.
33465      * @return {Roo.Element}
33466      */
33467     getEl : function(){
33468         return this.el;
33469     },
33470
33471     /**
33472      * Hides this region.
33473      */
33474     hide : function(){
33475         if(!this.collapsed){
33476             this.el.dom.style.left = "-2000px";
33477             this.el.hide();
33478         }else{
33479             this.collapsedEl.dom.style.left = "-2000px";
33480             this.collapsedEl.hide();
33481         }
33482         this.visible = false;
33483         this.fireEvent("visibilitychange", this, false);
33484     },
33485
33486     /**
33487      * Shows this region if it was previously hidden.
33488      */
33489     show : function(){
33490         if(!this.collapsed){
33491             this.el.show();
33492         }else{
33493             this.collapsedEl.show();
33494         }
33495         this.visible = true;
33496         this.fireEvent("visibilitychange", this, true);
33497     },
33498
33499     closeClicked : function(){
33500         if(this.activePanel){
33501             this.remove(this.activePanel);
33502         }
33503     },
33504
33505     collapseClick : function(e){
33506         if(this.isSlid){
33507            e.stopPropagation();
33508            this.slideIn();
33509         }else{
33510            e.stopPropagation();
33511            this.slideOut();
33512         }
33513     },
33514
33515     /**
33516      * Collapses this region.
33517      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33518      */
33519     collapse : function(skipAnim){
33520         if(this.collapsed) return;
33521         this.collapsed = true;
33522         if(this.split){
33523             this.split.el.hide();
33524         }
33525         if(this.config.animate && skipAnim !== true){
33526             this.fireEvent("invalidated", this);
33527             this.animateCollapse();
33528         }else{
33529             this.el.setLocation(-20000,-20000);
33530             this.el.hide();
33531             this.collapsedEl.show();
33532             this.fireEvent("collapsed", this);
33533             this.fireEvent("invalidated", this);
33534         }
33535     },
33536
33537     animateCollapse : function(){
33538         // overridden
33539     },
33540
33541     /**
33542      * Expands this region if it was previously collapsed.
33543      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33544      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33545      */
33546     expand : function(e, skipAnim){
33547         if(e) e.stopPropagation();
33548         if(!this.collapsed || this.el.hasActiveFx()) return;
33549         if(this.isSlid){
33550             this.afterSlideIn();
33551             skipAnim = true;
33552         }
33553         this.collapsed = false;
33554         if(this.config.animate && skipAnim !== true){
33555             this.animateExpand();
33556         }else{
33557             this.el.show();
33558             if(this.split){
33559                 this.split.el.show();
33560             }
33561             this.collapsedEl.setLocation(-2000,-2000);
33562             this.collapsedEl.hide();
33563             this.fireEvent("invalidated", this);
33564             this.fireEvent("expanded", this);
33565         }
33566     },
33567
33568     animateExpand : function(){
33569         // overridden
33570     },
33571
33572     initTabs : function()
33573     {
33574         this.bodyEl.setStyle("overflow", "hidden");
33575         var ts = new Roo.TabPanel(
33576                 this.bodyEl.dom,
33577                 {
33578                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33579                     disableTooltips: this.config.disableTabTips,
33580                     toolbar : this.config.toolbar
33581                 }
33582         );
33583         if(this.config.hideTabs){
33584             ts.stripWrap.setDisplayed(false);
33585         }
33586         this.tabs = ts;
33587         ts.resizeTabs = this.config.resizeTabs === true;
33588         ts.minTabWidth = this.config.minTabWidth || 40;
33589         ts.maxTabWidth = this.config.maxTabWidth || 250;
33590         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33591         ts.monitorResize = false;
33592         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33593         ts.bodyEl.addClass('x-layout-tabs-body');
33594         this.panels.each(this.initPanelAsTab, this);
33595     },
33596
33597     initPanelAsTab : function(panel){
33598         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33599                     this.config.closeOnTab && panel.isClosable());
33600         if(panel.tabTip !== undefined){
33601             ti.setTooltip(panel.tabTip);
33602         }
33603         ti.on("activate", function(){
33604               this.setActivePanel(panel);
33605         }, this);
33606         if(this.config.closeOnTab){
33607             ti.on("beforeclose", function(t, e){
33608                 e.cancel = true;
33609                 this.remove(panel);
33610             }, this);
33611         }
33612         return ti;
33613     },
33614
33615     updatePanelTitle : function(panel, title){
33616         if(this.activePanel == panel){
33617             this.updateTitle(title);
33618         }
33619         if(this.tabs){
33620             var ti = this.tabs.getTab(panel.getEl().id);
33621             ti.setText(title);
33622             if(panel.tabTip !== undefined){
33623                 ti.setTooltip(panel.tabTip);
33624             }
33625         }
33626     },
33627
33628     updateTitle : function(title){
33629         if(this.titleTextEl && !this.config.title){
33630             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33631         }
33632     },
33633
33634     setActivePanel : function(panel){
33635         panel = this.getPanel(panel);
33636         if(this.activePanel && this.activePanel != panel){
33637             this.activePanel.setActiveState(false);
33638         }
33639         this.activePanel = panel;
33640         panel.setActiveState(true);
33641         if(this.panelSize){
33642             panel.setSize(this.panelSize.width, this.panelSize.height);
33643         }
33644         if(this.closeBtn){
33645             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33646         }
33647         this.updateTitle(panel.getTitle());
33648         if(this.tabs){
33649             this.fireEvent("invalidated", this);
33650         }
33651         this.fireEvent("panelactivated", this, panel);
33652     },
33653
33654     /**
33655      * Shows the specified panel.
33656      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33657      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33658      */
33659     showPanel : function(panel){
33660         if(panel = this.getPanel(panel)){
33661             if(this.tabs){
33662                 var tab = this.tabs.getTab(panel.getEl().id);
33663                 if(tab.isHidden()){
33664                     this.tabs.unhideTab(tab.id);
33665                 }
33666                 tab.activate();
33667             }else{
33668                 this.setActivePanel(panel);
33669             }
33670         }
33671         return panel;
33672     },
33673
33674     /**
33675      * Get the active panel for this region.
33676      * @return {Roo.ContentPanel} The active panel or null
33677      */
33678     getActivePanel : function(){
33679         return this.activePanel;
33680     },
33681
33682     validateVisibility : function(){
33683         if(this.panels.getCount() < 1){
33684             this.updateTitle("&#160;");
33685             this.closeBtn.hide();
33686             this.hide();
33687         }else{
33688             if(!this.isVisible()){
33689                 this.show();
33690             }
33691         }
33692     },
33693
33694     /**
33695      * Adds the passed ContentPanel(s) to this region.
33696      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33697      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33698      */
33699     add : function(panel){
33700         if(arguments.length > 1){
33701             for(var i = 0, len = arguments.length; i < len; i++) {
33702                 this.add(arguments[i]);
33703             }
33704             return null;
33705         }
33706         if(this.hasPanel(panel)){
33707             this.showPanel(panel);
33708             return panel;
33709         }
33710         panel.setRegion(this);
33711         this.panels.add(panel);
33712         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33713             this.bodyEl.dom.appendChild(panel.getEl().dom);
33714             if(panel.background !== true){
33715                 this.setActivePanel(panel);
33716             }
33717             this.fireEvent("paneladded", this, panel);
33718             return panel;
33719         }
33720         if(!this.tabs){
33721             this.initTabs();
33722         }else{
33723             this.initPanelAsTab(panel);
33724         }
33725         if(panel.background !== true){
33726             this.tabs.activate(panel.getEl().id);
33727         }
33728         this.fireEvent("paneladded", this, panel);
33729         return panel;
33730     },
33731
33732     /**
33733      * Hides the tab for the specified panel.
33734      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33735      */
33736     hidePanel : function(panel){
33737         if(this.tabs && (panel = this.getPanel(panel))){
33738             this.tabs.hideTab(panel.getEl().id);
33739         }
33740     },
33741
33742     /**
33743      * Unhides the tab for a previously hidden panel.
33744      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33745      */
33746     unhidePanel : function(panel){
33747         if(this.tabs && (panel = this.getPanel(panel))){
33748             this.tabs.unhideTab(panel.getEl().id);
33749         }
33750     },
33751
33752     clearPanels : function(){
33753         while(this.panels.getCount() > 0){
33754              this.remove(this.panels.first());
33755         }
33756     },
33757
33758     /**
33759      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33760      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33761      * @param {Boolean} preservePanel Overrides the config preservePanel option
33762      * @return {Roo.ContentPanel} The panel that was removed
33763      */
33764     remove : function(panel, preservePanel){
33765         panel = this.getPanel(panel);
33766         if(!panel){
33767             return null;
33768         }
33769         var e = {};
33770         this.fireEvent("beforeremove", this, panel, e);
33771         if(e.cancel === true){
33772             return null;
33773         }
33774         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33775         var panelId = panel.getId();
33776         this.panels.removeKey(panelId);
33777         if(preservePanel){
33778             document.body.appendChild(panel.getEl().dom);
33779         }
33780         if(this.tabs){
33781             this.tabs.removeTab(panel.getEl().id);
33782         }else if (!preservePanel){
33783             this.bodyEl.dom.removeChild(panel.getEl().dom);
33784         }
33785         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33786             var p = this.panels.first();
33787             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33788             tempEl.appendChild(p.getEl().dom);
33789             this.bodyEl.update("");
33790             this.bodyEl.dom.appendChild(p.getEl().dom);
33791             tempEl = null;
33792             this.updateTitle(p.getTitle());
33793             this.tabs = null;
33794             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33795             this.setActivePanel(p);
33796         }
33797         panel.setRegion(null);
33798         if(this.activePanel == panel){
33799             this.activePanel = null;
33800         }
33801         if(this.config.autoDestroy !== false && preservePanel !== true){
33802             try{panel.destroy();}catch(e){}
33803         }
33804         this.fireEvent("panelremoved", this, panel);
33805         return panel;
33806     },
33807
33808     /**
33809      * Returns the TabPanel component used by this region
33810      * @return {Roo.TabPanel}
33811      */
33812     getTabs : function(){
33813         return this.tabs;
33814     },
33815
33816     createTool : function(parentEl, className){
33817         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33818             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33819         btn.addClassOnOver("x-layout-tools-button-over");
33820         return btn;
33821     }
33822 });/*
33823  * Based on:
33824  * Ext JS Library 1.1.1
33825  * Copyright(c) 2006-2007, Ext JS, LLC.
33826  *
33827  * Originally Released Under LGPL - original licence link has changed is not relivant.
33828  *
33829  * Fork - LGPL
33830  * <script type="text/javascript">
33831  */
33832  
33833
33834
33835 /**
33836  * @class Roo.SplitLayoutRegion
33837  * @extends Roo.LayoutRegion
33838  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33839  */
33840 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33841     this.cursor = cursor;
33842     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33843 };
33844
33845 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33846     splitTip : "Drag to resize.",
33847     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33848     useSplitTips : false,
33849
33850     applyConfig : function(config){
33851         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33852         if(config.split){
33853             if(!this.split){
33854                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33855                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33856                 /** The SplitBar for this region 
33857                 * @type Roo.SplitBar */
33858                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33859                 this.split.on("moved", this.onSplitMove, this);
33860                 this.split.useShim = config.useShim === true;
33861                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33862                 if(this.useSplitTips){
33863                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33864                 }
33865                 if(config.collapsible){
33866                     this.split.el.on("dblclick", this.collapse,  this);
33867                 }
33868             }
33869             if(typeof config.minSize != "undefined"){
33870                 this.split.minSize = config.minSize;
33871             }
33872             if(typeof config.maxSize != "undefined"){
33873                 this.split.maxSize = config.maxSize;
33874             }
33875             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33876                 this.hideSplitter();
33877             }
33878         }
33879     },
33880
33881     getHMaxSize : function(){
33882          var cmax = this.config.maxSize || 10000;
33883          var center = this.mgr.getRegion("center");
33884          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33885     },
33886
33887     getVMaxSize : function(){
33888          var cmax = this.config.maxSize || 10000;
33889          var center = this.mgr.getRegion("center");
33890          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33891     },
33892
33893     onSplitMove : function(split, newSize){
33894         this.fireEvent("resized", this, newSize);
33895     },
33896     
33897     /** 
33898      * Returns the {@link Roo.SplitBar} for this region.
33899      * @return {Roo.SplitBar}
33900      */
33901     getSplitBar : function(){
33902         return this.split;
33903     },
33904     
33905     hide : function(){
33906         this.hideSplitter();
33907         Roo.SplitLayoutRegion.superclass.hide.call(this);
33908     },
33909
33910     hideSplitter : function(){
33911         if(this.split){
33912             this.split.el.setLocation(-2000,-2000);
33913             this.split.el.hide();
33914         }
33915     },
33916
33917     show : function(){
33918         if(this.split){
33919             this.split.el.show();
33920         }
33921         Roo.SplitLayoutRegion.superclass.show.call(this);
33922     },
33923     
33924     beforeSlide: function(){
33925         if(Roo.isGecko){// firefox overflow auto bug workaround
33926             this.bodyEl.clip();
33927             if(this.tabs) this.tabs.bodyEl.clip();
33928             if(this.activePanel){
33929                 this.activePanel.getEl().clip();
33930                 
33931                 if(this.activePanel.beforeSlide){
33932                     this.activePanel.beforeSlide();
33933                 }
33934             }
33935         }
33936     },
33937     
33938     afterSlide : function(){
33939         if(Roo.isGecko){// firefox overflow auto bug workaround
33940             this.bodyEl.unclip();
33941             if(this.tabs) this.tabs.bodyEl.unclip();
33942             if(this.activePanel){
33943                 this.activePanel.getEl().unclip();
33944                 if(this.activePanel.afterSlide){
33945                     this.activePanel.afterSlide();
33946                 }
33947             }
33948         }
33949     },
33950
33951     initAutoHide : function(){
33952         if(this.autoHide !== false){
33953             if(!this.autoHideHd){
33954                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33955                 this.autoHideHd = {
33956                     "mouseout": function(e){
33957                         if(!e.within(this.el, true)){
33958                             st.delay(500);
33959                         }
33960                     },
33961                     "mouseover" : function(e){
33962                         st.cancel();
33963                     },
33964                     scope : this
33965                 };
33966             }
33967             this.el.on(this.autoHideHd);
33968         }
33969     },
33970
33971     clearAutoHide : function(){
33972         if(this.autoHide !== false){
33973             this.el.un("mouseout", this.autoHideHd.mouseout);
33974             this.el.un("mouseover", this.autoHideHd.mouseover);
33975         }
33976     },
33977
33978     clearMonitor : function(){
33979         Roo.get(document).un("click", this.slideInIf, this);
33980     },
33981
33982     // these names are backwards but not changed for compat
33983     slideOut : function(){
33984         if(this.isSlid || this.el.hasActiveFx()){
33985             return;
33986         }
33987         this.isSlid = true;
33988         if(this.collapseBtn){
33989             this.collapseBtn.hide();
33990         }
33991         this.closeBtnState = this.closeBtn.getStyle('display');
33992         this.closeBtn.hide();
33993         if(this.stickBtn){
33994             this.stickBtn.show();
33995         }
33996         this.el.show();
33997         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33998         this.beforeSlide();
33999         this.el.setStyle("z-index", 10001);
34000         this.el.slideIn(this.getSlideAnchor(), {
34001             callback: function(){
34002                 this.afterSlide();
34003                 this.initAutoHide();
34004                 Roo.get(document).on("click", this.slideInIf, this);
34005                 this.fireEvent("slideshow", this);
34006             },
34007             scope: this,
34008             block: true
34009         });
34010     },
34011
34012     afterSlideIn : function(){
34013         this.clearAutoHide();
34014         this.isSlid = false;
34015         this.clearMonitor();
34016         this.el.setStyle("z-index", "");
34017         if(this.collapseBtn){
34018             this.collapseBtn.show();
34019         }
34020         this.closeBtn.setStyle('display', this.closeBtnState);
34021         if(this.stickBtn){
34022             this.stickBtn.hide();
34023         }
34024         this.fireEvent("slidehide", this);
34025     },
34026
34027     slideIn : function(cb){
34028         if(!this.isSlid || this.el.hasActiveFx()){
34029             Roo.callback(cb);
34030             return;
34031         }
34032         this.isSlid = false;
34033         this.beforeSlide();
34034         this.el.slideOut(this.getSlideAnchor(), {
34035             callback: function(){
34036                 this.el.setLeftTop(-10000, -10000);
34037                 this.afterSlide();
34038                 this.afterSlideIn();
34039                 Roo.callback(cb);
34040             },
34041             scope: this,
34042             block: true
34043         });
34044     },
34045     
34046     slideInIf : function(e){
34047         if(!e.within(this.el)){
34048             this.slideIn();
34049         }
34050     },
34051
34052     animateCollapse : function(){
34053         this.beforeSlide();
34054         this.el.setStyle("z-index", 20000);
34055         var anchor = this.getSlideAnchor();
34056         this.el.slideOut(anchor, {
34057             callback : function(){
34058                 this.el.setStyle("z-index", "");
34059                 this.collapsedEl.slideIn(anchor, {duration:.3});
34060                 this.afterSlide();
34061                 this.el.setLocation(-10000,-10000);
34062                 this.el.hide();
34063                 this.fireEvent("collapsed", this);
34064             },
34065             scope: this,
34066             block: true
34067         });
34068     },
34069
34070     animateExpand : function(){
34071         this.beforeSlide();
34072         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34073         this.el.setStyle("z-index", 20000);
34074         this.collapsedEl.hide({
34075             duration:.1
34076         });
34077         this.el.slideIn(this.getSlideAnchor(), {
34078             callback : function(){
34079                 this.el.setStyle("z-index", "");
34080                 this.afterSlide();
34081                 if(this.split){
34082                     this.split.el.show();
34083                 }
34084                 this.fireEvent("invalidated", this);
34085                 this.fireEvent("expanded", this);
34086             },
34087             scope: this,
34088             block: true
34089         });
34090     },
34091
34092     anchors : {
34093         "west" : "left",
34094         "east" : "right",
34095         "north" : "top",
34096         "south" : "bottom"
34097     },
34098
34099     sanchors : {
34100         "west" : "l",
34101         "east" : "r",
34102         "north" : "t",
34103         "south" : "b"
34104     },
34105
34106     canchors : {
34107         "west" : "tl-tr",
34108         "east" : "tr-tl",
34109         "north" : "tl-bl",
34110         "south" : "bl-tl"
34111     },
34112
34113     getAnchor : function(){
34114         return this.anchors[this.position];
34115     },
34116
34117     getCollapseAnchor : function(){
34118         return this.canchors[this.position];
34119     },
34120
34121     getSlideAnchor : function(){
34122         return this.sanchors[this.position];
34123     },
34124
34125     getAlignAdj : function(){
34126         var cm = this.cmargins;
34127         switch(this.position){
34128             case "west":
34129                 return [0, 0];
34130             break;
34131             case "east":
34132                 return [0, 0];
34133             break;
34134             case "north":
34135                 return [0, 0];
34136             break;
34137             case "south":
34138                 return [0, 0];
34139             break;
34140         }
34141     },
34142
34143     getExpandAdj : function(){
34144         var c = this.collapsedEl, cm = this.cmargins;
34145         switch(this.position){
34146             case "west":
34147                 return [-(cm.right+c.getWidth()+cm.left), 0];
34148             break;
34149             case "east":
34150                 return [cm.right+c.getWidth()+cm.left, 0];
34151             break;
34152             case "north":
34153                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34154             break;
34155             case "south":
34156                 return [0, cm.top+cm.bottom+c.getHeight()];
34157             break;
34158         }
34159     }
34160 });/*
34161  * Based on:
34162  * Ext JS Library 1.1.1
34163  * Copyright(c) 2006-2007, Ext JS, LLC.
34164  *
34165  * Originally Released Under LGPL - original licence link has changed is not relivant.
34166  *
34167  * Fork - LGPL
34168  * <script type="text/javascript">
34169  */
34170 /*
34171  * These classes are private internal classes
34172  */
34173 Roo.CenterLayoutRegion = function(mgr, config){
34174     Roo.LayoutRegion.call(this, mgr, config, "center");
34175     this.visible = true;
34176     this.minWidth = config.minWidth || 20;
34177     this.minHeight = config.minHeight || 20;
34178 };
34179
34180 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34181     hide : function(){
34182         // center panel can't be hidden
34183     },
34184     
34185     show : function(){
34186         // center panel can't be hidden
34187     },
34188     
34189     getMinWidth: function(){
34190         return this.minWidth;
34191     },
34192     
34193     getMinHeight: function(){
34194         return this.minHeight;
34195     }
34196 });
34197
34198
34199 Roo.NorthLayoutRegion = function(mgr, config){
34200     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34201     if(this.split){
34202         this.split.placement = Roo.SplitBar.TOP;
34203         this.split.orientation = Roo.SplitBar.VERTICAL;
34204         this.split.el.addClass("x-layout-split-v");
34205     }
34206     var size = config.initialSize || config.height;
34207     if(typeof size != "undefined"){
34208         this.el.setHeight(size);
34209     }
34210 };
34211 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34212     orientation: Roo.SplitBar.VERTICAL,
34213     getBox : function(){
34214         if(this.collapsed){
34215             return this.collapsedEl.getBox();
34216         }
34217         var box = this.el.getBox();
34218         if(this.split){
34219             box.height += this.split.el.getHeight();
34220         }
34221         return box;
34222     },
34223     
34224     updateBox : function(box){
34225         if(this.split && !this.collapsed){
34226             box.height -= this.split.el.getHeight();
34227             this.split.el.setLeft(box.x);
34228             this.split.el.setTop(box.y+box.height);
34229             this.split.el.setWidth(box.width);
34230         }
34231         if(this.collapsed){
34232             this.updateBody(box.width, null);
34233         }
34234         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34235     }
34236 });
34237
34238 Roo.SouthLayoutRegion = function(mgr, config){
34239     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34240     if(this.split){
34241         this.split.placement = Roo.SplitBar.BOTTOM;
34242         this.split.orientation = Roo.SplitBar.VERTICAL;
34243         this.split.el.addClass("x-layout-split-v");
34244     }
34245     var size = config.initialSize || config.height;
34246     if(typeof size != "undefined"){
34247         this.el.setHeight(size);
34248     }
34249 };
34250 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34251     orientation: Roo.SplitBar.VERTICAL,
34252     getBox : function(){
34253         if(this.collapsed){
34254             return this.collapsedEl.getBox();
34255         }
34256         var box = this.el.getBox();
34257         if(this.split){
34258             var sh = this.split.el.getHeight();
34259             box.height += sh;
34260             box.y -= sh;
34261         }
34262         return box;
34263     },
34264     
34265     updateBox : function(box){
34266         if(this.split && !this.collapsed){
34267             var sh = this.split.el.getHeight();
34268             box.height -= sh;
34269             box.y += sh;
34270             this.split.el.setLeft(box.x);
34271             this.split.el.setTop(box.y-sh);
34272             this.split.el.setWidth(box.width);
34273         }
34274         if(this.collapsed){
34275             this.updateBody(box.width, null);
34276         }
34277         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34278     }
34279 });
34280
34281 Roo.EastLayoutRegion = function(mgr, config){
34282     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34283     if(this.split){
34284         this.split.placement = Roo.SplitBar.RIGHT;
34285         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34286         this.split.el.addClass("x-layout-split-h");
34287     }
34288     var size = config.initialSize || config.width;
34289     if(typeof size != "undefined"){
34290         this.el.setWidth(size);
34291     }
34292 };
34293 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34294     orientation: Roo.SplitBar.HORIZONTAL,
34295     getBox : function(){
34296         if(this.collapsed){
34297             return this.collapsedEl.getBox();
34298         }
34299         var box = this.el.getBox();
34300         if(this.split){
34301             var sw = this.split.el.getWidth();
34302             box.width += sw;
34303             box.x -= sw;
34304         }
34305         return box;
34306     },
34307
34308     updateBox : function(box){
34309         if(this.split && !this.collapsed){
34310             var sw = this.split.el.getWidth();
34311             box.width -= sw;
34312             this.split.el.setLeft(box.x);
34313             this.split.el.setTop(box.y);
34314             this.split.el.setHeight(box.height);
34315             box.x += sw;
34316         }
34317         if(this.collapsed){
34318             this.updateBody(null, box.height);
34319         }
34320         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34321     }
34322 });
34323
34324 Roo.WestLayoutRegion = function(mgr, config){
34325     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34326     if(this.split){
34327         this.split.placement = Roo.SplitBar.LEFT;
34328         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34329         this.split.el.addClass("x-layout-split-h");
34330     }
34331     var size = config.initialSize || config.width;
34332     if(typeof size != "undefined"){
34333         this.el.setWidth(size);
34334     }
34335 };
34336 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34337     orientation: Roo.SplitBar.HORIZONTAL,
34338     getBox : function(){
34339         if(this.collapsed){
34340             return this.collapsedEl.getBox();
34341         }
34342         var box = this.el.getBox();
34343         if(this.split){
34344             box.width += this.split.el.getWidth();
34345         }
34346         return box;
34347     },
34348     
34349     updateBox : function(box){
34350         if(this.split && !this.collapsed){
34351             var sw = this.split.el.getWidth();
34352             box.width -= sw;
34353             this.split.el.setLeft(box.x+box.width);
34354             this.split.el.setTop(box.y);
34355             this.split.el.setHeight(box.height);
34356         }
34357         if(this.collapsed){
34358             this.updateBody(null, box.height);
34359         }
34360         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34361     }
34362 });
34363 /*
34364  * Based on:
34365  * Ext JS Library 1.1.1
34366  * Copyright(c) 2006-2007, Ext JS, LLC.
34367  *
34368  * Originally Released Under LGPL - original licence link has changed is not relivant.
34369  *
34370  * Fork - LGPL
34371  * <script type="text/javascript">
34372  */
34373  
34374  
34375 /*
34376  * Private internal class for reading and applying state
34377  */
34378 Roo.LayoutStateManager = function(layout){
34379      // default empty state
34380      this.state = {
34381         north: {},
34382         south: {},
34383         east: {},
34384         west: {}       
34385     };
34386 };
34387
34388 Roo.LayoutStateManager.prototype = {
34389     init : function(layout, provider){
34390         this.provider = provider;
34391         var state = provider.get(layout.id+"-layout-state");
34392         if(state){
34393             var wasUpdating = layout.isUpdating();
34394             if(!wasUpdating){
34395                 layout.beginUpdate();
34396             }
34397             for(var key in state){
34398                 if(typeof state[key] != "function"){
34399                     var rstate = state[key];
34400                     var r = layout.getRegion(key);
34401                     if(r && rstate){
34402                         if(rstate.size){
34403                             r.resizeTo(rstate.size);
34404                         }
34405                         if(rstate.collapsed == true){
34406                             r.collapse(true);
34407                         }else{
34408                             r.expand(null, true);
34409                         }
34410                     }
34411                 }
34412             }
34413             if(!wasUpdating){
34414                 layout.endUpdate();
34415             }
34416             this.state = state; 
34417         }
34418         this.layout = layout;
34419         layout.on("regionresized", this.onRegionResized, this);
34420         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34421         layout.on("regionexpanded", this.onRegionExpanded, this);
34422     },
34423     
34424     storeState : function(){
34425         this.provider.set(this.layout.id+"-layout-state", this.state);
34426     },
34427     
34428     onRegionResized : function(region, newSize){
34429         this.state[region.getPosition()].size = newSize;
34430         this.storeState();
34431     },
34432     
34433     onRegionCollapsed : function(region){
34434         this.state[region.getPosition()].collapsed = true;
34435         this.storeState();
34436     },
34437     
34438     onRegionExpanded : function(region){
34439         this.state[region.getPosition()].collapsed = false;
34440         this.storeState();
34441     }
34442 };/*
34443  * Based on:
34444  * Ext JS Library 1.1.1
34445  * Copyright(c) 2006-2007, Ext JS, LLC.
34446  *
34447  * Originally Released Under LGPL - original licence link has changed is not relivant.
34448  *
34449  * Fork - LGPL
34450  * <script type="text/javascript">
34451  */
34452 /**
34453  * @class Roo.ContentPanel
34454  * @extends Roo.util.Observable
34455  * A basic ContentPanel element.
34456  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34457  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34458  * @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
34459  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34460  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34461  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34462  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34463  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34464  * @cfg {String} title          The title for this panel
34465  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34466  * @cfg {String} url            Calls {@link #setUrl} with this value
34467  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34468  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34469  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34470  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34471
34472  * @constructor
34473  * Create a new ContentPanel.
34474  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34475  * @param {String/Object} config A string to set only the title or a config object
34476  * @param {String} content (optional) Set the HTML content for this panel
34477  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34478  */
34479 Roo.ContentPanel = function(el, config, content){
34480     
34481      
34482     /*
34483     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34484         config = el;
34485         el = Roo.id();
34486     }
34487     if (config && config.parentLayout) { 
34488         el = config.parentLayout.el.createChild(); 
34489     }
34490     */
34491     if(el.autoCreate){ // xtype is available if this is called from factory
34492         config = el;
34493         el = Roo.id();
34494     }
34495     this.el = Roo.get(el);
34496     if(!this.el && config && config.autoCreate){
34497         if(typeof config.autoCreate == "object"){
34498             if(!config.autoCreate.id){
34499                 config.autoCreate.id = config.id||el;
34500             }
34501             this.el = Roo.DomHelper.append(document.body,
34502                         config.autoCreate, true);
34503         }else{
34504             this.el = Roo.DomHelper.append(document.body,
34505                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34506         }
34507     }
34508     this.closable = false;
34509     this.loaded = false;
34510     this.active = false;
34511     if(typeof config == "string"){
34512         this.title = config;
34513     }else{
34514         Roo.apply(this, config);
34515     }
34516     
34517     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34518         this.wrapEl = this.el.wrap();
34519         this.toolbar.container = this.el.insertSibling(false, 'before');
34520         this.toolbar = new Roo.Toolbar(this.toolbar);
34521     }
34522     
34523     // xtype created footer. - not sure if will work as we normally have to render first..
34524     if (this.footer && !this.footer.el && this.footer.xtype) {
34525         if (!this.wrapEl) {
34526             this.wrapEl = this.el.wrap();
34527         }
34528     
34529         this.footer.container = this.wrapEl.createChild();
34530          
34531         this.footer = Roo.factory(this.footer, Roo);
34532         
34533     }
34534     
34535     if(this.resizeEl){
34536         this.resizeEl = Roo.get(this.resizeEl, true);
34537     }else{
34538         this.resizeEl = this.el;
34539     }
34540     // handle view.xtype
34541     
34542  
34543     
34544     
34545     this.addEvents({
34546         /**
34547          * @event activate
34548          * Fires when this panel is activated. 
34549          * @param {Roo.ContentPanel} this
34550          */
34551         "activate" : true,
34552         /**
34553          * @event deactivate
34554          * Fires when this panel is activated. 
34555          * @param {Roo.ContentPanel} this
34556          */
34557         "deactivate" : true,
34558
34559         /**
34560          * @event resize
34561          * Fires when this panel is resized if fitToFrame is true.
34562          * @param {Roo.ContentPanel} this
34563          * @param {Number} width The width after any component adjustments
34564          * @param {Number} height The height after any component adjustments
34565          */
34566         "resize" : true,
34567         
34568          /**
34569          * @event render
34570          * Fires when this tab is created
34571          * @param {Roo.ContentPanel} this
34572          */
34573         "render" : true
34574         
34575         
34576         
34577     });
34578     
34579
34580     
34581     
34582     if(this.autoScroll){
34583         this.resizeEl.setStyle("overflow", "auto");
34584     } else {
34585         // fix randome scrolling
34586         this.el.on('scroll', function() {
34587             Roo.log('fix random scolling');
34588             this.scrollTo('top',0); 
34589         });
34590     }
34591     content = content || this.content;
34592     if(content){
34593         this.setContent(content);
34594     }
34595     if(config && config.url){
34596         this.setUrl(this.url, this.params, this.loadOnce);
34597     }
34598     
34599     
34600     
34601     Roo.ContentPanel.superclass.constructor.call(this);
34602     
34603     if (this.view && typeof(this.view.xtype) != 'undefined') {
34604         this.view.el = this.el.appendChild(document.createElement("div"));
34605         this.view = Roo.factory(this.view); 
34606         this.view.render  &&  this.view.render(false, '');  
34607     }
34608     
34609     
34610     this.fireEvent('render', this);
34611 };
34612
34613 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34614     tabTip:'',
34615     setRegion : function(region){
34616         this.region = region;
34617         if(region){
34618            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34619         }else{
34620            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34621         } 
34622     },
34623     
34624     /**
34625      * Returns the toolbar for this Panel if one was configured. 
34626      * @return {Roo.Toolbar} 
34627      */
34628     getToolbar : function(){
34629         return this.toolbar;
34630     },
34631     
34632     setActiveState : function(active){
34633         this.active = active;
34634         if(!active){
34635             this.fireEvent("deactivate", this);
34636         }else{
34637             this.fireEvent("activate", this);
34638         }
34639     },
34640     /**
34641      * Updates this panel's element
34642      * @param {String} content The new content
34643      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34644     */
34645     setContent : function(content, loadScripts){
34646         this.el.update(content, loadScripts);
34647     },
34648
34649     ignoreResize : function(w, h){
34650         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34651             return true;
34652         }else{
34653             this.lastSize = {width: w, height: h};
34654             return false;
34655         }
34656     },
34657     /**
34658      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34659      * @return {Roo.UpdateManager} The UpdateManager
34660      */
34661     getUpdateManager : function(){
34662         return this.el.getUpdateManager();
34663     },
34664      /**
34665      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34666      * @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:
34667 <pre><code>
34668 panel.load({
34669     url: "your-url.php",
34670     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34671     callback: yourFunction,
34672     scope: yourObject, //(optional scope)
34673     discardUrl: false,
34674     nocache: false,
34675     text: "Loading...",
34676     timeout: 30,
34677     scripts: false
34678 });
34679 </code></pre>
34680      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34681      * 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.
34682      * @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}
34683      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34684      * @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.
34685      * @return {Roo.ContentPanel} this
34686      */
34687     load : function(){
34688         var um = this.el.getUpdateManager();
34689         um.update.apply(um, arguments);
34690         return this;
34691     },
34692
34693
34694     /**
34695      * 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.
34696      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34697      * @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)
34698      * @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)
34699      * @return {Roo.UpdateManager} The UpdateManager
34700      */
34701     setUrl : function(url, params, loadOnce){
34702         if(this.refreshDelegate){
34703             this.removeListener("activate", this.refreshDelegate);
34704         }
34705         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34706         this.on("activate", this.refreshDelegate);
34707         return this.el.getUpdateManager();
34708     },
34709     
34710     _handleRefresh : function(url, params, loadOnce){
34711         if(!loadOnce || !this.loaded){
34712             var updater = this.el.getUpdateManager();
34713             updater.update(url, params, this._setLoaded.createDelegate(this));
34714         }
34715     },
34716     
34717     _setLoaded : function(){
34718         this.loaded = true;
34719     }, 
34720     
34721     /**
34722      * Returns this panel's id
34723      * @return {String} 
34724      */
34725     getId : function(){
34726         return this.el.id;
34727     },
34728     
34729     /** 
34730      * Returns this panel's element - used by regiosn to add.
34731      * @return {Roo.Element} 
34732      */
34733     getEl : function(){
34734         return this.wrapEl || this.el;
34735     },
34736     
34737     adjustForComponents : function(width, height)
34738     {
34739         //Roo.log('adjustForComponents ');
34740         if(this.resizeEl != this.el){
34741             width -= this.el.getFrameWidth('lr');
34742             height -= this.el.getFrameWidth('tb');
34743         }
34744         if(this.toolbar){
34745             var te = this.toolbar.getEl();
34746             height -= te.getHeight();
34747             te.setWidth(width);
34748         }
34749         if(this.footer){
34750             var te = this.footer.getEl();
34751             Roo.log("footer:" + te.getHeight());
34752             
34753             height -= te.getHeight();
34754             te.setWidth(width);
34755         }
34756         
34757         
34758         if(this.adjustments){
34759             width += this.adjustments[0];
34760             height += this.adjustments[1];
34761         }
34762         return {"width": width, "height": height};
34763     },
34764     
34765     setSize : function(width, height){
34766         if(this.fitToFrame && !this.ignoreResize(width, height)){
34767             if(this.fitContainer && this.resizeEl != this.el){
34768                 this.el.setSize(width, height);
34769             }
34770             var size = this.adjustForComponents(width, height);
34771             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34772             this.fireEvent('resize', this, size.width, size.height);
34773         }
34774     },
34775     
34776     /**
34777      * Returns this panel's title
34778      * @return {String} 
34779      */
34780     getTitle : function(){
34781         return this.title;
34782     },
34783     
34784     /**
34785      * Set this panel's title
34786      * @param {String} title
34787      */
34788     setTitle : function(title){
34789         this.title = title;
34790         if(this.region){
34791             this.region.updatePanelTitle(this, title);
34792         }
34793     },
34794     
34795     /**
34796      * Returns true is this panel was configured to be closable
34797      * @return {Boolean} 
34798      */
34799     isClosable : function(){
34800         return this.closable;
34801     },
34802     
34803     beforeSlide : function(){
34804         this.el.clip();
34805         this.resizeEl.clip();
34806     },
34807     
34808     afterSlide : function(){
34809         this.el.unclip();
34810         this.resizeEl.unclip();
34811     },
34812     
34813     /**
34814      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34815      *   Will fail silently if the {@link #setUrl} method has not been called.
34816      *   This does not activate the panel, just updates its content.
34817      */
34818     refresh : function(){
34819         if(this.refreshDelegate){
34820            this.loaded = false;
34821            this.refreshDelegate();
34822         }
34823     },
34824     
34825     /**
34826      * Destroys this panel
34827      */
34828     destroy : function(){
34829         this.el.removeAllListeners();
34830         var tempEl = document.createElement("span");
34831         tempEl.appendChild(this.el.dom);
34832         tempEl.innerHTML = "";
34833         this.el.remove();
34834         this.el = null;
34835     },
34836     
34837     /**
34838      * form - if the content panel contains a form - this is a reference to it.
34839      * @type {Roo.form.Form}
34840      */
34841     form : false,
34842     /**
34843      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34844      *    This contains a reference to it.
34845      * @type {Roo.View}
34846      */
34847     view : false,
34848     
34849       /**
34850      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34851      * <pre><code>
34852
34853 layout.addxtype({
34854        xtype : 'Form',
34855        items: [ .... ]
34856    }
34857 );
34858
34859 </code></pre>
34860      * @param {Object} cfg Xtype definition of item to add.
34861      */
34862     
34863     addxtype : function(cfg) {
34864         // add form..
34865         if (cfg.xtype.match(/^Form$/)) {
34866             
34867             var el;
34868             //if (this.footer) {
34869             //    el = this.footer.container.insertSibling(false, 'before');
34870             //} else {
34871                 el = this.el.createChild();
34872             //}
34873
34874             this.form = new  Roo.form.Form(cfg);
34875             
34876             
34877             if ( this.form.allItems.length) this.form.render(el.dom);
34878             return this.form;
34879         }
34880         // should only have one of theses..
34881         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34882             // views.. should not be just added - used named prop 'view''
34883             
34884             cfg.el = this.el.appendChild(document.createElement("div"));
34885             // factory?
34886             
34887             var ret = new Roo.factory(cfg);
34888              
34889              ret.render && ret.render(false, ''); // render blank..
34890             this.view = ret;
34891             return ret;
34892         }
34893         return false;
34894     }
34895 });
34896
34897 /**
34898  * @class Roo.GridPanel
34899  * @extends Roo.ContentPanel
34900  * @constructor
34901  * Create a new GridPanel.
34902  * @param {Roo.grid.Grid} grid The grid for this panel
34903  * @param {String/Object} config A string to set only the panel's title, or a config object
34904  */
34905 Roo.GridPanel = function(grid, config){
34906     
34907   
34908     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34909         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34910         
34911     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34912     
34913     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34914     
34915     if(this.toolbar){
34916         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34917     }
34918     // xtype created footer. - not sure if will work as we normally have to render first..
34919     if (this.footer && !this.footer.el && this.footer.xtype) {
34920         
34921         this.footer.container = this.grid.getView().getFooterPanel(true);
34922         this.footer.dataSource = this.grid.dataSource;
34923         this.footer = Roo.factory(this.footer, Roo);
34924         
34925     }
34926     
34927     grid.monitorWindowResize = false; // turn off autosizing
34928     grid.autoHeight = false;
34929     grid.autoWidth = false;
34930     this.grid = grid;
34931     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34932 };
34933
34934 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34935     getId : function(){
34936         return this.grid.id;
34937     },
34938     
34939     /**
34940      * Returns the grid for this panel
34941      * @return {Roo.grid.Grid} 
34942      */
34943     getGrid : function(){
34944         return this.grid;    
34945     },
34946     
34947     setSize : function(width, height){
34948         if(!this.ignoreResize(width, height)){
34949             var grid = this.grid;
34950             var size = this.adjustForComponents(width, height);
34951             grid.getGridEl().setSize(size.width, size.height);
34952             grid.autoSize();
34953         }
34954     },
34955     
34956     beforeSlide : function(){
34957         this.grid.getView().scroller.clip();
34958     },
34959     
34960     afterSlide : function(){
34961         this.grid.getView().scroller.unclip();
34962     },
34963     
34964     destroy : function(){
34965         this.grid.destroy();
34966         delete this.grid;
34967         Roo.GridPanel.superclass.destroy.call(this); 
34968     }
34969 });
34970
34971
34972 /**
34973  * @class Roo.NestedLayoutPanel
34974  * @extends Roo.ContentPanel
34975  * @constructor
34976  * Create a new NestedLayoutPanel.
34977  * 
34978  * 
34979  * @param {Roo.BorderLayout} layout The layout for this panel
34980  * @param {String/Object} config A string to set only the title or a config object
34981  */
34982 Roo.NestedLayoutPanel = function(layout, config)
34983 {
34984     // construct with only one argument..
34985     /* FIXME - implement nicer consturctors
34986     if (layout.layout) {
34987         config = layout;
34988         layout = config.layout;
34989         delete config.layout;
34990     }
34991     if (layout.xtype && !layout.getEl) {
34992         // then layout needs constructing..
34993         layout = Roo.factory(layout, Roo);
34994     }
34995     */
34996     
34997     
34998     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
34999     
35000     layout.monitorWindowResize = false; // turn off autosizing
35001     this.layout = layout;
35002     this.layout.getEl().addClass("x-layout-nested-layout");
35003     
35004     
35005     
35006     
35007 };
35008
35009 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35010
35011     setSize : function(width, height){
35012         if(!this.ignoreResize(width, height)){
35013             var size = this.adjustForComponents(width, height);
35014             var el = this.layout.getEl();
35015             el.setSize(size.width, size.height);
35016             var touch = el.dom.offsetWidth;
35017             this.layout.layout();
35018             // ie requires a double layout on the first pass
35019             if(Roo.isIE && !this.initialized){
35020                 this.initialized = true;
35021                 this.layout.layout();
35022             }
35023         }
35024     },
35025     
35026     // activate all subpanels if not currently active..
35027     
35028     setActiveState : function(active){
35029         this.active = active;
35030         if(!active){
35031             this.fireEvent("deactivate", this);
35032             return;
35033         }
35034         
35035         this.fireEvent("activate", this);
35036         // not sure if this should happen before or after..
35037         if (!this.layout) {
35038             return; // should not happen..
35039         }
35040         var reg = false;
35041         for (var r in this.layout.regions) {
35042             reg = this.layout.getRegion(r);
35043             if (reg.getActivePanel()) {
35044                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35045                 reg.setActivePanel(reg.getActivePanel());
35046                 continue;
35047             }
35048             if (!reg.panels.length) {
35049                 continue;
35050             }
35051             reg.showPanel(reg.getPanel(0));
35052         }
35053         
35054         
35055         
35056         
35057     },
35058     
35059     /**
35060      * Returns the nested BorderLayout for this panel
35061      * @return {Roo.BorderLayout} 
35062      */
35063     getLayout : function(){
35064         return this.layout;
35065     },
35066     
35067      /**
35068      * Adds a xtype elements to the layout of the nested panel
35069      * <pre><code>
35070
35071 panel.addxtype({
35072        xtype : 'ContentPanel',
35073        region: 'west',
35074        items: [ .... ]
35075    }
35076 );
35077
35078 panel.addxtype({
35079         xtype : 'NestedLayoutPanel',
35080         region: 'west',
35081         layout: {
35082            center: { },
35083            west: { }   
35084         },
35085         items : [ ... list of content panels or nested layout panels.. ]
35086    }
35087 );
35088 </code></pre>
35089      * @param {Object} cfg Xtype definition of item to add.
35090      */
35091     addxtype : function(cfg) {
35092         return this.layout.addxtype(cfg);
35093     
35094     }
35095 });
35096
35097 Roo.ScrollPanel = function(el, config, content){
35098     config = config || {};
35099     config.fitToFrame = true;
35100     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35101     
35102     this.el.dom.style.overflow = "hidden";
35103     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35104     this.el.removeClass("x-layout-inactive-content");
35105     this.el.on("mousewheel", this.onWheel, this);
35106
35107     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35108     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35109     up.unselectable(); down.unselectable();
35110     up.on("click", this.scrollUp, this);
35111     down.on("click", this.scrollDown, this);
35112     up.addClassOnOver("x-scroller-btn-over");
35113     down.addClassOnOver("x-scroller-btn-over");
35114     up.addClassOnClick("x-scroller-btn-click");
35115     down.addClassOnClick("x-scroller-btn-click");
35116     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35117
35118     this.resizeEl = this.el;
35119     this.el = wrap; this.up = up; this.down = down;
35120 };
35121
35122 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35123     increment : 100,
35124     wheelIncrement : 5,
35125     scrollUp : function(){
35126         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35127     },
35128
35129     scrollDown : function(){
35130         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35131     },
35132
35133     afterScroll : function(){
35134         var el = this.resizeEl;
35135         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35136         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35137         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35138     },
35139
35140     setSize : function(){
35141         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35142         this.afterScroll();
35143     },
35144
35145     onWheel : function(e){
35146         var d = e.getWheelDelta();
35147         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35148         this.afterScroll();
35149         e.stopEvent();
35150     },
35151
35152     setContent : function(content, loadScripts){
35153         this.resizeEl.update(content, loadScripts);
35154     }
35155
35156 });
35157
35158
35159
35160
35161
35162
35163
35164
35165
35166 /**
35167  * @class Roo.TreePanel
35168  * @extends Roo.ContentPanel
35169  * @constructor
35170  * Create a new TreePanel. - defaults to fit/scoll contents.
35171  * @param {String/Object} config A string to set only the panel's title, or a config object
35172  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35173  */
35174 Roo.TreePanel = function(config){
35175     var el = config.el;
35176     var tree = config.tree;
35177     delete config.tree; 
35178     delete config.el; // hopefull!
35179     
35180     // wrapper for IE7 strict & safari scroll issue
35181     
35182     var treeEl = el.createChild();
35183     config.resizeEl = treeEl;
35184     
35185     
35186     
35187     Roo.TreePanel.superclass.constructor.call(this, el, config);
35188  
35189  
35190     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35191     //console.log(tree);
35192     this.on('activate', function()
35193     {
35194         if (this.tree.rendered) {
35195             return;
35196         }
35197         //console.log('render tree');
35198         this.tree.render();
35199     });
35200     // this should not be needed.. - it's actually the 'el' that resizes?
35201     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35202     
35203     //this.on('resize',  function (cp, w, h) {
35204     //        this.tree.innerCt.setWidth(w);
35205     //        this.tree.innerCt.setHeight(h);
35206     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35207     //});
35208
35209         
35210     
35211 };
35212
35213 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35214     fitToFrame : true,
35215     autoScroll : true
35216 });
35217
35218
35219
35220
35221
35222
35223
35224
35225
35226
35227
35228 /*
35229  * Based on:
35230  * Ext JS Library 1.1.1
35231  * Copyright(c) 2006-2007, Ext JS, LLC.
35232  *
35233  * Originally Released Under LGPL - original licence link has changed is not relivant.
35234  *
35235  * Fork - LGPL
35236  * <script type="text/javascript">
35237  */
35238  
35239
35240 /**
35241  * @class Roo.ReaderLayout
35242  * @extends Roo.BorderLayout
35243  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35244  * center region containing two nested regions (a top one for a list view and one for item preview below),
35245  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35246  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35247  * expedites the setup of the overall layout and regions for this common application style.
35248  * Example:
35249  <pre><code>
35250 var reader = new Roo.ReaderLayout();
35251 var CP = Roo.ContentPanel;  // shortcut for adding
35252
35253 reader.beginUpdate();
35254 reader.add("north", new CP("north", "North"));
35255 reader.add("west", new CP("west", {title: "West"}));
35256 reader.add("east", new CP("east", {title: "East"}));
35257
35258 reader.regions.listView.add(new CP("listView", "List"));
35259 reader.regions.preview.add(new CP("preview", "Preview"));
35260 reader.endUpdate();
35261 </code></pre>
35262 * @constructor
35263 * Create a new ReaderLayout
35264 * @param {Object} config Configuration options
35265 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35266 * document.body if omitted)
35267 */
35268 Roo.ReaderLayout = function(config, renderTo){
35269     var c = config || {size:{}};
35270     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35271         north: c.north !== false ? Roo.apply({
35272             split:false,
35273             initialSize: 32,
35274             titlebar: false
35275         }, c.north) : false,
35276         west: c.west !== false ? Roo.apply({
35277             split:true,
35278             initialSize: 200,
35279             minSize: 175,
35280             maxSize: 400,
35281             titlebar: true,
35282             collapsible: true,
35283             animate: true,
35284             margins:{left:5,right:0,bottom:5,top:5},
35285             cmargins:{left:5,right:5,bottom:5,top:5}
35286         }, c.west) : false,
35287         east: c.east !== false ? Roo.apply({
35288             split:true,
35289             initialSize: 200,
35290             minSize: 175,
35291             maxSize: 400,
35292             titlebar: true,
35293             collapsible: true,
35294             animate: true,
35295             margins:{left:0,right:5,bottom:5,top:5},
35296             cmargins:{left:5,right:5,bottom:5,top:5}
35297         }, c.east) : false,
35298         center: Roo.apply({
35299             tabPosition: 'top',
35300             autoScroll:false,
35301             closeOnTab: true,
35302             titlebar:false,
35303             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35304         }, c.center)
35305     });
35306
35307     this.el.addClass('x-reader');
35308
35309     this.beginUpdate();
35310
35311     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35312         south: c.preview !== false ? Roo.apply({
35313             split:true,
35314             initialSize: 200,
35315             minSize: 100,
35316             autoScroll:true,
35317             collapsible:true,
35318             titlebar: true,
35319             cmargins:{top:5,left:0, right:0, bottom:0}
35320         }, c.preview) : false,
35321         center: Roo.apply({
35322             autoScroll:false,
35323             titlebar:false,
35324             minHeight:200
35325         }, c.listView)
35326     });
35327     this.add('center', new Roo.NestedLayoutPanel(inner,
35328             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35329
35330     this.endUpdate();
35331
35332     this.regions.preview = inner.getRegion('south');
35333     this.regions.listView = inner.getRegion('center');
35334 };
35335
35336 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35337  * Based on:
35338  * Ext JS Library 1.1.1
35339  * Copyright(c) 2006-2007, Ext JS, LLC.
35340  *
35341  * Originally Released Under LGPL - original licence link has changed is not relivant.
35342  *
35343  * Fork - LGPL
35344  * <script type="text/javascript">
35345  */
35346  
35347 /**
35348  * @class Roo.grid.Grid
35349  * @extends Roo.util.Observable
35350  * This class represents the primary interface of a component based grid control.
35351  * <br><br>Usage:<pre><code>
35352  var grid = new Roo.grid.Grid("my-container-id", {
35353      ds: myDataStore,
35354      cm: myColModel,
35355      selModel: mySelectionModel,
35356      autoSizeColumns: true,
35357      monitorWindowResize: false,
35358      trackMouseOver: true
35359  });
35360  // set any options
35361  grid.render();
35362  * </code></pre>
35363  * <b>Common Problems:</b><br/>
35364  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35365  * element will correct this<br/>
35366  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35367  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35368  * are unpredictable.<br/>
35369  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35370  * grid to calculate dimensions/offsets.<br/>
35371   * @constructor
35372  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35373  * The container MUST have some type of size defined for the grid to fill. The container will be
35374  * automatically set to position relative if it isn't already.
35375  * @param {Object} config A config object that sets properties on this grid.
35376  */
35377 Roo.grid.Grid = function(container, config){
35378         // initialize the container
35379         this.container = Roo.get(container);
35380         this.container.update("");
35381         this.container.setStyle("overflow", "hidden");
35382     this.container.addClass('x-grid-container');
35383
35384     this.id = this.container.id;
35385
35386     Roo.apply(this, config);
35387     // check and correct shorthanded configs
35388     if(this.ds){
35389         this.dataSource = this.ds;
35390         delete this.ds;
35391     }
35392     if(this.cm){
35393         this.colModel = this.cm;
35394         delete this.cm;
35395     }
35396     if(this.sm){
35397         this.selModel = this.sm;
35398         delete this.sm;
35399     }
35400
35401     if (this.selModel) {
35402         this.selModel = Roo.factory(this.selModel, Roo.grid);
35403         this.sm = this.selModel;
35404         this.sm.xmodule = this.xmodule || false;
35405     }
35406     if (typeof(this.colModel.config) == 'undefined') {
35407         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35408         this.cm = this.colModel;
35409         this.cm.xmodule = this.xmodule || false;
35410     }
35411     if (this.dataSource) {
35412         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35413         this.ds = this.dataSource;
35414         this.ds.xmodule = this.xmodule || false;
35415          
35416     }
35417     
35418     
35419     
35420     if(this.width){
35421         this.container.setWidth(this.width);
35422     }
35423
35424     if(this.height){
35425         this.container.setHeight(this.height);
35426     }
35427     /** @private */
35428         this.addEvents({
35429         // raw events
35430         /**
35431          * @event click
35432          * The raw click event for the entire grid.
35433          * @param {Roo.EventObject} e
35434          */
35435         "click" : true,
35436         /**
35437          * @event dblclick
35438          * The raw dblclick event for the entire grid.
35439          * @param {Roo.EventObject} e
35440          */
35441         "dblclick" : true,
35442         /**
35443          * @event contextmenu
35444          * The raw contextmenu event for the entire grid.
35445          * @param {Roo.EventObject} e
35446          */
35447         "contextmenu" : true,
35448         /**
35449          * @event mousedown
35450          * The raw mousedown event for the entire grid.
35451          * @param {Roo.EventObject} e
35452          */
35453         "mousedown" : true,
35454         /**
35455          * @event mouseup
35456          * The raw mouseup event for the entire grid.
35457          * @param {Roo.EventObject} e
35458          */
35459         "mouseup" : true,
35460         /**
35461          * @event mouseover
35462          * The raw mouseover event for the entire grid.
35463          * @param {Roo.EventObject} e
35464          */
35465         "mouseover" : true,
35466         /**
35467          * @event mouseout
35468          * The raw mouseout event for the entire grid.
35469          * @param {Roo.EventObject} e
35470          */
35471         "mouseout" : true,
35472         /**
35473          * @event keypress
35474          * The raw keypress event for the entire grid.
35475          * @param {Roo.EventObject} e
35476          */
35477         "keypress" : true,
35478         /**
35479          * @event keydown
35480          * The raw keydown event for the entire grid.
35481          * @param {Roo.EventObject} e
35482          */
35483         "keydown" : true,
35484
35485         // custom events
35486
35487         /**
35488          * @event cellclick
35489          * Fires when a cell is clicked
35490          * @param {Grid} this
35491          * @param {Number} rowIndex
35492          * @param {Number} columnIndex
35493          * @param {Roo.EventObject} e
35494          */
35495         "cellclick" : true,
35496         /**
35497          * @event celldblclick
35498          * Fires when a cell is double clicked
35499          * @param {Grid} this
35500          * @param {Number} rowIndex
35501          * @param {Number} columnIndex
35502          * @param {Roo.EventObject} e
35503          */
35504         "celldblclick" : true,
35505         /**
35506          * @event rowclick
35507          * Fires when a row is clicked
35508          * @param {Grid} this
35509          * @param {Number} rowIndex
35510          * @param {Roo.EventObject} e
35511          */
35512         "rowclick" : true,
35513         /**
35514          * @event rowdblclick
35515          * Fires when a row is double clicked
35516          * @param {Grid} this
35517          * @param {Number} rowIndex
35518          * @param {Roo.EventObject} e
35519          */
35520         "rowdblclick" : true,
35521         /**
35522          * @event headerclick
35523          * Fires when a header is clicked
35524          * @param {Grid} this
35525          * @param {Number} columnIndex
35526          * @param {Roo.EventObject} e
35527          */
35528         "headerclick" : true,
35529         /**
35530          * @event headerdblclick
35531          * Fires when a header cell is double clicked
35532          * @param {Grid} this
35533          * @param {Number} columnIndex
35534          * @param {Roo.EventObject} e
35535          */
35536         "headerdblclick" : true,
35537         /**
35538          * @event rowcontextmenu
35539          * Fires when a row is right clicked
35540          * @param {Grid} this
35541          * @param {Number} rowIndex
35542          * @param {Roo.EventObject} e
35543          */
35544         "rowcontextmenu" : true,
35545         /**
35546          * @event cellcontextmenu
35547          * Fires when a cell is right clicked
35548          * @param {Grid} this
35549          * @param {Number} rowIndex
35550          * @param {Number} cellIndex
35551          * @param {Roo.EventObject} e
35552          */
35553          "cellcontextmenu" : true,
35554         /**
35555          * @event headercontextmenu
35556          * Fires when a header is right clicked
35557          * @param {Grid} this
35558          * @param {Number} columnIndex
35559          * @param {Roo.EventObject} e
35560          */
35561         "headercontextmenu" : true,
35562         /**
35563          * @event bodyscroll
35564          * Fires when the body element is scrolled
35565          * @param {Number} scrollLeft
35566          * @param {Number} scrollTop
35567          */
35568         "bodyscroll" : true,
35569         /**
35570          * @event columnresize
35571          * Fires when the user resizes a column
35572          * @param {Number} columnIndex
35573          * @param {Number} newSize
35574          */
35575         "columnresize" : true,
35576         /**
35577          * @event columnmove
35578          * Fires when the user moves a column
35579          * @param {Number} oldIndex
35580          * @param {Number} newIndex
35581          */
35582         "columnmove" : true,
35583         /**
35584          * @event startdrag
35585          * Fires when row(s) start being dragged
35586          * @param {Grid} this
35587          * @param {Roo.GridDD} dd The drag drop object
35588          * @param {event} e The raw browser event
35589          */
35590         "startdrag" : true,
35591         /**
35592          * @event enddrag
35593          * Fires when a drag operation is complete
35594          * @param {Grid} this
35595          * @param {Roo.GridDD} dd The drag drop object
35596          * @param {event} e The raw browser event
35597          */
35598         "enddrag" : true,
35599         /**
35600          * @event dragdrop
35601          * Fires when dragged row(s) are dropped on a valid DD target
35602          * @param {Grid} this
35603          * @param {Roo.GridDD} dd The drag drop object
35604          * @param {String} targetId The target drag drop object
35605          * @param {event} e The raw browser event
35606          */
35607         "dragdrop" : true,
35608         /**
35609          * @event dragover
35610          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35611          * @param {Grid} this
35612          * @param {Roo.GridDD} dd The drag drop object
35613          * @param {String} targetId The target drag drop object
35614          * @param {event} e The raw browser event
35615          */
35616         "dragover" : true,
35617         /**
35618          * @event dragenter
35619          *  Fires when the dragged row(s) first cross another DD target while being dragged
35620          * @param {Grid} this
35621          * @param {Roo.GridDD} dd The drag drop object
35622          * @param {String} targetId The target drag drop object
35623          * @param {event} e The raw browser event
35624          */
35625         "dragenter" : true,
35626         /**
35627          * @event dragout
35628          * Fires when the dragged row(s) leave another DD target while being dragged
35629          * @param {Grid} this
35630          * @param {Roo.GridDD} dd The drag drop object
35631          * @param {String} targetId The target drag drop object
35632          * @param {event} e The raw browser event
35633          */
35634         "dragout" : true,
35635         /**
35636          * @event rowclass
35637          * Fires when a row is rendered, so you can change add a style to it.
35638          * @param {GridView} gridview   The grid view
35639          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35640          */
35641         'rowclass' : true,
35642
35643         /**
35644          * @event render
35645          * Fires when the grid is rendered
35646          * @param {Grid} grid
35647          */
35648         'render' : true
35649     });
35650
35651     Roo.grid.Grid.superclass.constructor.call(this);
35652 };
35653 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35654     
35655     /**
35656      * @cfg {String} ddGroup - drag drop group.
35657      */
35658
35659     /**
35660      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35661      */
35662     minColumnWidth : 25,
35663
35664     /**
35665      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35666      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35667      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35668      */
35669     autoSizeColumns : false,
35670
35671     /**
35672      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35673      */
35674     autoSizeHeaders : true,
35675
35676     /**
35677      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35678      */
35679     monitorWindowResize : true,
35680
35681     /**
35682      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35683      * rows measured to get a columns size. Default is 0 (all rows).
35684      */
35685     maxRowsToMeasure : 0,
35686
35687     /**
35688      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35689      */
35690     trackMouseOver : true,
35691
35692     /**
35693     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35694     */
35695     
35696     /**
35697     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35698     */
35699     enableDragDrop : false,
35700     
35701     /**
35702     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35703     */
35704     enableColumnMove : true,
35705     
35706     /**
35707     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35708     */
35709     enableColumnHide : true,
35710     
35711     /**
35712     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35713     */
35714     enableRowHeightSync : false,
35715     
35716     /**
35717     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35718     */
35719     stripeRows : true,
35720     
35721     /**
35722     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35723     */
35724     autoHeight : false,
35725
35726     /**
35727      * @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.
35728      */
35729     autoExpandColumn : false,
35730
35731     /**
35732     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35733     * Default is 50.
35734     */
35735     autoExpandMin : 50,
35736
35737     /**
35738     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35739     */
35740     autoExpandMax : 1000,
35741
35742     /**
35743     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35744     */
35745     view : null,
35746
35747     /**
35748     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35749     */
35750     loadMask : false,
35751     /**
35752     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35753     */
35754     dropTarget: false,
35755     
35756    
35757     
35758     // private
35759     rendered : false,
35760
35761     /**
35762     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35763     * of a fixed width. Default is false.
35764     */
35765     /**
35766     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35767     */
35768     /**
35769      * Called once after all setup has been completed and the grid is ready to be rendered.
35770      * @return {Roo.grid.Grid} this
35771      */
35772     render : function()
35773     {
35774         var c = this.container;
35775         // try to detect autoHeight/width mode
35776         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35777             this.autoHeight = true;
35778         }
35779         var view = this.getView();
35780         view.init(this);
35781
35782         c.on("click", this.onClick, this);
35783         c.on("dblclick", this.onDblClick, this);
35784         c.on("contextmenu", this.onContextMenu, this);
35785         c.on("keydown", this.onKeyDown, this);
35786         if (Roo.isTouch) {
35787             c.on("touch", this.onTouch, this);
35788         }
35789
35790         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35791
35792         this.getSelectionModel().init(this);
35793
35794         view.render();
35795
35796         if(this.loadMask){
35797             this.loadMask = new Roo.LoadMask(this.container,
35798                     Roo.apply({store:this.dataSource}, this.loadMask));
35799         }
35800         
35801         
35802         if (this.toolbar && this.toolbar.xtype) {
35803             this.toolbar.container = this.getView().getHeaderPanel(true);
35804             this.toolbar = new Roo.Toolbar(this.toolbar);
35805         }
35806         if (this.footer && this.footer.xtype) {
35807             this.footer.dataSource = this.getDataSource();
35808             this.footer.container = this.getView().getFooterPanel(true);
35809             this.footer = Roo.factory(this.footer, Roo);
35810         }
35811         if (this.dropTarget && this.dropTarget.xtype) {
35812             delete this.dropTarget.xtype;
35813             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35814         }
35815         
35816         
35817         this.rendered = true;
35818         this.fireEvent('render', this);
35819         return this;
35820     },
35821
35822         /**
35823          * Reconfigures the grid to use a different Store and Column Model.
35824          * The View will be bound to the new objects and refreshed.
35825          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35826          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35827          */
35828     reconfigure : function(dataSource, colModel){
35829         if(this.loadMask){
35830             this.loadMask.destroy();
35831             this.loadMask = new Roo.LoadMask(this.container,
35832                     Roo.apply({store:dataSource}, this.loadMask));
35833         }
35834         this.view.bind(dataSource, colModel);
35835         this.dataSource = dataSource;
35836         this.colModel = colModel;
35837         this.view.refresh(true);
35838     },
35839
35840     // private
35841     onKeyDown : function(e){
35842         this.fireEvent("keydown", e);
35843     },
35844
35845     /**
35846      * Destroy this grid.
35847      * @param {Boolean} removeEl True to remove the element
35848      */
35849     destroy : function(removeEl, keepListeners){
35850         if(this.loadMask){
35851             this.loadMask.destroy();
35852         }
35853         var c = this.container;
35854         c.removeAllListeners();
35855         this.view.destroy();
35856         this.colModel.purgeListeners();
35857         if(!keepListeners){
35858             this.purgeListeners();
35859         }
35860         c.update("");
35861         if(removeEl === true){
35862             c.remove();
35863         }
35864     },
35865
35866     // private
35867     processEvent : function(name, e){
35868         // does this fire select???
35869         Roo.log('grid:processEvent '  + name);
35870         this.fireEvent(name == 'touch' ? 'click' : name, e);
35871         var t = e.getTarget();
35872         var v = this.view;
35873         var header = v.findHeaderIndex(t);
35874         if(header !== false){
35875             this.fireEvent("header" + name, this, header, e);
35876         }else{
35877             var row = v.findRowIndex(t);
35878             var cell = v.findCellIndex(t);
35879             if (name == 'touch') {
35880                 // first touch is always a click.
35881                 // hopefull this happens after selection is updated.?
35882                 name = 'click';
35883                 
35884                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
35885                     var cs = this.selModel.getSelectedCell();
35886                     if (row == cs[0] && cell == cs[1]){
35887                         name = 'dblclick';
35888                     }
35889                 }
35890                 if (typeof(this.selModel.getSelections) != 'undefined') {
35891                     var cs = this.selModel.getSelections();
35892                     var ds = this.dataSource;
35893                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
35894                         name = 'dblclick';
35895                     }
35896                 }
35897                 
35898             }
35899             
35900             
35901             if(row !== false){
35902                 this.fireEvent("row" + name, this, row, e);
35903                 if(cell !== false){
35904                     this.fireEvent("cell" + name, this, row, cell, e);
35905                 }
35906             }
35907         }
35908     },
35909
35910     // private
35911     onClick : function(e){
35912         this.processEvent("click", e);
35913     },
35914    // private
35915     onTouch : function(e){
35916         this.processEvent("touch", e);
35917     },
35918
35919     // private
35920     onContextMenu : function(e, t){
35921         this.processEvent("contextmenu", e);
35922     },
35923
35924     // private
35925     onDblClick : function(e){
35926         this.processEvent("dblclick", e);
35927     },
35928
35929     // private
35930     walkCells : function(row, col, step, fn, scope){
35931         var cm = this.colModel, clen = cm.getColumnCount();
35932         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35933         if(step < 0){
35934             if(col < 0){
35935                 row--;
35936                 first = false;
35937             }
35938             while(row >= 0){
35939                 if(!first){
35940                     col = clen-1;
35941                 }
35942                 first = false;
35943                 while(col >= 0){
35944                     if(fn.call(scope || this, row, col, cm) === true){
35945                         return [row, col];
35946                     }
35947                     col--;
35948                 }
35949                 row--;
35950             }
35951         } else {
35952             if(col >= clen){
35953                 row++;
35954                 first = false;
35955             }
35956             while(row < rlen){
35957                 if(!first){
35958                     col = 0;
35959                 }
35960                 first = false;
35961                 while(col < clen){
35962                     if(fn.call(scope || this, row, col, cm) === true){
35963                         return [row, col];
35964                     }
35965                     col++;
35966                 }
35967                 row++;
35968             }
35969         }
35970         return null;
35971     },
35972
35973     // private
35974     getSelections : function(){
35975         return this.selModel.getSelections();
35976     },
35977
35978     /**
35979      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
35980      * but if manual update is required this method will initiate it.
35981      */
35982     autoSize : function(){
35983         if(this.rendered){
35984             this.view.layout();
35985             if(this.view.adjustForScroll){
35986                 this.view.adjustForScroll();
35987             }
35988         }
35989     },
35990
35991     /**
35992      * Returns the grid's underlying element.
35993      * @return {Element} The element
35994      */
35995     getGridEl : function(){
35996         return this.container;
35997     },
35998
35999     // private for compatibility, overridden by editor grid
36000     stopEditing : function(){},
36001
36002     /**
36003      * Returns the grid's SelectionModel.
36004      * @return {SelectionModel}
36005      */
36006     getSelectionModel : function(){
36007         if(!this.selModel){
36008             this.selModel = new Roo.grid.RowSelectionModel();
36009         }
36010         return this.selModel;
36011     },
36012
36013     /**
36014      * Returns the grid's DataSource.
36015      * @return {DataSource}
36016      */
36017     getDataSource : function(){
36018         return this.dataSource;
36019     },
36020
36021     /**
36022      * Returns the grid's ColumnModel.
36023      * @return {ColumnModel}
36024      */
36025     getColumnModel : function(){
36026         return this.colModel;
36027     },
36028
36029     /**
36030      * Returns the grid's GridView object.
36031      * @return {GridView}
36032      */
36033     getView : function(){
36034         if(!this.view){
36035             this.view = new Roo.grid.GridView(this.viewConfig);
36036         }
36037         return this.view;
36038     },
36039     /**
36040      * Called to get grid's drag proxy text, by default returns this.ddText.
36041      * @return {String}
36042      */
36043     getDragDropText : function(){
36044         var count = this.selModel.getCount();
36045         return String.format(this.ddText, count, count == 1 ? '' : 's');
36046     }
36047 });
36048 /**
36049  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36050  * %0 is replaced with the number of selected rows.
36051  * @type String
36052  */
36053 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36054  * Based on:
36055  * Ext JS Library 1.1.1
36056  * Copyright(c) 2006-2007, Ext JS, LLC.
36057  *
36058  * Originally Released Under LGPL - original licence link has changed is not relivant.
36059  *
36060  * Fork - LGPL
36061  * <script type="text/javascript">
36062  */
36063  
36064 Roo.grid.AbstractGridView = function(){
36065         this.grid = null;
36066         
36067         this.events = {
36068             "beforerowremoved" : true,
36069             "beforerowsinserted" : true,
36070             "beforerefresh" : true,
36071             "rowremoved" : true,
36072             "rowsinserted" : true,
36073             "rowupdated" : true,
36074             "refresh" : true
36075         };
36076     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36077 };
36078
36079 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36080     rowClass : "x-grid-row",
36081     cellClass : "x-grid-cell",
36082     tdClass : "x-grid-td",
36083     hdClass : "x-grid-hd",
36084     splitClass : "x-grid-hd-split",
36085     
36086         init: function(grid){
36087         this.grid = grid;
36088                 var cid = this.grid.getGridEl().id;
36089         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36090         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36091         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36092         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36093         },
36094         
36095         getColumnRenderers : function(){
36096         var renderers = [];
36097         var cm = this.grid.colModel;
36098         var colCount = cm.getColumnCount();
36099         for(var i = 0; i < colCount; i++){
36100             renderers[i] = cm.getRenderer(i);
36101         }
36102         return renderers;
36103     },
36104     
36105     getColumnIds : function(){
36106         var ids = [];
36107         var cm = this.grid.colModel;
36108         var colCount = cm.getColumnCount();
36109         for(var i = 0; i < colCount; i++){
36110             ids[i] = cm.getColumnId(i);
36111         }
36112         return ids;
36113     },
36114     
36115     getDataIndexes : function(){
36116         if(!this.indexMap){
36117             this.indexMap = this.buildIndexMap();
36118         }
36119         return this.indexMap.colToData;
36120     },
36121     
36122     getColumnIndexByDataIndex : function(dataIndex){
36123         if(!this.indexMap){
36124             this.indexMap = this.buildIndexMap();
36125         }
36126         return this.indexMap.dataToCol[dataIndex];
36127     },
36128     
36129     /**
36130      * Set a css style for a column dynamically. 
36131      * @param {Number} colIndex The index of the column
36132      * @param {String} name The css property name
36133      * @param {String} value The css value
36134      */
36135     setCSSStyle : function(colIndex, name, value){
36136         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36137         Roo.util.CSS.updateRule(selector, name, value);
36138     },
36139     
36140     generateRules : function(cm){
36141         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36142         Roo.util.CSS.removeStyleSheet(rulesId);
36143         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36144             var cid = cm.getColumnId(i);
36145             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36146                          this.tdSelector, cid, " {\n}\n",
36147                          this.hdSelector, cid, " {\n}\n",
36148                          this.splitSelector, cid, " {\n}\n");
36149         }
36150         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36151     }
36152 });/*
36153  * Based on:
36154  * Ext JS Library 1.1.1
36155  * Copyright(c) 2006-2007, Ext JS, LLC.
36156  *
36157  * Originally Released Under LGPL - original licence link has changed is not relivant.
36158  *
36159  * Fork - LGPL
36160  * <script type="text/javascript">
36161  */
36162
36163 // private
36164 // This is a support class used internally by the Grid components
36165 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36166     this.grid = grid;
36167     this.view = grid.getView();
36168     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36169     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36170     if(hd2){
36171         this.setHandleElId(Roo.id(hd));
36172         this.setOuterHandleElId(Roo.id(hd2));
36173     }
36174     this.scroll = false;
36175 };
36176 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36177     maxDragWidth: 120,
36178     getDragData : function(e){
36179         var t = Roo.lib.Event.getTarget(e);
36180         var h = this.view.findHeaderCell(t);
36181         if(h){
36182             return {ddel: h.firstChild, header:h};
36183         }
36184         return false;
36185     },
36186
36187     onInitDrag : function(e){
36188         this.view.headersDisabled = true;
36189         var clone = this.dragData.ddel.cloneNode(true);
36190         clone.id = Roo.id();
36191         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36192         this.proxy.update(clone);
36193         return true;
36194     },
36195
36196     afterValidDrop : function(){
36197         var v = this.view;
36198         setTimeout(function(){
36199             v.headersDisabled = false;
36200         }, 50);
36201     },
36202
36203     afterInvalidDrop : function(){
36204         var v = this.view;
36205         setTimeout(function(){
36206             v.headersDisabled = false;
36207         }, 50);
36208     }
36209 });
36210 /*
36211  * Based on:
36212  * Ext JS Library 1.1.1
36213  * Copyright(c) 2006-2007, Ext JS, LLC.
36214  *
36215  * Originally Released Under LGPL - original licence link has changed is not relivant.
36216  *
36217  * Fork - LGPL
36218  * <script type="text/javascript">
36219  */
36220 // private
36221 // This is a support class used internally by the Grid components
36222 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36223     this.grid = grid;
36224     this.view = grid.getView();
36225     // split the proxies so they don't interfere with mouse events
36226     this.proxyTop = Roo.DomHelper.append(document.body, {
36227         cls:"col-move-top", html:"&#160;"
36228     }, true);
36229     this.proxyBottom = Roo.DomHelper.append(document.body, {
36230         cls:"col-move-bottom", html:"&#160;"
36231     }, true);
36232     this.proxyTop.hide = this.proxyBottom.hide = function(){
36233         this.setLeftTop(-100,-100);
36234         this.setStyle("visibility", "hidden");
36235     };
36236     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36237     // temporarily disabled
36238     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36239     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36240 };
36241 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36242     proxyOffsets : [-4, -9],
36243     fly: Roo.Element.fly,
36244
36245     getTargetFromEvent : function(e){
36246         var t = Roo.lib.Event.getTarget(e);
36247         var cindex = this.view.findCellIndex(t);
36248         if(cindex !== false){
36249             return this.view.getHeaderCell(cindex);
36250         }
36251         return null;
36252     },
36253
36254     nextVisible : function(h){
36255         var v = this.view, cm = this.grid.colModel;
36256         h = h.nextSibling;
36257         while(h){
36258             if(!cm.isHidden(v.getCellIndex(h))){
36259                 return h;
36260             }
36261             h = h.nextSibling;
36262         }
36263         return null;
36264     },
36265
36266     prevVisible : function(h){
36267         var v = this.view, cm = this.grid.colModel;
36268         h = h.prevSibling;
36269         while(h){
36270             if(!cm.isHidden(v.getCellIndex(h))){
36271                 return h;
36272             }
36273             h = h.prevSibling;
36274         }
36275         return null;
36276     },
36277
36278     positionIndicator : function(h, n, e){
36279         var x = Roo.lib.Event.getPageX(e);
36280         var r = Roo.lib.Dom.getRegion(n.firstChild);
36281         var px, pt, py = r.top + this.proxyOffsets[1];
36282         if((r.right - x) <= (r.right-r.left)/2){
36283             px = r.right+this.view.borderWidth;
36284             pt = "after";
36285         }else{
36286             px = r.left;
36287             pt = "before";
36288         }
36289         var oldIndex = this.view.getCellIndex(h);
36290         var newIndex = this.view.getCellIndex(n);
36291
36292         if(this.grid.colModel.isFixed(newIndex)){
36293             return false;
36294         }
36295
36296         var locked = this.grid.colModel.isLocked(newIndex);
36297
36298         if(pt == "after"){
36299             newIndex++;
36300         }
36301         if(oldIndex < newIndex){
36302             newIndex--;
36303         }
36304         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36305             return false;
36306         }
36307         px +=  this.proxyOffsets[0];
36308         this.proxyTop.setLeftTop(px, py);
36309         this.proxyTop.show();
36310         if(!this.bottomOffset){
36311             this.bottomOffset = this.view.mainHd.getHeight();
36312         }
36313         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36314         this.proxyBottom.show();
36315         return pt;
36316     },
36317
36318     onNodeEnter : function(n, dd, e, data){
36319         if(data.header != n){
36320             this.positionIndicator(data.header, n, e);
36321         }
36322     },
36323
36324     onNodeOver : function(n, dd, e, data){
36325         var result = false;
36326         if(data.header != n){
36327             result = this.positionIndicator(data.header, n, e);
36328         }
36329         if(!result){
36330             this.proxyTop.hide();
36331             this.proxyBottom.hide();
36332         }
36333         return result ? this.dropAllowed : this.dropNotAllowed;
36334     },
36335
36336     onNodeOut : function(n, dd, e, data){
36337         this.proxyTop.hide();
36338         this.proxyBottom.hide();
36339     },
36340
36341     onNodeDrop : function(n, dd, e, data){
36342         var h = data.header;
36343         if(h != n){
36344             var cm = this.grid.colModel;
36345             var x = Roo.lib.Event.getPageX(e);
36346             var r = Roo.lib.Dom.getRegion(n.firstChild);
36347             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36348             var oldIndex = this.view.getCellIndex(h);
36349             var newIndex = this.view.getCellIndex(n);
36350             var locked = cm.isLocked(newIndex);
36351             if(pt == "after"){
36352                 newIndex++;
36353             }
36354             if(oldIndex < newIndex){
36355                 newIndex--;
36356             }
36357             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36358                 return false;
36359             }
36360             cm.setLocked(oldIndex, locked, true);
36361             cm.moveColumn(oldIndex, newIndex);
36362             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36363             return true;
36364         }
36365         return false;
36366     }
36367 });
36368 /*
36369  * Based on:
36370  * Ext JS Library 1.1.1
36371  * Copyright(c) 2006-2007, Ext JS, LLC.
36372  *
36373  * Originally Released Under LGPL - original licence link has changed is not relivant.
36374  *
36375  * Fork - LGPL
36376  * <script type="text/javascript">
36377  */
36378   
36379 /**
36380  * @class Roo.grid.GridView
36381  * @extends Roo.util.Observable
36382  *
36383  * @constructor
36384  * @param {Object} config
36385  */
36386 Roo.grid.GridView = function(config){
36387     Roo.grid.GridView.superclass.constructor.call(this);
36388     this.el = null;
36389
36390     Roo.apply(this, config);
36391 };
36392
36393 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36394
36395     unselectable :  'unselectable="on"',
36396     unselectableCls :  'x-unselectable',
36397     
36398     
36399     rowClass : "x-grid-row",
36400
36401     cellClass : "x-grid-col",
36402
36403     tdClass : "x-grid-td",
36404
36405     hdClass : "x-grid-hd",
36406
36407     splitClass : "x-grid-split",
36408
36409     sortClasses : ["sort-asc", "sort-desc"],
36410
36411     enableMoveAnim : false,
36412
36413     hlColor: "C3DAF9",
36414
36415     dh : Roo.DomHelper,
36416
36417     fly : Roo.Element.fly,
36418
36419     css : Roo.util.CSS,
36420
36421     borderWidth: 1,
36422
36423     splitOffset: 3,
36424
36425     scrollIncrement : 22,
36426
36427     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36428
36429     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36430
36431     bind : function(ds, cm){
36432         if(this.ds){
36433             this.ds.un("load", this.onLoad, this);
36434             this.ds.un("datachanged", this.onDataChange, this);
36435             this.ds.un("add", this.onAdd, this);
36436             this.ds.un("remove", this.onRemove, this);
36437             this.ds.un("update", this.onUpdate, this);
36438             this.ds.un("clear", this.onClear, this);
36439         }
36440         if(ds){
36441             ds.on("load", this.onLoad, this);
36442             ds.on("datachanged", this.onDataChange, this);
36443             ds.on("add", this.onAdd, this);
36444             ds.on("remove", this.onRemove, this);
36445             ds.on("update", this.onUpdate, this);
36446             ds.on("clear", this.onClear, this);
36447         }
36448         this.ds = ds;
36449
36450         if(this.cm){
36451             this.cm.un("widthchange", this.onColWidthChange, this);
36452             this.cm.un("headerchange", this.onHeaderChange, this);
36453             this.cm.un("hiddenchange", this.onHiddenChange, this);
36454             this.cm.un("columnmoved", this.onColumnMove, this);
36455             this.cm.un("columnlockchange", this.onColumnLock, this);
36456         }
36457         if(cm){
36458             this.generateRules(cm);
36459             cm.on("widthchange", this.onColWidthChange, this);
36460             cm.on("headerchange", this.onHeaderChange, this);
36461             cm.on("hiddenchange", this.onHiddenChange, this);
36462             cm.on("columnmoved", this.onColumnMove, this);
36463             cm.on("columnlockchange", this.onColumnLock, this);
36464         }
36465         this.cm = cm;
36466     },
36467
36468     init: function(grid){
36469         Roo.grid.GridView.superclass.init.call(this, grid);
36470
36471         this.bind(grid.dataSource, grid.colModel);
36472
36473         grid.on("headerclick", this.handleHeaderClick, this);
36474
36475         if(grid.trackMouseOver){
36476             grid.on("mouseover", this.onRowOver, this);
36477             grid.on("mouseout", this.onRowOut, this);
36478         }
36479         grid.cancelTextSelection = function(){};
36480         this.gridId = grid.id;
36481
36482         var tpls = this.templates || {};
36483
36484         if(!tpls.master){
36485             tpls.master = new Roo.Template(
36486                '<div class="x-grid" hidefocus="true">',
36487                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36488                   '<div class="x-grid-topbar"></div>',
36489                   '<div class="x-grid-scroller"><div></div></div>',
36490                   '<div class="x-grid-locked">',
36491                       '<div class="x-grid-header">{lockedHeader}</div>',
36492                       '<div class="x-grid-body">{lockedBody}</div>',
36493                   "</div>",
36494                   '<div class="x-grid-viewport">',
36495                       '<div class="x-grid-header">{header}</div>',
36496                       '<div class="x-grid-body">{body}</div>',
36497                   "</div>",
36498                   '<div class="x-grid-bottombar"></div>',
36499                  
36500                   '<div class="x-grid-resize-proxy">&#160;</div>',
36501                "</div>"
36502             );
36503             tpls.master.disableformats = true;
36504         }
36505
36506         if(!tpls.header){
36507             tpls.header = new Roo.Template(
36508                '<table border="0" cellspacing="0" cellpadding="0">',
36509                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36510                "</table>{splits}"
36511             );
36512             tpls.header.disableformats = true;
36513         }
36514         tpls.header.compile();
36515
36516         if(!tpls.hcell){
36517             tpls.hcell = new Roo.Template(
36518                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36519                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36520                 "</div></td>"
36521              );
36522              tpls.hcell.disableFormats = true;
36523         }
36524         tpls.hcell.compile();
36525
36526         if(!tpls.hsplit){
36527             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36528                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36529             tpls.hsplit.disableFormats = true;
36530         }
36531         tpls.hsplit.compile();
36532
36533         if(!tpls.body){
36534             tpls.body = new Roo.Template(
36535                '<table border="0" cellspacing="0" cellpadding="0">',
36536                "<tbody>{rows}</tbody>",
36537                "</table>"
36538             );
36539             tpls.body.disableFormats = true;
36540         }
36541         tpls.body.compile();
36542
36543         if(!tpls.row){
36544             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36545             tpls.row.disableFormats = true;
36546         }
36547         tpls.row.compile();
36548
36549         if(!tpls.cell){
36550             tpls.cell = new Roo.Template(
36551                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36552                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36553                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36554                 "</td>"
36555             );
36556             tpls.cell.disableFormats = true;
36557         }
36558         tpls.cell.compile();
36559
36560         this.templates = tpls;
36561     },
36562
36563     // remap these for backwards compat
36564     onColWidthChange : function(){
36565         this.updateColumns.apply(this, arguments);
36566     },
36567     onHeaderChange : function(){
36568         this.updateHeaders.apply(this, arguments);
36569     }, 
36570     onHiddenChange : function(){
36571         this.handleHiddenChange.apply(this, arguments);
36572     },
36573     onColumnMove : function(){
36574         this.handleColumnMove.apply(this, arguments);
36575     },
36576     onColumnLock : function(){
36577         this.handleLockChange.apply(this, arguments);
36578     },
36579
36580     onDataChange : function(){
36581         this.refresh();
36582         this.updateHeaderSortState();
36583     },
36584
36585     onClear : function(){
36586         this.refresh();
36587     },
36588
36589     onUpdate : function(ds, record){
36590         this.refreshRow(record);
36591     },
36592
36593     refreshRow : function(record){
36594         var ds = this.ds, index;
36595         if(typeof record == 'number'){
36596             index = record;
36597             record = ds.getAt(index);
36598         }else{
36599             index = ds.indexOf(record);
36600         }
36601         this.insertRows(ds, index, index, true);
36602         this.onRemove(ds, record, index+1, true);
36603         this.syncRowHeights(index, index);
36604         this.layout();
36605         this.fireEvent("rowupdated", this, index, record);
36606     },
36607
36608     onAdd : function(ds, records, index){
36609         this.insertRows(ds, index, index + (records.length-1));
36610     },
36611
36612     onRemove : function(ds, record, index, isUpdate){
36613         if(isUpdate !== true){
36614             this.fireEvent("beforerowremoved", this, index, record);
36615         }
36616         var bt = this.getBodyTable(), lt = this.getLockedTable();
36617         if(bt.rows[index]){
36618             bt.firstChild.removeChild(bt.rows[index]);
36619         }
36620         if(lt.rows[index]){
36621             lt.firstChild.removeChild(lt.rows[index]);
36622         }
36623         if(isUpdate !== true){
36624             this.stripeRows(index);
36625             this.syncRowHeights(index, index);
36626             this.layout();
36627             this.fireEvent("rowremoved", this, index, record);
36628         }
36629     },
36630
36631     onLoad : function(){
36632         this.scrollToTop();
36633     },
36634
36635     /**
36636      * Scrolls the grid to the top
36637      */
36638     scrollToTop : function(){
36639         if(this.scroller){
36640             this.scroller.dom.scrollTop = 0;
36641             this.syncScroll();
36642         }
36643     },
36644
36645     /**
36646      * Gets a panel in the header of the grid that can be used for toolbars etc.
36647      * After modifying the contents of this panel a call to grid.autoSize() may be
36648      * required to register any changes in size.
36649      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36650      * @return Roo.Element
36651      */
36652     getHeaderPanel : function(doShow){
36653         if(doShow){
36654             this.headerPanel.show();
36655         }
36656         return this.headerPanel;
36657     },
36658
36659     /**
36660      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36661      * After modifying the contents of this panel a call to grid.autoSize() may be
36662      * required to register any changes in size.
36663      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36664      * @return Roo.Element
36665      */
36666     getFooterPanel : function(doShow){
36667         if(doShow){
36668             this.footerPanel.show();
36669         }
36670         return this.footerPanel;
36671     },
36672
36673     initElements : function(){
36674         var E = Roo.Element;
36675         var el = this.grid.getGridEl().dom.firstChild;
36676         var cs = el.childNodes;
36677
36678         this.el = new E(el);
36679         
36680          this.focusEl = new E(el.firstChild);
36681         this.focusEl.swallowEvent("click", true);
36682         
36683         this.headerPanel = new E(cs[1]);
36684         this.headerPanel.enableDisplayMode("block");
36685
36686         this.scroller = new E(cs[2]);
36687         this.scrollSizer = new E(this.scroller.dom.firstChild);
36688
36689         this.lockedWrap = new E(cs[3]);
36690         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36691         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36692
36693         this.mainWrap = new E(cs[4]);
36694         this.mainHd = new E(this.mainWrap.dom.firstChild);
36695         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36696
36697         this.footerPanel = new E(cs[5]);
36698         this.footerPanel.enableDisplayMode("block");
36699
36700         this.resizeProxy = new E(cs[6]);
36701
36702         this.headerSelector = String.format(
36703            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36704            this.lockedHd.id, this.mainHd.id
36705         );
36706
36707         this.splitterSelector = String.format(
36708            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36709            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36710         );
36711     },
36712     idToCssName : function(s)
36713     {
36714         return s.replace(/[^a-z0-9]+/ig, '-');
36715     },
36716
36717     getHeaderCell : function(index){
36718         return Roo.DomQuery.select(this.headerSelector)[index];
36719     },
36720
36721     getHeaderCellMeasure : function(index){
36722         return this.getHeaderCell(index).firstChild;
36723     },
36724
36725     getHeaderCellText : function(index){
36726         return this.getHeaderCell(index).firstChild.firstChild;
36727     },
36728
36729     getLockedTable : function(){
36730         return this.lockedBody.dom.firstChild;
36731     },
36732
36733     getBodyTable : function(){
36734         return this.mainBody.dom.firstChild;
36735     },
36736
36737     getLockedRow : function(index){
36738         return this.getLockedTable().rows[index];
36739     },
36740
36741     getRow : function(index){
36742         return this.getBodyTable().rows[index];
36743     },
36744
36745     getRowComposite : function(index){
36746         if(!this.rowEl){
36747             this.rowEl = new Roo.CompositeElementLite();
36748         }
36749         var els = [], lrow, mrow;
36750         if(lrow = this.getLockedRow(index)){
36751             els.push(lrow);
36752         }
36753         if(mrow = this.getRow(index)){
36754             els.push(mrow);
36755         }
36756         this.rowEl.elements = els;
36757         return this.rowEl;
36758     },
36759     /**
36760      * Gets the 'td' of the cell
36761      * 
36762      * @param {Integer} rowIndex row to select
36763      * @param {Integer} colIndex column to select
36764      * 
36765      * @return {Object} 
36766      */
36767     getCell : function(rowIndex, colIndex){
36768         var locked = this.cm.getLockedCount();
36769         var source;
36770         if(colIndex < locked){
36771             source = this.lockedBody.dom.firstChild;
36772         }else{
36773             source = this.mainBody.dom.firstChild;
36774             colIndex -= locked;
36775         }
36776         return source.rows[rowIndex].childNodes[colIndex];
36777     },
36778
36779     getCellText : function(rowIndex, colIndex){
36780         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36781     },
36782
36783     getCellBox : function(cell){
36784         var b = this.fly(cell).getBox();
36785         if(Roo.isOpera){ // opera fails to report the Y
36786             b.y = cell.offsetTop + this.mainBody.getY();
36787         }
36788         return b;
36789     },
36790
36791     getCellIndex : function(cell){
36792         var id = String(cell.className).match(this.cellRE);
36793         if(id){
36794             return parseInt(id[1], 10);
36795         }
36796         return 0;
36797     },
36798
36799     findHeaderIndex : function(n){
36800         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36801         return r ? this.getCellIndex(r) : false;
36802     },
36803
36804     findHeaderCell : function(n){
36805         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36806         return r ? r : false;
36807     },
36808
36809     findRowIndex : function(n){
36810         if(!n){
36811             return false;
36812         }
36813         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36814         return r ? r.rowIndex : false;
36815     },
36816
36817     findCellIndex : function(node){
36818         var stop = this.el.dom;
36819         while(node && node != stop){
36820             if(this.findRE.test(node.className)){
36821                 return this.getCellIndex(node);
36822             }
36823             node = node.parentNode;
36824         }
36825         return false;
36826     },
36827
36828     getColumnId : function(index){
36829         return this.cm.getColumnId(index);
36830     },
36831
36832     getSplitters : function()
36833     {
36834         if(this.splitterSelector){
36835            return Roo.DomQuery.select(this.splitterSelector);
36836         }else{
36837             return null;
36838       }
36839     },
36840
36841     getSplitter : function(index){
36842         return this.getSplitters()[index];
36843     },
36844
36845     onRowOver : function(e, t){
36846         var row;
36847         if((row = this.findRowIndex(t)) !== false){
36848             this.getRowComposite(row).addClass("x-grid-row-over");
36849         }
36850     },
36851
36852     onRowOut : function(e, t){
36853         var row;
36854         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36855             this.getRowComposite(row).removeClass("x-grid-row-over");
36856         }
36857     },
36858
36859     renderHeaders : function(){
36860         var cm = this.cm;
36861         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36862         var cb = [], lb = [], sb = [], lsb = [], p = {};
36863         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36864             p.cellId = "x-grid-hd-0-" + i;
36865             p.splitId = "x-grid-csplit-0-" + i;
36866             p.id = cm.getColumnId(i);
36867             p.title = cm.getColumnTooltip(i) || "";
36868             p.value = cm.getColumnHeader(i) || "";
36869             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36870             if(!cm.isLocked(i)){
36871                 cb[cb.length] = ct.apply(p);
36872                 sb[sb.length] = st.apply(p);
36873             }else{
36874                 lb[lb.length] = ct.apply(p);
36875                 lsb[lsb.length] = st.apply(p);
36876             }
36877         }
36878         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36879                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36880     },
36881
36882     updateHeaders : function(){
36883         var html = this.renderHeaders();
36884         this.lockedHd.update(html[0]);
36885         this.mainHd.update(html[1]);
36886     },
36887
36888     /**
36889      * Focuses the specified row.
36890      * @param {Number} row The row index
36891      */
36892     focusRow : function(row)
36893     {
36894         //Roo.log('GridView.focusRow');
36895         var x = this.scroller.dom.scrollLeft;
36896         this.focusCell(row, 0, false);
36897         this.scroller.dom.scrollLeft = x;
36898     },
36899
36900     /**
36901      * Focuses the specified cell.
36902      * @param {Number} row The row index
36903      * @param {Number} col The column index
36904      * @param {Boolean} hscroll false to disable horizontal scrolling
36905      */
36906     focusCell : function(row, col, hscroll)
36907     {
36908         //Roo.log('GridView.focusCell');
36909         var el = this.ensureVisible(row, col, hscroll);
36910         this.focusEl.alignTo(el, "tl-tl");
36911         if(Roo.isGecko){
36912             this.focusEl.focus();
36913         }else{
36914             this.focusEl.focus.defer(1, this.focusEl);
36915         }
36916     },
36917
36918     /**
36919      * Scrolls the specified cell into view
36920      * @param {Number} row The row index
36921      * @param {Number} col The column index
36922      * @param {Boolean} hscroll false to disable horizontal scrolling
36923      */
36924     ensureVisible : function(row, col, hscroll)
36925     {
36926         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36927         //return null; //disable for testing.
36928         if(typeof row != "number"){
36929             row = row.rowIndex;
36930         }
36931         if(row < 0 && row >= this.ds.getCount()){
36932             return  null;
36933         }
36934         col = (col !== undefined ? col : 0);
36935         var cm = this.grid.colModel;
36936         while(cm.isHidden(col)){
36937             col++;
36938         }
36939
36940         var el = this.getCell(row, col);
36941         if(!el){
36942             return null;
36943         }
36944         var c = this.scroller.dom;
36945
36946         var ctop = parseInt(el.offsetTop, 10);
36947         var cleft = parseInt(el.offsetLeft, 10);
36948         var cbot = ctop + el.offsetHeight;
36949         var cright = cleft + el.offsetWidth;
36950         
36951         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36952         var stop = parseInt(c.scrollTop, 10);
36953         var sleft = parseInt(c.scrollLeft, 10);
36954         var sbot = stop + ch;
36955         var sright = sleft + c.clientWidth;
36956         /*
36957         Roo.log('GridView.ensureVisible:' +
36958                 ' ctop:' + ctop +
36959                 ' c.clientHeight:' + c.clientHeight +
36960                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36961                 ' stop:' + stop +
36962                 ' cbot:' + cbot +
36963                 ' sbot:' + sbot +
36964                 ' ch:' + ch  
36965                 );
36966         */
36967         if(ctop < stop){
36968              c.scrollTop = ctop;
36969             //Roo.log("set scrolltop to ctop DISABLE?");
36970         }else if(cbot > sbot){
36971             //Roo.log("set scrolltop to cbot-ch");
36972             c.scrollTop = cbot-ch;
36973         }
36974         
36975         if(hscroll !== false){
36976             if(cleft < sleft){
36977                 c.scrollLeft = cleft;
36978             }else if(cright > sright){
36979                 c.scrollLeft = cright-c.clientWidth;
36980             }
36981         }
36982          
36983         return el;
36984     },
36985
36986     updateColumns : function(){
36987         this.grid.stopEditing();
36988         var cm = this.grid.colModel, colIds = this.getColumnIds();
36989         //var totalWidth = cm.getTotalWidth();
36990         var pos = 0;
36991         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36992             //if(cm.isHidden(i)) continue;
36993             var w = cm.getColumnWidth(i);
36994             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36995             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36996         }
36997         this.updateSplitters();
36998     },
36999
37000     generateRules : function(cm){
37001         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37002         Roo.util.CSS.removeStyleSheet(rulesId);
37003         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37004             var cid = cm.getColumnId(i);
37005             var align = '';
37006             if(cm.config[i].align){
37007                 align = 'text-align:'+cm.config[i].align+';';
37008             }
37009             var hidden = '';
37010             if(cm.isHidden(i)){
37011                 hidden = 'display:none;';
37012             }
37013             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37014             ruleBuf.push(
37015                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37016                     this.hdSelector, cid, " {\n", align, width, "}\n",
37017                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37018                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37019         }
37020         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37021     },
37022
37023     updateSplitters : function(){
37024         var cm = this.cm, s = this.getSplitters();
37025         if(s){ // splitters not created yet
37026             var pos = 0, locked = true;
37027             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37028                 if(cm.isHidden(i)) continue;
37029                 var w = cm.getColumnWidth(i); // make sure it's a number
37030                 if(!cm.isLocked(i) && locked){
37031                     pos = 0;
37032                     locked = false;
37033                 }
37034                 pos += w;
37035                 s[i].style.left = (pos-this.splitOffset) + "px";
37036             }
37037         }
37038     },
37039
37040     handleHiddenChange : function(colModel, colIndex, hidden){
37041         if(hidden){
37042             this.hideColumn(colIndex);
37043         }else{
37044             this.unhideColumn(colIndex);
37045         }
37046     },
37047
37048     hideColumn : function(colIndex){
37049         var cid = this.getColumnId(colIndex);
37050         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37051         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37052         if(Roo.isSafari){
37053             this.updateHeaders();
37054         }
37055         this.updateSplitters();
37056         this.layout();
37057     },
37058
37059     unhideColumn : function(colIndex){
37060         var cid = this.getColumnId(colIndex);
37061         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37062         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37063
37064         if(Roo.isSafari){
37065             this.updateHeaders();
37066         }
37067         this.updateSplitters();
37068         this.layout();
37069     },
37070
37071     insertRows : function(dm, firstRow, lastRow, isUpdate){
37072         if(firstRow == 0 && lastRow == dm.getCount()-1){
37073             this.refresh();
37074         }else{
37075             if(!isUpdate){
37076                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37077             }
37078             var s = this.getScrollState();
37079             var markup = this.renderRows(firstRow, lastRow);
37080             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37081             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37082             this.restoreScroll(s);
37083             if(!isUpdate){
37084                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37085                 this.syncRowHeights(firstRow, lastRow);
37086                 this.stripeRows(firstRow);
37087                 this.layout();
37088             }
37089         }
37090     },
37091
37092     bufferRows : function(markup, target, index){
37093         var before = null, trows = target.rows, tbody = target.tBodies[0];
37094         if(index < trows.length){
37095             before = trows[index];
37096         }
37097         var b = document.createElement("div");
37098         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37099         var rows = b.firstChild.rows;
37100         for(var i = 0, len = rows.length; i < len; i++){
37101             if(before){
37102                 tbody.insertBefore(rows[0], before);
37103             }else{
37104                 tbody.appendChild(rows[0]);
37105             }
37106         }
37107         b.innerHTML = "";
37108         b = null;
37109     },
37110
37111     deleteRows : function(dm, firstRow, lastRow){
37112         if(dm.getRowCount()<1){
37113             this.fireEvent("beforerefresh", this);
37114             this.mainBody.update("");
37115             this.lockedBody.update("");
37116             this.fireEvent("refresh", this);
37117         }else{
37118             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37119             var bt = this.getBodyTable();
37120             var tbody = bt.firstChild;
37121             var rows = bt.rows;
37122             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37123                 tbody.removeChild(rows[firstRow]);
37124             }
37125             this.stripeRows(firstRow);
37126             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37127         }
37128     },
37129
37130     updateRows : function(dataSource, firstRow, lastRow){
37131         var s = this.getScrollState();
37132         this.refresh();
37133         this.restoreScroll(s);
37134     },
37135
37136     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37137         if(!noRefresh){
37138            this.refresh();
37139         }
37140         this.updateHeaderSortState();
37141     },
37142
37143     getScrollState : function(){
37144         
37145         var sb = this.scroller.dom;
37146         return {left: sb.scrollLeft, top: sb.scrollTop};
37147     },
37148
37149     stripeRows : function(startRow){
37150         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37151             return;
37152         }
37153         startRow = startRow || 0;
37154         var rows = this.getBodyTable().rows;
37155         var lrows = this.getLockedTable().rows;
37156         var cls = ' x-grid-row-alt ';
37157         for(var i = startRow, len = rows.length; i < len; i++){
37158             var row = rows[i], lrow = lrows[i];
37159             var isAlt = ((i+1) % 2 == 0);
37160             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37161             if(isAlt == hasAlt){
37162                 continue;
37163             }
37164             if(isAlt){
37165                 row.className += " x-grid-row-alt";
37166             }else{
37167                 row.className = row.className.replace("x-grid-row-alt", "");
37168             }
37169             if(lrow){
37170                 lrow.className = row.className;
37171             }
37172         }
37173     },
37174
37175     restoreScroll : function(state){
37176         //Roo.log('GridView.restoreScroll');
37177         var sb = this.scroller.dom;
37178         sb.scrollLeft = state.left;
37179         sb.scrollTop = state.top;
37180         this.syncScroll();
37181     },
37182
37183     syncScroll : function(){
37184         //Roo.log('GridView.syncScroll');
37185         var sb = this.scroller.dom;
37186         var sh = this.mainHd.dom;
37187         var bs = this.mainBody.dom;
37188         var lv = this.lockedBody.dom;
37189         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37190         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37191     },
37192
37193     handleScroll : function(e){
37194         this.syncScroll();
37195         var sb = this.scroller.dom;
37196         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37197         e.stopEvent();
37198     },
37199
37200     handleWheel : function(e){
37201         var d = e.getWheelDelta();
37202         this.scroller.dom.scrollTop -= d*22;
37203         // set this here to prevent jumpy scrolling on large tables
37204         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37205         e.stopEvent();
37206     },
37207
37208     renderRows : function(startRow, endRow){
37209         // pull in all the crap needed to render rows
37210         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37211         var colCount = cm.getColumnCount();
37212
37213         if(ds.getCount() < 1){
37214             return ["", ""];
37215         }
37216
37217         // build a map for all the columns
37218         var cs = [];
37219         for(var i = 0; i < colCount; i++){
37220             var name = cm.getDataIndex(i);
37221             cs[i] = {
37222                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37223                 renderer : cm.getRenderer(i),
37224                 id : cm.getColumnId(i),
37225                 locked : cm.isLocked(i)
37226             };
37227         }
37228
37229         startRow = startRow || 0;
37230         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37231
37232         // records to render
37233         var rs = ds.getRange(startRow, endRow);
37234
37235         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37236     },
37237
37238     // As much as I hate to duplicate code, this was branched because FireFox really hates
37239     // [].join("") on strings. The performance difference was substantial enough to
37240     // branch this function
37241     doRender : Roo.isGecko ?
37242             function(cs, rs, ds, startRow, colCount, stripe){
37243                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37244                 // buffers
37245                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37246                 
37247                 var hasListener = this.grid.hasListener('rowclass');
37248                 var rowcfg = {};
37249                 for(var j = 0, len = rs.length; j < len; j++){
37250                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37251                     for(var i = 0; i < colCount; i++){
37252                         c = cs[i];
37253                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37254                         p.id = c.id;
37255                         p.css = p.attr = "";
37256                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37257                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37258                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37259                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37260                         }
37261                         var markup = ct.apply(p);
37262                         if(!c.locked){
37263                             cb+= markup;
37264                         }else{
37265                             lcb+= markup;
37266                         }
37267                     }
37268                     var alt = [];
37269                     if(stripe && ((rowIndex+1) % 2 == 0)){
37270                         alt.push("x-grid-row-alt")
37271                     }
37272                     if(r.dirty){
37273                         alt.push(  " x-grid-dirty-row");
37274                     }
37275                     rp.cells = lcb;
37276                     if(this.getRowClass){
37277                         alt.push(this.getRowClass(r, rowIndex));
37278                     }
37279                     if (hasListener) {
37280                         rowcfg = {
37281                              
37282                             record: r,
37283                             rowIndex : rowIndex,
37284                             rowClass : ''
37285                         }
37286                         this.grid.fireEvent('rowclass', this, rowcfg);
37287                         alt.push(rowcfg.rowClass);
37288                     }
37289                     rp.alt = alt.join(" ");
37290                     lbuf+= rt.apply(rp);
37291                     rp.cells = cb;
37292                     buf+=  rt.apply(rp);
37293                 }
37294                 return [lbuf, buf];
37295             } :
37296             function(cs, rs, ds, startRow, colCount, stripe){
37297                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37298                 // buffers
37299                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37300                 var hasListener = this.grid.hasListener('rowclass');
37301  
37302                 var rowcfg = {};
37303                 for(var j = 0, len = rs.length; j < len; j++){
37304                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37305                     for(var i = 0; i < colCount; i++){
37306                         c = cs[i];
37307                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37308                         p.id = c.id;
37309                         p.css = p.attr = "";
37310                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37311                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37312                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37313                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37314                         }
37315                         
37316                         var markup = ct.apply(p);
37317                         if(!c.locked){
37318                             cb[cb.length] = markup;
37319                         }else{
37320                             lcb[lcb.length] = markup;
37321                         }
37322                     }
37323                     var alt = [];
37324                     if(stripe && ((rowIndex+1) % 2 == 0)){
37325                         alt.push( "x-grid-row-alt");
37326                     }
37327                     if(r.dirty){
37328                         alt.push(" x-grid-dirty-row");
37329                     }
37330                     rp.cells = lcb;
37331                     if(this.getRowClass){
37332                         alt.push( this.getRowClass(r, rowIndex));
37333                     }
37334                     if (hasListener) {
37335                         rowcfg = {
37336                              
37337                             record: r,
37338                             rowIndex : rowIndex,
37339                             rowClass : ''
37340                         }
37341                         this.grid.fireEvent('rowclass', this, rowcfg);
37342                         alt.push(rowcfg.rowClass);
37343                     }
37344                     rp.alt = alt.join(" ");
37345                     rp.cells = lcb.join("");
37346                     lbuf[lbuf.length] = rt.apply(rp);
37347                     rp.cells = cb.join("");
37348                     buf[buf.length] =  rt.apply(rp);
37349                 }
37350                 return [lbuf.join(""), buf.join("")];
37351             },
37352
37353     renderBody : function(){
37354         var markup = this.renderRows();
37355         var bt = this.templates.body;
37356         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37357     },
37358
37359     /**
37360      * Refreshes the grid
37361      * @param {Boolean} headersToo
37362      */
37363     refresh : function(headersToo){
37364         this.fireEvent("beforerefresh", this);
37365         this.grid.stopEditing();
37366         var result = this.renderBody();
37367         this.lockedBody.update(result[0]);
37368         this.mainBody.update(result[1]);
37369         if(headersToo === true){
37370             this.updateHeaders();
37371             this.updateColumns();
37372             this.updateSplitters();
37373             this.updateHeaderSortState();
37374         }
37375         this.syncRowHeights();
37376         this.layout();
37377         this.fireEvent("refresh", this);
37378     },
37379
37380     handleColumnMove : function(cm, oldIndex, newIndex){
37381         this.indexMap = null;
37382         var s = this.getScrollState();
37383         this.refresh(true);
37384         this.restoreScroll(s);
37385         this.afterMove(newIndex);
37386     },
37387
37388     afterMove : function(colIndex){
37389         if(this.enableMoveAnim && Roo.enableFx){
37390             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37391         }
37392         // if multisort - fix sortOrder, and reload..
37393         if (this.grid.dataSource.multiSort) {
37394             // the we can call sort again..
37395             var dm = this.grid.dataSource;
37396             var cm = this.grid.colModel;
37397             var so = [];
37398             for(var i = 0; i < cm.config.length; i++ ) {
37399                 
37400                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37401                     continue; // dont' bother, it's not in sort list or being set.
37402                 }
37403                 
37404                 so.push(cm.config[i].dataIndex);
37405             };
37406             dm.sortOrder = so;
37407             dm.load(dm.lastOptions);
37408             
37409             
37410         }
37411         
37412     },
37413
37414     updateCell : function(dm, rowIndex, dataIndex){
37415         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37416         if(typeof colIndex == "undefined"){ // not present in grid
37417             return;
37418         }
37419         var cm = this.grid.colModel;
37420         var cell = this.getCell(rowIndex, colIndex);
37421         var cellText = this.getCellText(rowIndex, colIndex);
37422
37423         var p = {
37424             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37425             id : cm.getColumnId(colIndex),
37426             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37427         };
37428         var renderer = cm.getRenderer(colIndex);
37429         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37430         if(typeof val == "undefined" || val === "") val = "&#160;";
37431         cellText.innerHTML = val;
37432         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37433         this.syncRowHeights(rowIndex, rowIndex);
37434     },
37435
37436     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37437         var maxWidth = 0;
37438         if(this.grid.autoSizeHeaders){
37439             var h = this.getHeaderCellMeasure(colIndex);
37440             maxWidth = Math.max(maxWidth, h.scrollWidth);
37441         }
37442         var tb, index;
37443         if(this.cm.isLocked(colIndex)){
37444             tb = this.getLockedTable();
37445             index = colIndex;
37446         }else{
37447             tb = this.getBodyTable();
37448             index = colIndex - this.cm.getLockedCount();
37449         }
37450         if(tb && tb.rows){
37451             var rows = tb.rows;
37452             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37453             for(var i = 0; i < stopIndex; i++){
37454                 var cell = rows[i].childNodes[index].firstChild;
37455                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37456             }
37457         }
37458         return maxWidth + /*margin for error in IE*/ 5;
37459     },
37460     /**
37461      * Autofit a column to its content.
37462      * @param {Number} colIndex
37463      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37464      */
37465      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37466          if(this.cm.isHidden(colIndex)){
37467              return; // can't calc a hidden column
37468          }
37469         if(forceMinSize){
37470             var cid = this.cm.getColumnId(colIndex);
37471             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37472            if(this.grid.autoSizeHeaders){
37473                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37474            }
37475         }
37476         var newWidth = this.calcColumnWidth(colIndex);
37477         this.cm.setColumnWidth(colIndex,
37478             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37479         if(!suppressEvent){
37480             this.grid.fireEvent("columnresize", colIndex, newWidth);
37481         }
37482     },
37483
37484     /**
37485      * Autofits all columns to their content and then expands to fit any extra space in the grid
37486      */
37487      autoSizeColumns : function(){
37488         var cm = this.grid.colModel;
37489         var colCount = cm.getColumnCount();
37490         for(var i = 0; i < colCount; i++){
37491             this.autoSizeColumn(i, true, true);
37492         }
37493         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37494             this.fitColumns();
37495         }else{
37496             this.updateColumns();
37497             this.layout();
37498         }
37499     },
37500
37501     /**
37502      * Autofits all columns to the grid's width proportionate with their current size
37503      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37504      */
37505     fitColumns : function(reserveScrollSpace){
37506         var cm = this.grid.colModel;
37507         var colCount = cm.getColumnCount();
37508         var cols = [];
37509         var width = 0;
37510         var i, w;
37511         for (i = 0; i < colCount; i++){
37512             if(!cm.isHidden(i) && !cm.isFixed(i)){
37513                 w = cm.getColumnWidth(i);
37514                 cols.push(i);
37515                 cols.push(w);
37516                 width += w;
37517             }
37518         }
37519         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37520         if(reserveScrollSpace){
37521             avail -= 17;
37522         }
37523         var frac = (avail - cm.getTotalWidth())/width;
37524         while (cols.length){
37525             w = cols.pop();
37526             i = cols.pop();
37527             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37528         }
37529         this.updateColumns();
37530         this.layout();
37531     },
37532
37533     onRowSelect : function(rowIndex){
37534         var row = this.getRowComposite(rowIndex);
37535         row.addClass("x-grid-row-selected");
37536     },
37537
37538     onRowDeselect : function(rowIndex){
37539         var row = this.getRowComposite(rowIndex);
37540         row.removeClass("x-grid-row-selected");
37541     },
37542
37543     onCellSelect : function(row, col){
37544         var cell = this.getCell(row, col);
37545         if(cell){
37546             Roo.fly(cell).addClass("x-grid-cell-selected");
37547         }
37548     },
37549
37550     onCellDeselect : function(row, col){
37551         var cell = this.getCell(row, col);
37552         if(cell){
37553             Roo.fly(cell).removeClass("x-grid-cell-selected");
37554         }
37555     },
37556
37557     updateHeaderSortState : function(){
37558         
37559         // sort state can be single { field: xxx, direction : yyy}
37560         // or   { xxx=>ASC , yyy : DESC ..... }
37561         
37562         var mstate = {};
37563         if (!this.ds.multiSort) { 
37564             var state = this.ds.getSortState();
37565             if(!state){
37566                 return;
37567             }
37568             mstate[state.field] = state.direction;
37569             // FIXME... - this is not used here.. but might be elsewhere..
37570             this.sortState = state;
37571             
37572         } else {
37573             mstate = this.ds.sortToggle;
37574         }
37575         //remove existing sort classes..
37576         
37577         var sc = this.sortClasses;
37578         var hds = this.el.select(this.headerSelector).removeClass(sc);
37579         
37580         for(var f in mstate) {
37581         
37582             var sortColumn = this.cm.findColumnIndex(f);
37583             
37584             if(sortColumn != -1){
37585                 var sortDir = mstate[f];        
37586                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37587             }
37588         }
37589         
37590          
37591         
37592     },
37593
37594
37595     handleHeaderClick : function(g, index){
37596         if(this.headersDisabled){
37597             return;
37598         }
37599         var dm = g.dataSource, cm = g.colModel;
37600         if(!cm.isSortable(index)){
37601             return;
37602         }
37603         g.stopEditing();
37604         
37605         if (dm.multiSort) {
37606             // update the sortOrder
37607             var so = [];
37608             for(var i = 0; i < cm.config.length; i++ ) {
37609                 
37610                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37611                     continue; // dont' bother, it's not in sort list or being set.
37612                 }
37613                 
37614                 so.push(cm.config[i].dataIndex);
37615             };
37616             dm.sortOrder = so;
37617         }
37618         
37619         
37620         dm.sort(cm.getDataIndex(index));
37621     },
37622
37623
37624     destroy : function(){
37625         if(this.colMenu){
37626             this.colMenu.removeAll();
37627             Roo.menu.MenuMgr.unregister(this.colMenu);
37628             this.colMenu.getEl().remove();
37629             delete this.colMenu;
37630         }
37631         if(this.hmenu){
37632             this.hmenu.removeAll();
37633             Roo.menu.MenuMgr.unregister(this.hmenu);
37634             this.hmenu.getEl().remove();
37635             delete this.hmenu;
37636         }
37637         if(this.grid.enableColumnMove){
37638             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37639             if(dds){
37640                 for(var dd in dds){
37641                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37642                         var elid = dds[dd].dragElId;
37643                         dds[dd].unreg();
37644                         Roo.get(elid).remove();
37645                     } else if(dds[dd].config.isTarget){
37646                         dds[dd].proxyTop.remove();
37647                         dds[dd].proxyBottom.remove();
37648                         dds[dd].unreg();
37649                     }
37650                     if(Roo.dd.DDM.locationCache[dd]){
37651                         delete Roo.dd.DDM.locationCache[dd];
37652                     }
37653                 }
37654                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37655             }
37656         }
37657         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37658         this.bind(null, null);
37659         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37660     },
37661
37662     handleLockChange : function(){
37663         this.refresh(true);
37664     },
37665
37666     onDenyColumnLock : function(){
37667
37668     },
37669
37670     onDenyColumnHide : function(){
37671
37672     },
37673
37674     handleHdMenuClick : function(item){
37675         var index = this.hdCtxIndex;
37676         var cm = this.cm, ds = this.ds;
37677         switch(item.id){
37678             case "asc":
37679                 ds.sort(cm.getDataIndex(index), "ASC");
37680                 break;
37681             case "desc":
37682                 ds.sort(cm.getDataIndex(index), "DESC");
37683                 break;
37684             case "lock":
37685                 var lc = cm.getLockedCount();
37686                 if(cm.getColumnCount(true) <= lc+1){
37687                     this.onDenyColumnLock();
37688                     return;
37689                 }
37690                 if(lc != index){
37691                     cm.setLocked(index, true, true);
37692                     cm.moveColumn(index, lc);
37693                     this.grid.fireEvent("columnmove", index, lc);
37694                 }else{
37695                     cm.setLocked(index, true);
37696                 }
37697             break;
37698             case "unlock":
37699                 var lc = cm.getLockedCount();
37700                 if((lc-1) != index){
37701                     cm.setLocked(index, false, true);
37702                     cm.moveColumn(index, lc-1);
37703                     this.grid.fireEvent("columnmove", index, lc-1);
37704                 }else{
37705                     cm.setLocked(index, false);
37706                 }
37707             break;
37708             default:
37709                 index = cm.getIndexById(item.id.substr(4));
37710                 if(index != -1){
37711                     if(item.checked && cm.getColumnCount(true) <= 1){
37712                         this.onDenyColumnHide();
37713                         return false;
37714                     }
37715                     cm.setHidden(index, item.checked);
37716                 }
37717         }
37718         return true;
37719     },
37720
37721     beforeColMenuShow : function(){
37722         var cm = this.cm,  colCount = cm.getColumnCount();
37723         this.colMenu.removeAll();
37724         for(var i = 0; i < colCount; i++){
37725             this.colMenu.add(new Roo.menu.CheckItem({
37726                 id: "col-"+cm.getColumnId(i),
37727                 text: cm.getColumnHeader(i),
37728                 checked: !cm.isHidden(i),
37729                 hideOnClick:false
37730             }));
37731         }
37732     },
37733
37734     handleHdCtx : function(g, index, e){
37735         e.stopEvent();
37736         var hd = this.getHeaderCell(index);
37737         this.hdCtxIndex = index;
37738         var ms = this.hmenu.items, cm = this.cm;
37739         ms.get("asc").setDisabled(!cm.isSortable(index));
37740         ms.get("desc").setDisabled(!cm.isSortable(index));
37741         if(this.grid.enableColLock !== false){
37742             ms.get("lock").setDisabled(cm.isLocked(index));
37743             ms.get("unlock").setDisabled(!cm.isLocked(index));
37744         }
37745         this.hmenu.show(hd, "tl-bl");
37746     },
37747
37748     handleHdOver : function(e){
37749         var hd = this.findHeaderCell(e.getTarget());
37750         if(hd && !this.headersDisabled){
37751             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37752                this.fly(hd).addClass("x-grid-hd-over");
37753             }
37754         }
37755     },
37756
37757     handleHdOut : function(e){
37758         var hd = this.findHeaderCell(e.getTarget());
37759         if(hd){
37760             this.fly(hd).removeClass("x-grid-hd-over");
37761         }
37762     },
37763
37764     handleSplitDblClick : function(e, t){
37765         var i = this.getCellIndex(t);
37766         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37767             this.autoSizeColumn(i, true);
37768             this.layout();
37769         }
37770     },
37771
37772     render : function(){
37773
37774         var cm = this.cm;
37775         var colCount = cm.getColumnCount();
37776
37777         if(this.grid.monitorWindowResize === true){
37778             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37779         }
37780         var header = this.renderHeaders();
37781         var body = this.templates.body.apply({rows:""});
37782         var html = this.templates.master.apply({
37783             lockedBody: body,
37784             body: body,
37785             lockedHeader: header[0],
37786             header: header[1]
37787         });
37788
37789         //this.updateColumns();
37790
37791         this.grid.getGridEl().dom.innerHTML = html;
37792
37793         this.initElements();
37794         
37795         // a kludge to fix the random scolling effect in webkit
37796         this.el.on("scroll", function() {
37797             this.el.dom.scrollTop=0; // hopefully not recursive..
37798         },this);
37799
37800         this.scroller.on("scroll", this.handleScroll, this);
37801         this.lockedBody.on("mousewheel", this.handleWheel, this);
37802         this.mainBody.on("mousewheel", this.handleWheel, this);
37803
37804         this.mainHd.on("mouseover", this.handleHdOver, this);
37805         this.mainHd.on("mouseout", this.handleHdOut, this);
37806         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37807                 {delegate: "."+this.splitClass});
37808
37809         this.lockedHd.on("mouseover", this.handleHdOver, this);
37810         this.lockedHd.on("mouseout", this.handleHdOut, this);
37811         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37812                 {delegate: "."+this.splitClass});
37813
37814         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37815             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37816         }
37817
37818         this.updateSplitters();
37819
37820         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37821             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37822             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37823         }
37824
37825         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37826             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37827             this.hmenu.add(
37828                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37829                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37830             );
37831             if(this.grid.enableColLock !== false){
37832                 this.hmenu.add('-',
37833                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37834                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37835                 );
37836             }
37837             if(this.grid.enableColumnHide !== false){
37838
37839                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37840                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37841                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37842
37843                 this.hmenu.add('-',
37844                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37845                 );
37846             }
37847             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37848
37849             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37850         }
37851
37852         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37853             this.dd = new Roo.grid.GridDragZone(this.grid, {
37854                 ddGroup : this.grid.ddGroup || 'GridDD'
37855             });
37856             
37857         }
37858
37859         /*
37860         for(var i = 0; i < colCount; i++){
37861             if(cm.isHidden(i)){
37862                 this.hideColumn(i);
37863             }
37864             if(cm.config[i].align){
37865                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37866                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37867             }
37868         }*/
37869         
37870         this.updateHeaderSortState();
37871
37872         this.beforeInitialResize();
37873         this.layout(true);
37874
37875         // two part rendering gives faster view to the user
37876         this.renderPhase2.defer(1, this);
37877     },
37878
37879     renderPhase2 : function(){
37880         // render the rows now
37881         this.refresh();
37882         if(this.grid.autoSizeColumns){
37883             this.autoSizeColumns();
37884         }
37885     },
37886
37887     beforeInitialResize : function(){
37888
37889     },
37890
37891     onColumnSplitterMoved : function(i, w){
37892         this.userResized = true;
37893         var cm = this.grid.colModel;
37894         cm.setColumnWidth(i, w, true);
37895         var cid = cm.getColumnId(i);
37896         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37897         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37898         this.updateSplitters();
37899         this.layout();
37900         this.grid.fireEvent("columnresize", i, w);
37901     },
37902
37903     syncRowHeights : function(startIndex, endIndex){
37904         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37905             startIndex = startIndex || 0;
37906             var mrows = this.getBodyTable().rows;
37907             var lrows = this.getLockedTable().rows;
37908             var len = mrows.length-1;
37909             endIndex = Math.min(endIndex || len, len);
37910             for(var i = startIndex; i <= endIndex; i++){
37911                 var m = mrows[i], l = lrows[i];
37912                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37913                 m.style.height = l.style.height = h + "px";
37914             }
37915         }
37916     },
37917
37918     layout : function(initialRender, is2ndPass){
37919         var g = this.grid;
37920         var auto = g.autoHeight;
37921         var scrollOffset = 16;
37922         var c = g.getGridEl(), cm = this.cm,
37923                 expandCol = g.autoExpandColumn,
37924                 gv = this;
37925         //c.beginMeasure();
37926
37927         if(!c.dom.offsetWidth){ // display:none?
37928             if(initialRender){
37929                 this.lockedWrap.show();
37930                 this.mainWrap.show();
37931             }
37932             return;
37933         }
37934
37935         var hasLock = this.cm.isLocked(0);
37936
37937         var tbh = this.headerPanel.getHeight();
37938         var bbh = this.footerPanel.getHeight();
37939
37940         if(auto){
37941             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37942             var newHeight = ch + c.getBorderWidth("tb");
37943             if(g.maxHeight){
37944                 newHeight = Math.min(g.maxHeight, newHeight);
37945             }
37946             c.setHeight(newHeight);
37947         }
37948
37949         if(g.autoWidth){
37950             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37951         }
37952
37953         var s = this.scroller;
37954
37955         var csize = c.getSize(true);
37956
37957         this.el.setSize(csize.width, csize.height);
37958
37959         this.headerPanel.setWidth(csize.width);
37960         this.footerPanel.setWidth(csize.width);
37961
37962         var hdHeight = this.mainHd.getHeight();
37963         var vw = csize.width;
37964         var vh = csize.height - (tbh + bbh);
37965
37966         s.setSize(vw, vh);
37967
37968         var bt = this.getBodyTable();
37969         var ltWidth = hasLock ?
37970                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37971
37972         var scrollHeight = bt.offsetHeight;
37973         var scrollWidth = ltWidth + bt.offsetWidth;
37974         var vscroll = false, hscroll = false;
37975
37976         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37977
37978         var lw = this.lockedWrap, mw = this.mainWrap;
37979         var lb = this.lockedBody, mb = this.mainBody;
37980
37981         setTimeout(function(){
37982             var t = s.dom.offsetTop;
37983             var w = s.dom.clientWidth,
37984                 h = s.dom.clientHeight;
37985
37986             lw.setTop(t);
37987             lw.setSize(ltWidth, h);
37988
37989             mw.setLeftTop(ltWidth, t);
37990             mw.setSize(w-ltWidth, h);
37991
37992             lb.setHeight(h-hdHeight);
37993             mb.setHeight(h-hdHeight);
37994
37995             if(is2ndPass !== true && !gv.userResized && expandCol){
37996                 // high speed resize without full column calculation
37997                 
37998                 var ci = cm.getIndexById(expandCol);
37999                 if (ci < 0) {
38000                     ci = cm.findColumnIndex(expandCol);
38001                 }
38002                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38003                 var expandId = cm.getColumnId(ci);
38004                 var  tw = cm.getTotalWidth(false);
38005                 var currentWidth = cm.getColumnWidth(ci);
38006                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38007                 if(currentWidth != cw){
38008                     cm.setColumnWidth(ci, cw, true);
38009                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38010                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38011                     gv.updateSplitters();
38012                     gv.layout(false, true);
38013                 }
38014             }
38015
38016             if(initialRender){
38017                 lw.show();
38018                 mw.show();
38019             }
38020             //c.endMeasure();
38021         }, 10);
38022     },
38023
38024     onWindowResize : function(){
38025         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38026             return;
38027         }
38028         this.layout();
38029     },
38030
38031     appendFooter : function(parentEl){
38032         return null;
38033     },
38034
38035     sortAscText : "Sort Ascending",
38036     sortDescText : "Sort Descending",
38037     lockText : "Lock Column",
38038     unlockText : "Unlock Column",
38039     columnsText : "Columns"
38040 });
38041
38042
38043 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38044     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38045     this.proxy.el.addClass('x-grid3-col-dd');
38046 };
38047
38048 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38049     handleMouseDown : function(e){
38050
38051     },
38052
38053     callHandleMouseDown : function(e){
38054         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38055     }
38056 });
38057 /*
38058  * Based on:
38059  * Ext JS Library 1.1.1
38060  * Copyright(c) 2006-2007, Ext JS, LLC.
38061  *
38062  * Originally Released Under LGPL - original licence link has changed is not relivant.
38063  *
38064  * Fork - LGPL
38065  * <script type="text/javascript">
38066  */
38067  
38068 // private
38069 // This is a support class used internally by the Grid components
38070 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38071     this.grid = grid;
38072     this.view = grid.getView();
38073     this.proxy = this.view.resizeProxy;
38074     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38075         "gridSplitters" + this.grid.getGridEl().id, {
38076         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38077     });
38078     this.setHandleElId(Roo.id(hd));
38079     this.setOuterHandleElId(Roo.id(hd2));
38080     this.scroll = false;
38081 };
38082 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38083     fly: Roo.Element.fly,
38084
38085     b4StartDrag : function(x, y){
38086         this.view.headersDisabled = true;
38087         this.proxy.setHeight(this.view.mainWrap.getHeight());
38088         var w = this.cm.getColumnWidth(this.cellIndex);
38089         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38090         this.resetConstraints();
38091         this.setXConstraint(minw, 1000);
38092         this.setYConstraint(0, 0);
38093         this.minX = x - minw;
38094         this.maxX = x + 1000;
38095         this.startPos = x;
38096         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38097     },
38098
38099
38100     handleMouseDown : function(e){
38101         ev = Roo.EventObject.setEvent(e);
38102         var t = this.fly(ev.getTarget());
38103         if(t.hasClass("x-grid-split")){
38104             this.cellIndex = this.view.getCellIndex(t.dom);
38105             this.split = t.dom;
38106             this.cm = this.grid.colModel;
38107             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38108                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38109             }
38110         }
38111     },
38112
38113     endDrag : function(e){
38114         this.view.headersDisabled = false;
38115         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38116         var diff = endX - this.startPos;
38117         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38118     },
38119
38120     autoOffset : function(){
38121         this.setDelta(0,0);
38122     }
38123 });/*
38124  * Based on:
38125  * Ext JS Library 1.1.1
38126  * Copyright(c) 2006-2007, Ext JS, LLC.
38127  *
38128  * Originally Released Under LGPL - original licence link has changed is not relivant.
38129  *
38130  * Fork - LGPL
38131  * <script type="text/javascript">
38132  */
38133  
38134 // private
38135 // This is a support class used internally by the Grid components
38136 Roo.grid.GridDragZone = function(grid, config){
38137     this.view = grid.getView();
38138     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38139     if(this.view.lockedBody){
38140         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38141         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38142     }
38143     this.scroll = false;
38144     this.grid = grid;
38145     this.ddel = document.createElement('div');
38146     this.ddel.className = 'x-grid-dd-wrap';
38147 };
38148
38149 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38150     ddGroup : "GridDD",
38151
38152     getDragData : function(e){
38153         var t = Roo.lib.Event.getTarget(e);
38154         var rowIndex = this.view.findRowIndex(t);
38155         var sm = this.grid.selModel;
38156             
38157         //Roo.log(rowIndex);
38158         
38159         if (sm.getSelectedCell) {
38160             // cell selection..
38161             if (!sm.getSelectedCell()) {
38162                 return false;
38163             }
38164             if (rowIndex != sm.getSelectedCell()[0]) {
38165                 return false;
38166             }
38167         
38168         }
38169         
38170         if(rowIndex !== false){
38171             
38172             // if editorgrid.. 
38173             
38174             
38175             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38176                
38177             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38178               //  
38179             //}
38180             if (e.hasModifier()){
38181                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38182             }
38183             
38184             Roo.log("getDragData");
38185             
38186             return {
38187                 grid: this.grid,
38188                 ddel: this.ddel,
38189                 rowIndex: rowIndex,
38190                 selections:sm.getSelections ? sm.getSelections() : (
38191                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38192                 )
38193             };
38194         }
38195         return false;
38196     },
38197
38198     onInitDrag : function(e){
38199         var data = this.dragData;
38200         this.ddel.innerHTML = this.grid.getDragDropText();
38201         this.proxy.update(this.ddel);
38202         // fire start drag?
38203     },
38204
38205     afterRepair : function(){
38206         this.dragging = false;
38207     },
38208
38209     getRepairXY : function(e, data){
38210         return false;
38211     },
38212
38213     onEndDrag : function(data, e){
38214         // fire end drag?
38215     },
38216
38217     onValidDrop : function(dd, e, id){
38218         // fire drag drop?
38219         this.hideProxy();
38220     },
38221
38222     beforeInvalidDrop : function(e, id){
38223
38224     }
38225 });/*
38226  * Based on:
38227  * Ext JS Library 1.1.1
38228  * Copyright(c) 2006-2007, Ext JS, LLC.
38229  *
38230  * Originally Released Under LGPL - original licence link has changed is not relivant.
38231  *
38232  * Fork - LGPL
38233  * <script type="text/javascript">
38234  */
38235  
38236
38237 /**
38238  * @class Roo.grid.ColumnModel
38239  * @extends Roo.util.Observable
38240  * This is the default implementation of a ColumnModel used by the Grid. It defines
38241  * the columns in the grid.
38242  * <br>Usage:<br>
38243  <pre><code>
38244  var colModel = new Roo.grid.ColumnModel([
38245         {header: "Ticker", width: 60, sortable: true, locked: true},
38246         {header: "Company Name", width: 150, sortable: true},
38247         {header: "Market Cap.", width: 100, sortable: true},
38248         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38249         {header: "Employees", width: 100, sortable: true, resizable: false}
38250  ]);
38251  </code></pre>
38252  * <p>
38253  
38254  * The config options listed for this class are options which may appear in each
38255  * individual column definition.
38256  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38257  * @constructor
38258  * @param {Object} config An Array of column config objects. See this class's
38259  * config objects for details.
38260 */
38261 Roo.grid.ColumnModel = function(config){
38262         /**
38263      * The config passed into the constructor
38264      */
38265     this.config = config;
38266     this.lookup = {};
38267
38268     // if no id, create one
38269     // if the column does not have a dataIndex mapping,
38270     // map it to the order it is in the config
38271     for(var i = 0, len = config.length; i < len; i++){
38272         var c = config[i];
38273         if(typeof c.dataIndex == "undefined"){
38274             c.dataIndex = i;
38275         }
38276         if(typeof c.renderer == "string"){
38277             c.renderer = Roo.util.Format[c.renderer];
38278         }
38279         if(typeof c.id == "undefined"){
38280             c.id = Roo.id();
38281         }
38282         if(c.editor && c.editor.xtype){
38283             c.editor  = Roo.factory(c.editor, Roo.grid);
38284         }
38285         if(c.editor && c.editor.isFormField){
38286             c.editor = new Roo.grid.GridEditor(c.editor);
38287         }
38288         this.lookup[c.id] = c;
38289     }
38290
38291     /**
38292      * The width of columns which have no width specified (defaults to 100)
38293      * @type Number
38294      */
38295     this.defaultWidth = 100;
38296
38297     /**
38298      * Default sortable of columns which have no sortable specified (defaults to false)
38299      * @type Boolean
38300      */
38301     this.defaultSortable = false;
38302
38303     this.addEvents({
38304         /**
38305              * @event widthchange
38306              * Fires when the width of a column changes.
38307              * @param {ColumnModel} this
38308              * @param {Number} columnIndex The column index
38309              * @param {Number} newWidth The new width
38310              */
38311             "widthchange": true,
38312         /**
38313              * @event headerchange
38314              * Fires when the text of a header changes.
38315              * @param {ColumnModel} this
38316              * @param {Number} columnIndex The column index
38317              * @param {Number} newText The new header text
38318              */
38319             "headerchange": true,
38320         /**
38321              * @event hiddenchange
38322              * Fires when a column is hidden or "unhidden".
38323              * @param {ColumnModel} this
38324              * @param {Number} columnIndex The column index
38325              * @param {Boolean} hidden true if hidden, false otherwise
38326              */
38327             "hiddenchange": true,
38328             /**
38329          * @event columnmoved
38330          * Fires when a column is moved.
38331          * @param {ColumnModel} this
38332          * @param {Number} oldIndex
38333          * @param {Number} newIndex
38334          */
38335         "columnmoved" : true,
38336         /**
38337          * @event columlockchange
38338          * Fires when a column's locked state is changed
38339          * @param {ColumnModel} this
38340          * @param {Number} colIndex
38341          * @param {Boolean} locked true if locked
38342          */
38343         "columnlockchange" : true
38344     });
38345     Roo.grid.ColumnModel.superclass.constructor.call(this);
38346 };
38347 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38348     /**
38349      * @cfg {String} header The header text to display in the Grid view.
38350      */
38351     /**
38352      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38353      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38354      * specified, the column's index is used as an index into the Record's data Array.
38355      */
38356     /**
38357      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38358      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38359      */
38360     /**
38361      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38362      * Defaults to the value of the {@link #defaultSortable} property.
38363      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38364      */
38365     /**
38366      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38367      */
38368     /**
38369      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38370      */
38371     /**
38372      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38373      */
38374     /**
38375      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38376      */
38377     /**
38378      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38379      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38380      * default renderer uses the raw data value.
38381      */
38382        /**
38383      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38384      */
38385     /**
38386      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38387      */
38388
38389     /**
38390      * Returns the id of the column at the specified index.
38391      * @param {Number} index The column index
38392      * @return {String} the id
38393      */
38394     getColumnId : function(index){
38395         return this.config[index].id;
38396     },
38397
38398     /**
38399      * Returns the column for a specified id.
38400      * @param {String} id The column id
38401      * @return {Object} the column
38402      */
38403     getColumnById : function(id){
38404         return this.lookup[id];
38405     },
38406
38407     
38408     /**
38409      * Returns the column for a specified dataIndex.
38410      * @param {String} dataIndex The column dataIndex
38411      * @return {Object|Boolean} the column or false if not found
38412      */
38413     getColumnByDataIndex: function(dataIndex){
38414         var index = this.findColumnIndex(dataIndex);
38415         return index > -1 ? this.config[index] : false;
38416     },
38417     
38418     /**
38419      * Returns the index for a specified column id.
38420      * @param {String} id The column id
38421      * @return {Number} the index, or -1 if not found
38422      */
38423     getIndexById : function(id){
38424         for(var i = 0, len = this.config.length; i < len; i++){
38425             if(this.config[i].id == id){
38426                 return i;
38427             }
38428         }
38429         return -1;
38430     },
38431     
38432     /**
38433      * Returns the index for a specified column dataIndex.
38434      * @param {String} dataIndex The column dataIndex
38435      * @return {Number} the index, or -1 if not found
38436      */
38437     
38438     findColumnIndex : function(dataIndex){
38439         for(var i = 0, len = this.config.length; i < len; i++){
38440             if(this.config[i].dataIndex == dataIndex){
38441                 return i;
38442             }
38443         }
38444         return -1;
38445     },
38446     
38447     
38448     moveColumn : function(oldIndex, newIndex){
38449         var c = this.config[oldIndex];
38450         this.config.splice(oldIndex, 1);
38451         this.config.splice(newIndex, 0, c);
38452         this.dataMap = null;
38453         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38454     },
38455
38456     isLocked : function(colIndex){
38457         return this.config[colIndex].locked === true;
38458     },
38459
38460     setLocked : function(colIndex, value, suppressEvent){
38461         if(this.isLocked(colIndex) == value){
38462             return;
38463         }
38464         this.config[colIndex].locked = value;
38465         if(!suppressEvent){
38466             this.fireEvent("columnlockchange", this, colIndex, value);
38467         }
38468     },
38469
38470     getTotalLockedWidth : function(){
38471         var totalWidth = 0;
38472         for(var i = 0; i < this.config.length; i++){
38473             if(this.isLocked(i) && !this.isHidden(i)){
38474                 this.totalWidth += this.getColumnWidth(i);
38475             }
38476         }
38477         return totalWidth;
38478     },
38479
38480     getLockedCount : function(){
38481         for(var i = 0, len = this.config.length; i < len; i++){
38482             if(!this.isLocked(i)){
38483                 return i;
38484             }
38485         }
38486     },
38487
38488     /**
38489      * Returns the number of columns.
38490      * @return {Number}
38491      */
38492     getColumnCount : function(visibleOnly){
38493         if(visibleOnly === true){
38494             var c = 0;
38495             for(var i = 0, len = this.config.length; i < len; i++){
38496                 if(!this.isHidden(i)){
38497                     c++;
38498                 }
38499             }
38500             return c;
38501         }
38502         return this.config.length;
38503     },
38504
38505     /**
38506      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38507      * @param {Function} fn
38508      * @param {Object} scope (optional)
38509      * @return {Array} result
38510      */
38511     getColumnsBy : function(fn, scope){
38512         var r = [];
38513         for(var i = 0, len = this.config.length; i < len; i++){
38514             var c = this.config[i];
38515             if(fn.call(scope||this, c, i) === true){
38516                 r[r.length] = c;
38517             }
38518         }
38519         return r;
38520     },
38521
38522     /**
38523      * Returns true if the specified column is sortable.
38524      * @param {Number} col The column index
38525      * @return {Boolean}
38526      */
38527     isSortable : function(col){
38528         if(typeof this.config[col].sortable == "undefined"){
38529             return this.defaultSortable;
38530         }
38531         return this.config[col].sortable;
38532     },
38533
38534     /**
38535      * Returns the rendering (formatting) function defined for the column.
38536      * @param {Number} col The column index.
38537      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38538      */
38539     getRenderer : function(col){
38540         if(!this.config[col].renderer){
38541             return Roo.grid.ColumnModel.defaultRenderer;
38542         }
38543         return this.config[col].renderer;
38544     },
38545
38546     /**
38547      * Sets the rendering (formatting) function for a column.
38548      * @param {Number} col The column index
38549      * @param {Function} fn The function to use to process the cell's raw data
38550      * to return HTML markup for the grid view. The render function is called with
38551      * the following parameters:<ul>
38552      * <li>Data value.</li>
38553      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38554      * <li>css A CSS style string to apply to the table cell.</li>
38555      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38556      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38557      * <li>Row index</li>
38558      * <li>Column index</li>
38559      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38560      */
38561     setRenderer : function(col, fn){
38562         this.config[col].renderer = fn;
38563     },
38564
38565     /**
38566      * Returns the width for the specified column.
38567      * @param {Number} col The column index
38568      * @return {Number}
38569      */
38570     getColumnWidth : function(col){
38571         return this.config[col].width * 1 || this.defaultWidth;
38572     },
38573
38574     /**
38575      * Sets the width for a column.
38576      * @param {Number} col The column index
38577      * @param {Number} width The new width
38578      */
38579     setColumnWidth : function(col, width, suppressEvent){
38580         this.config[col].width = width;
38581         this.totalWidth = null;
38582         if(!suppressEvent){
38583              this.fireEvent("widthchange", this, col, width);
38584         }
38585     },
38586
38587     /**
38588      * Returns the total width of all columns.
38589      * @param {Boolean} includeHidden True to include hidden column widths
38590      * @return {Number}
38591      */
38592     getTotalWidth : function(includeHidden){
38593         if(!this.totalWidth){
38594             this.totalWidth = 0;
38595             for(var i = 0, len = this.config.length; i < len; i++){
38596                 if(includeHidden || !this.isHidden(i)){
38597                     this.totalWidth += this.getColumnWidth(i);
38598                 }
38599             }
38600         }
38601         return this.totalWidth;
38602     },
38603
38604     /**
38605      * Returns the header for the specified column.
38606      * @param {Number} col The column index
38607      * @return {String}
38608      */
38609     getColumnHeader : function(col){
38610         return this.config[col].header;
38611     },
38612
38613     /**
38614      * Sets the header for a column.
38615      * @param {Number} col The column index
38616      * @param {String} header The new header
38617      */
38618     setColumnHeader : function(col, header){
38619         this.config[col].header = header;
38620         this.fireEvent("headerchange", this, col, header);
38621     },
38622
38623     /**
38624      * Returns the tooltip for the specified column.
38625      * @param {Number} col The column index
38626      * @return {String}
38627      */
38628     getColumnTooltip : function(col){
38629             return this.config[col].tooltip;
38630     },
38631     /**
38632      * Sets the tooltip for a column.
38633      * @param {Number} col The column index
38634      * @param {String} tooltip The new tooltip
38635      */
38636     setColumnTooltip : function(col, tooltip){
38637             this.config[col].tooltip = tooltip;
38638     },
38639
38640     /**
38641      * Returns the dataIndex for the specified column.
38642      * @param {Number} col The column index
38643      * @return {Number}
38644      */
38645     getDataIndex : function(col){
38646         return this.config[col].dataIndex;
38647     },
38648
38649     /**
38650      * Sets the dataIndex for a column.
38651      * @param {Number} col The column index
38652      * @param {Number} dataIndex The new dataIndex
38653      */
38654     setDataIndex : function(col, dataIndex){
38655         this.config[col].dataIndex = dataIndex;
38656     },
38657
38658     
38659     
38660     /**
38661      * Returns true if the cell is editable.
38662      * @param {Number} colIndex The column index
38663      * @param {Number} rowIndex The row index
38664      * @return {Boolean}
38665      */
38666     isCellEditable : function(colIndex, rowIndex){
38667         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38668     },
38669
38670     /**
38671      * Returns the editor defined for the cell/column.
38672      * return false or null to disable editing.
38673      * @param {Number} colIndex The column index
38674      * @param {Number} rowIndex The row index
38675      * @return {Object}
38676      */
38677     getCellEditor : function(colIndex, rowIndex){
38678         return this.config[colIndex].editor;
38679     },
38680
38681     /**
38682      * Sets if a column is editable.
38683      * @param {Number} col The column index
38684      * @param {Boolean} editable True if the column is editable
38685      */
38686     setEditable : function(col, editable){
38687         this.config[col].editable = editable;
38688     },
38689
38690
38691     /**
38692      * Returns true if the column is hidden.
38693      * @param {Number} colIndex The column index
38694      * @return {Boolean}
38695      */
38696     isHidden : function(colIndex){
38697         return this.config[colIndex].hidden;
38698     },
38699
38700
38701     /**
38702      * Returns true if the column width cannot be changed
38703      */
38704     isFixed : function(colIndex){
38705         return this.config[colIndex].fixed;
38706     },
38707
38708     /**
38709      * Returns true if the column can be resized
38710      * @return {Boolean}
38711      */
38712     isResizable : function(colIndex){
38713         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38714     },
38715     /**
38716      * Sets if a column is hidden.
38717      * @param {Number} colIndex The column index
38718      * @param {Boolean} hidden True if the column is hidden
38719      */
38720     setHidden : function(colIndex, hidden){
38721         this.config[colIndex].hidden = hidden;
38722         this.totalWidth = null;
38723         this.fireEvent("hiddenchange", this, colIndex, hidden);
38724     },
38725
38726     /**
38727      * Sets the editor for a column.
38728      * @param {Number} col The column index
38729      * @param {Object} editor The editor object
38730      */
38731     setEditor : function(col, editor){
38732         this.config[col].editor = editor;
38733     }
38734 });
38735
38736 Roo.grid.ColumnModel.defaultRenderer = function(value){
38737         if(typeof value == "string" && value.length < 1){
38738             return "&#160;";
38739         }
38740         return value;
38741 };
38742
38743 // Alias for backwards compatibility
38744 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38745 /*
38746  * Based on:
38747  * Ext JS Library 1.1.1
38748  * Copyright(c) 2006-2007, Ext JS, LLC.
38749  *
38750  * Originally Released Under LGPL - original licence link has changed is not relivant.
38751  *
38752  * Fork - LGPL
38753  * <script type="text/javascript">
38754  */
38755
38756 /**
38757  * @class Roo.grid.AbstractSelectionModel
38758  * @extends Roo.util.Observable
38759  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38760  * implemented by descendant classes.  This class should not be directly instantiated.
38761  * @constructor
38762  */
38763 Roo.grid.AbstractSelectionModel = function(){
38764     this.locked = false;
38765     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38766 };
38767
38768 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38769     /** @ignore Called by the grid automatically. Do not call directly. */
38770     init : function(grid){
38771         this.grid = grid;
38772         this.initEvents();
38773     },
38774
38775     /**
38776      * Locks the selections.
38777      */
38778     lock : function(){
38779         this.locked = true;
38780     },
38781
38782     /**
38783      * Unlocks the selections.
38784      */
38785     unlock : function(){
38786         this.locked = false;
38787     },
38788
38789     /**
38790      * Returns true if the selections are locked.
38791      * @return {Boolean}
38792      */
38793     isLocked : function(){
38794         return this.locked;
38795     }
38796 });/*
38797  * Based on:
38798  * Ext JS Library 1.1.1
38799  * Copyright(c) 2006-2007, Ext JS, LLC.
38800  *
38801  * Originally Released Under LGPL - original licence link has changed is not relivant.
38802  *
38803  * Fork - LGPL
38804  * <script type="text/javascript">
38805  */
38806 /**
38807  * @extends Roo.grid.AbstractSelectionModel
38808  * @class Roo.grid.RowSelectionModel
38809  * The default SelectionModel used by {@link Roo.grid.Grid}.
38810  * It supports multiple selections and keyboard selection/navigation. 
38811  * @constructor
38812  * @param {Object} config
38813  */
38814 Roo.grid.RowSelectionModel = function(config){
38815     Roo.apply(this, config);
38816     this.selections = new Roo.util.MixedCollection(false, function(o){
38817         return o.id;
38818     });
38819
38820     this.last = false;
38821     this.lastActive = false;
38822
38823     this.addEvents({
38824         /**
38825              * @event selectionchange
38826              * Fires when the selection changes
38827              * @param {SelectionModel} this
38828              */
38829             "selectionchange" : true,
38830         /**
38831              * @event afterselectionchange
38832              * Fires after the selection changes (eg. by key press or clicking)
38833              * @param {SelectionModel} this
38834              */
38835             "afterselectionchange" : true,
38836         /**
38837              * @event beforerowselect
38838              * Fires when a row is selected being selected, return false to cancel.
38839              * @param {SelectionModel} this
38840              * @param {Number} rowIndex The selected index
38841              * @param {Boolean} keepExisting False if other selections will be cleared
38842              */
38843             "beforerowselect" : true,
38844         /**
38845              * @event rowselect
38846              * Fires when a row is selected.
38847              * @param {SelectionModel} this
38848              * @param {Number} rowIndex The selected index
38849              * @param {Roo.data.Record} r The record
38850              */
38851             "rowselect" : true,
38852         /**
38853              * @event rowdeselect
38854              * Fires when a row is deselected.
38855              * @param {SelectionModel} this
38856              * @param {Number} rowIndex The selected index
38857              */
38858         "rowdeselect" : true
38859     });
38860     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38861     this.locked = false;
38862 };
38863
38864 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38865     /**
38866      * @cfg {Boolean} singleSelect
38867      * True to allow selection of only one row at a time (defaults to false)
38868      */
38869     singleSelect : false,
38870
38871     // private
38872     initEvents : function(){
38873
38874         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38875             this.grid.on("mousedown", this.handleMouseDown, this);
38876         }else{ // allow click to work like normal
38877             this.grid.on("rowclick", this.handleDragableRowClick, this);
38878         }
38879
38880         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38881             "up" : function(e){
38882                 if(!e.shiftKey){
38883                     this.selectPrevious(e.shiftKey);
38884                 }else if(this.last !== false && this.lastActive !== false){
38885                     var last = this.last;
38886                     this.selectRange(this.last,  this.lastActive-1);
38887                     this.grid.getView().focusRow(this.lastActive);
38888                     if(last !== false){
38889                         this.last = last;
38890                     }
38891                 }else{
38892                     this.selectFirstRow();
38893                 }
38894                 this.fireEvent("afterselectionchange", this);
38895             },
38896             "down" : function(e){
38897                 if(!e.shiftKey){
38898                     this.selectNext(e.shiftKey);
38899                 }else if(this.last !== false && this.lastActive !== false){
38900                     var last = this.last;
38901                     this.selectRange(this.last,  this.lastActive+1);
38902                     this.grid.getView().focusRow(this.lastActive);
38903                     if(last !== false){
38904                         this.last = last;
38905                     }
38906                 }else{
38907                     this.selectFirstRow();
38908                 }
38909                 this.fireEvent("afterselectionchange", this);
38910             },
38911             scope: this
38912         });
38913
38914         var view = this.grid.view;
38915         view.on("refresh", this.onRefresh, this);
38916         view.on("rowupdated", this.onRowUpdated, this);
38917         view.on("rowremoved", this.onRemove, this);
38918     },
38919
38920     // private
38921     onRefresh : function(){
38922         var ds = this.grid.dataSource, i, v = this.grid.view;
38923         var s = this.selections;
38924         s.each(function(r){
38925             if((i = ds.indexOfId(r.id)) != -1){
38926                 v.onRowSelect(i);
38927             }else{
38928                 s.remove(r);
38929             }
38930         });
38931     },
38932
38933     // private
38934     onRemove : function(v, index, r){
38935         this.selections.remove(r);
38936     },
38937
38938     // private
38939     onRowUpdated : function(v, index, r){
38940         if(this.isSelected(r)){
38941             v.onRowSelect(index);
38942         }
38943     },
38944
38945     /**
38946      * Select records.
38947      * @param {Array} records The records to select
38948      * @param {Boolean} keepExisting (optional) True to keep existing selections
38949      */
38950     selectRecords : function(records, keepExisting){
38951         if(!keepExisting){
38952             this.clearSelections();
38953         }
38954         var ds = this.grid.dataSource;
38955         for(var i = 0, len = records.length; i < len; i++){
38956             this.selectRow(ds.indexOf(records[i]), true);
38957         }
38958     },
38959
38960     /**
38961      * Gets the number of selected rows.
38962      * @return {Number}
38963      */
38964     getCount : function(){
38965         return this.selections.length;
38966     },
38967
38968     /**
38969      * Selects the first row in the grid.
38970      */
38971     selectFirstRow : function(){
38972         this.selectRow(0);
38973     },
38974
38975     /**
38976      * Select the last row.
38977      * @param {Boolean} keepExisting (optional) True to keep existing selections
38978      */
38979     selectLastRow : function(keepExisting){
38980         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
38981     },
38982
38983     /**
38984      * Selects the row immediately following the last selected row.
38985      * @param {Boolean} keepExisting (optional) True to keep existing selections
38986      */
38987     selectNext : function(keepExisting){
38988         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
38989             this.selectRow(this.last+1, keepExisting);
38990             this.grid.getView().focusRow(this.last);
38991         }
38992     },
38993
38994     /**
38995      * Selects the row that precedes the last selected row.
38996      * @param {Boolean} keepExisting (optional) True to keep existing selections
38997      */
38998     selectPrevious : function(keepExisting){
38999         if(this.last){
39000             this.selectRow(this.last-1, keepExisting);
39001             this.grid.getView().focusRow(this.last);
39002         }
39003     },
39004
39005     /**
39006      * Returns the selected records
39007      * @return {Array} Array of selected records
39008      */
39009     getSelections : function(){
39010         return [].concat(this.selections.items);
39011     },
39012
39013     /**
39014      * Returns the first selected record.
39015      * @return {Record}
39016      */
39017     getSelected : function(){
39018         return this.selections.itemAt(0);
39019     },
39020
39021
39022     /**
39023      * Clears all selections.
39024      */
39025     clearSelections : function(fast){
39026         if(this.locked) return;
39027         if(fast !== true){
39028             var ds = this.grid.dataSource;
39029             var s = this.selections;
39030             s.each(function(r){
39031                 this.deselectRow(ds.indexOfId(r.id));
39032             }, this);
39033             s.clear();
39034         }else{
39035             this.selections.clear();
39036         }
39037         this.last = false;
39038     },
39039
39040
39041     /**
39042      * Selects all rows.
39043      */
39044     selectAll : function(){
39045         if(this.locked) return;
39046         this.selections.clear();
39047         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39048             this.selectRow(i, true);
39049         }
39050     },
39051
39052     /**
39053      * Returns True if there is a selection.
39054      * @return {Boolean}
39055      */
39056     hasSelection : function(){
39057         return this.selections.length > 0;
39058     },
39059
39060     /**
39061      * Returns True if the specified row is selected.
39062      * @param {Number/Record} record The record or index of the record to check
39063      * @return {Boolean}
39064      */
39065     isSelected : function(index){
39066         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39067         return (r && this.selections.key(r.id) ? true : false);
39068     },
39069
39070     /**
39071      * Returns True if the specified record id is selected.
39072      * @param {String} id The id of record to check
39073      * @return {Boolean}
39074      */
39075     isIdSelected : function(id){
39076         return (this.selections.key(id) ? true : false);
39077     },
39078
39079     // private
39080     handleMouseDown : function(e, t){
39081         var view = this.grid.getView(), rowIndex;
39082         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39083             return;
39084         };
39085         if(e.shiftKey && this.last !== false){
39086             var last = this.last;
39087             this.selectRange(last, rowIndex, e.ctrlKey);
39088             this.last = last; // reset the last
39089             view.focusRow(rowIndex);
39090         }else{
39091             var isSelected = this.isSelected(rowIndex);
39092             if(e.button !== 0 && isSelected){
39093                 view.focusRow(rowIndex);
39094             }else if(e.ctrlKey && isSelected){
39095                 this.deselectRow(rowIndex);
39096             }else if(!isSelected){
39097                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39098                 view.focusRow(rowIndex);
39099             }
39100         }
39101         this.fireEvent("afterselectionchange", this);
39102     },
39103     // private
39104     handleDragableRowClick :  function(grid, rowIndex, e) 
39105     {
39106         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39107             this.selectRow(rowIndex, false);
39108             grid.view.focusRow(rowIndex);
39109              this.fireEvent("afterselectionchange", this);
39110         }
39111     },
39112     
39113     /**
39114      * Selects multiple rows.
39115      * @param {Array} rows Array of the indexes of the row to select
39116      * @param {Boolean} keepExisting (optional) True to keep existing selections
39117      */
39118     selectRows : function(rows, keepExisting){
39119         if(!keepExisting){
39120             this.clearSelections();
39121         }
39122         for(var i = 0, len = rows.length; i < len; i++){
39123             this.selectRow(rows[i], true);
39124         }
39125     },
39126
39127     /**
39128      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39129      * @param {Number} startRow The index of the first row in the range
39130      * @param {Number} endRow The index of the last row in the range
39131      * @param {Boolean} keepExisting (optional) True to retain existing selections
39132      */
39133     selectRange : function(startRow, endRow, keepExisting){
39134         if(this.locked) return;
39135         if(!keepExisting){
39136             this.clearSelections();
39137         }
39138         if(startRow <= endRow){
39139             for(var i = startRow; i <= endRow; i++){
39140                 this.selectRow(i, true);
39141             }
39142         }else{
39143             for(var i = startRow; i >= endRow; i--){
39144                 this.selectRow(i, true);
39145             }
39146         }
39147     },
39148
39149     /**
39150      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39151      * @param {Number} startRow The index of the first row in the range
39152      * @param {Number} endRow The index of the last row in the range
39153      */
39154     deselectRange : function(startRow, endRow, preventViewNotify){
39155         if(this.locked) return;
39156         for(var i = startRow; i <= endRow; i++){
39157             this.deselectRow(i, preventViewNotify);
39158         }
39159     },
39160
39161     /**
39162      * Selects a row.
39163      * @param {Number} row The index of the row to select
39164      * @param {Boolean} keepExisting (optional) True to keep existing selections
39165      */
39166     selectRow : function(index, keepExisting, preventViewNotify){
39167         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39168         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39169             if(!keepExisting || this.singleSelect){
39170                 this.clearSelections();
39171             }
39172             var r = this.grid.dataSource.getAt(index);
39173             this.selections.add(r);
39174             this.last = this.lastActive = index;
39175             if(!preventViewNotify){
39176                 this.grid.getView().onRowSelect(index);
39177             }
39178             this.fireEvent("rowselect", this, index, r);
39179             this.fireEvent("selectionchange", this);
39180         }
39181     },
39182
39183     /**
39184      * Deselects a row.
39185      * @param {Number} row The index of the row to deselect
39186      */
39187     deselectRow : function(index, preventViewNotify){
39188         if(this.locked) return;
39189         if(this.last == index){
39190             this.last = false;
39191         }
39192         if(this.lastActive == index){
39193             this.lastActive = false;
39194         }
39195         var r = this.grid.dataSource.getAt(index);
39196         this.selections.remove(r);
39197         if(!preventViewNotify){
39198             this.grid.getView().onRowDeselect(index);
39199         }
39200         this.fireEvent("rowdeselect", this, index);
39201         this.fireEvent("selectionchange", this);
39202     },
39203
39204     // private
39205     restoreLast : function(){
39206         if(this._last){
39207             this.last = this._last;
39208         }
39209     },
39210
39211     // private
39212     acceptsNav : function(row, col, cm){
39213         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39214     },
39215
39216     // private
39217     onEditorKey : function(field, e){
39218         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39219         if(k == e.TAB){
39220             e.stopEvent();
39221             ed.completeEdit();
39222             if(e.shiftKey){
39223                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39224             }else{
39225                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39226             }
39227         }else if(k == e.ENTER && !e.ctrlKey){
39228             e.stopEvent();
39229             ed.completeEdit();
39230             if(e.shiftKey){
39231                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39232             }else{
39233                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39234             }
39235         }else if(k == e.ESC){
39236             ed.cancelEdit();
39237         }
39238         if(newCell){
39239             g.startEditing(newCell[0], newCell[1]);
39240         }
39241     }
39242 });/*
39243  * Based on:
39244  * Ext JS Library 1.1.1
39245  * Copyright(c) 2006-2007, Ext JS, LLC.
39246  *
39247  * Originally Released Under LGPL - original licence link has changed is not relivant.
39248  *
39249  * Fork - LGPL
39250  * <script type="text/javascript">
39251  */
39252 /**
39253  * @class Roo.grid.CellSelectionModel
39254  * @extends Roo.grid.AbstractSelectionModel
39255  * This class provides the basic implementation for cell selection in a grid.
39256  * @constructor
39257  * @param {Object} config The object containing the configuration of this model.
39258  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39259  */
39260 Roo.grid.CellSelectionModel = function(config){
39261     Roo.apply(this, config);
39262
39263     this.selection = null;
39264
39265     this.addEvents({
39266         /**
39267              * @event beforerowselect
39268              * Fires before a cell is selected.
39269              * @param {SelectionModel} this
39270              * @param {Number} rowIndex The selected row index
39271              * @param {Number} colIndex The selected cell index
39272              */
39273             "beforecellselect" : true,
39274         /**
39275              * @event cellselect
39276              * Fires when a cell is selected.
39277              * @param {SelectionModel} this
39278              * @param {Number} rowIndex The selected row index
39279              * @param {Number} colIndex The selected cell index
39280              */
39281             "cellselect" : true,
39282         /**
39283              * @event selectionchange
39284              * Fires when the active selection changes.
39285              * @param {SelectionModel} this
39286              * @param {Object} selection null for no selection or an object (o) with two properties
39287                 <ul>
39288                 <li>o.record: the record object for the row the selection is in</li>
39289                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39290                 </ul>
39291              */
39292             "selectionchange" : true,
39293         /**
39294              * @event tabend
39295              * Fires when the tab (or enter) was pressed on the last editable cell
39296              * You can use this to trigger add new row.
39297              * @param {SelectionModel} this
39298              */
39299             "tabend" : true,
39300          /**
39301              * @event beforeeditnext
39302              * Fires before the next editable sell is made active
39303              * You can use this to skip to another cell or fire the tabend
39304              *    if you set cell to false
39305              * @param {Object} eventdata object : { cell : [ row, col ] } 
39306              */
39307             "beforeeditnext" : true
39308     });
39309     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39310 };
39311
39312 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39313     
39314     enter_is_tab: false,
39315
39316     /** @ignore */
39317     initEvents : function(){
39318         this.grid.on("mousedown", this.handleMouseDown, this);
39319         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39320         var view = this.grid.view;
39321         view.on("refresh", this.onViewChange, this);
39322         view.on("rowupdated", this.onRowUpdated, this);
39323         view.on("beforerowremoved", this.clearSelections, this);
39324         view.on("beforerowsinserted", this.clearSelections, this);
39325         if(this.grid.isEditor){
39326             this.grid.on("beforeedit", this.beforeEdit,  this);
39327         }
39328     },
39329
39330         //private
39331     beforeEdit : function(e){
39332         this.select(e.row, e.column, false, true, e.record);
39333     },
39334
39335         //private
39336     onRowUpdated : function(v, index, r){
39337         if(this.selection && this.selection.record == r){
39338             v.onCellSelect(index, this.selection.cell[1]);
39339         }
39340     },
39341
39342         //private
39343     onViewChange : function(){
39344         this.clearSelections(true);
39345     },
39346
39347         /**
39348          * Returns the currently selected cell,.
39349          * @return {Array} The selected cell (row, column) or null if none selected.
39350          */
39351     getSelectedCell : function(){
39352         return this.selection ? this.selection.cell : null;
39353     },
39354
39355     /**
39356      * Clears all selections.
39357      * @param {Boolean} true to prevent the gridview from being notified about the change.
39358      */
39359     clearSelections : function(preventNotify){
39360         var s = this.selection;
39361         if(s){
39362             if(preventNotify !== true){
39363                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39364             }
39365             this.selection = null;
39366             this.fireEvent("selectionchange", this, null);
39367         }
39368     },
39369
39370     /**
39371      * Returns true if there is a selection.
39372      * @return {Boolean}
39373      */
39374     hasSelection : function(){
39375         return this.selection ? true : false;
39376     },
39377
39378     /** @ignore */
39379     handleMouseDown : function(e, t){
39380         var v = this.grid.getView();
39381         if(this.isLocked()){
39382             return;
39383         };
39384         var row = v.findRowIndex(t);
39385         var cell = v.findCellIndex(t);
39386         if(row !== false && cell !== false){
39387             this.select(row, cell);
39388         }
39389     },
39390
39391     /**
39392      * Selects a cell.
39393      * @param {Number} rowIndex
39394      * @param {Number} collIndex
39395      */
39396     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39397         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39398             this.clearSelections();
39399             r = r || this.grid.dataSource.getAt(rowIndex);
39400             this.selection = {
39401                 record : r,
39402                 cell : [rowIndex, colIndex]
39403             };
39404             if(!preventViewNotify){
39405                 var v = this.grid.getView();
39406                 v.onCellSelect(rowIndex, colIndex);
39407                 if(preventFocus !== true){
39408                     v.focusCell(rowIndex, colIndex);
39409                 }
39410             }
39411             this.fireEvent("cellselect", this, rowIndex, colIndex);
39412             this.fireEvent("selectionchange", this, this.selection);
39413         }
39414     },
39415
39416         //private
39417     isSelectable : function(rowIndex, colIndex, cm){
39418         return !cm.isHidden(colIndex);
39419     },
39420
39421     /** @ignore */
39422     handleKeyDown : function(e){
39423         //Roo.log('Cell Sel Model handleKeyDown');
39424         if(!e.isNavKeyPress()){
39425             return;
39426         }
39427         var g = this.grid, s = this.selection;
39428         if(!s){
39429             e.stopEvent();
39430             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39431             if(cell){
39432                 this.select(cell[0], cell[1]);
39433             }
39434             return;
39435         }
39436         var sm = this;
39437         var walk = function(row, col, step){
39438             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39439         };
39440         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39441         var newCell;
39442
39443       
39444
39445         switch(k){
39446             case e.TAB:
39447                 // handled by onEditorKey
39448                 if (g.isEditor && g.editing) {
39449                     return;
39450                 }
39451                 if(e.shiftKey) {
39452                     newCell = walk(r, c-1, -1);
39453                 } else {
39454                     newCell = walk(r, c+1, 1);
39455                 }
39456                 break;
39457             
39458             case e.DOWN:
39459                newCell = walk(r+1, c, 1);
39460                 break;
39461             
39462             case e.UP:
39463                 newCell = walk(r-1, c, -1);
39464                 break;
39465             
39466             case e.RIGHT:
39467                 newCell = walk(r, c+1, 1);
39468                 break;
39469             
39470             case e.LEFT:
39471                 newCell = walk(r, c-1, -1);
39472                 break;
39473             
39474             case e.ENTER:
39475                 
39476                 if(g.isEditor && !g.editing){
39477                    g.startEditing(r, c);
39478                    e.stopEvent();
39479                    return;
39480                 }
39481                 
39482                 
39483              break;
39484         };
39485         if(newCell){
39486             this.select(newCell[0], newCell[1]);
39487             e.stopEvent();
39488             
39489         }
39490     },
39491
39492     acceptsNav : function(row, col, cm){
39493         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39494     },
39495     /**
39496      * Selects a cell.
39497      * @param {Number} field (not used) - as it's normally used as a listener
39498      * @param {Number} e - event - fake it by using
39499      *
39500      * var e = Roo.EventObjectImpl.prototype;
39501      * e.keyCode = e.TAB
39502      *
39503      * 
39504      */
39505     onEditorKey : function(field, e){
39506         
39507         var k = e.getKey(),
39508             newCell,
39509             g = this.grid,
39510             ed = g.activeEditor,
39511             forward = false;
39512         ///Roo.log('onEditorKey' + k);
39513         
39514         
39515         if (this.enter_is_tab && k == e.ENTER) {
39516             k = e.TAB;
39517         }
39518         
39519         if(k == e.TAB){
39520             if(e.shiftKey){
39521                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39522             }else{
39523                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39524                 forward = true;
39525             }
39526             
39527             e.stopEvent();
39528             
39529         } else if(k == e.ENTER &&  !e.ctrlKey){
39530             ed.completeEdit();
39531             e.stopEvent();
39532             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39533         
39534                 } else if(k == e.ESC){
39535             ed.cancelEdit();
39536         }
39537                 
39538         if (newCell) {
39539             var ecall = { cell : newCell, forward : forward };
39540             this.fireEvent('beforeeditnext', ecall );
39541             newCell = ecall.cell;
39542                         forward = ecall.forward;
39543         }
39544                 
39545         if(newCell){
39546             //Roo.log('next cell after edit');
39547             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39548         } else if (forward) {
39549             // tabbed past last
39550             this.fireEvent.defer(100, this, ['tabend',this]);
39551         }
39552     }
39553 });/*
39554  * Based on:
39555  * Ext JS Library 1.1.1
39556  * Copyright(c) 2006-2007, Ext JS, LLC.
39557  *
39558  * Originally Released Under LGPL - original licence link has changed is not relivant.
39559  *
39560  * Fork - LGPL
39561  * <script type="text/javascript">
39562  */
39563  
39564 /**
39565  * @class Roo.grid.EditorGrid
39566  * @extends Roo.grid.Grid
39567  * Class for creating and editable grid.
39568  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39569  * The container MUST have some type of size defined for the grid to fill. The container will be 
39570  * automatically set to position relative if it isn't already.
39571  * @param {Object} dataSource The data model to bind to
39572  * @param {Object} colModel The column model with info about this grid's columns
39573  */
39574 Roo.grid.EditorGrid = function(container, config){
39575     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39576     this.getGridEl().addClass("xedit-grid");
39577
39578     if(!this.selModel){
39579         this.selModel = new Roo.grid.CellSelectionModel();
39580     }
39581
39582     this.activeEditor = null;
39583
39584         this.addEvents({
39585             /**
39586              * @event beforeedit
39587              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39588              * <ul style="padding:5px;padding-left:16px;">
39589              * <li>grid - This grid</li>
39590              * <li>record - The record being edited</li>
39591              * <li>field - The field name being edited</li>
39592              * <li>value - The value for the field being edited.</li>
39593              * <li>row - The grid row index</li>
39594              * <li>column - The grid column index</li>
39595              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39596              * </ul>
39597              * @param {Object} e An edit event (see above for description)
39598              */
39599             "beforeedit" : true,
39600             /**
39601              * @event afteredit
39602              * Fires after a cell is edited. <br />
39603              * <ul style="padding:5px;padding-left:16px;">
39604              * <li>grid - This grid</li>
39605              * <li>record - The record being edited</li>
39606              * <li>field - The field name being edited</li>
39607              * <li>value - The value being set</li>
39608              * <li>originalValue - The original value for the field, before the edit.</li>
39609              * <li>row - The grid row index</li>
39610              * <li>column - The grid column index</li>
39611              * </ul>
39612              * @param {Object} e An edit event (see above for description)
39613              */
39614             "afteredit" : true,
39615             /**
39616              * @event validateedit
39617              * Fires after a cell is edited, but before the value is set in the record. 
39618          * You can use this to modify the value being set in the field, Return false
39619              * to cancel the change. The edit event object has the following properties <br />
39620              * <ul style="padding:5px;padding-left:16px;">
39621          * <li>editor - This editor</li>
39622              * <li>grid - This grid</li>
39623              * <li>record - The record being edited</li>
39624              * <li>field - The field name being edited</li>
39625              * <li>value - The value being set</li>
39626              * <li>originalValue - The original value for the field, before the edit.</li>
39627              * <li>row - The grid row index</li>
39628              * <li>column - The grid column index</li>
39629              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39630              * </ul>
39631              * @param {Object} e An edit event (see above for description)
39632              */
39633             "validateedit" : true
39634         });
39635     this.on("bodyscroll", this.stopEditing,  this);
39636     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39637 };
39638
39639 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39640     /**
39641      * @cfg {Number} clicksToEdit
39642      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39643      */
39644     clicksToEdit: 2,
39645
39646     // private
39647     isEditor : true,
39648     // private
39649     trackMouseOver: false, // causes very odd FF errors
39650
39651     onCellDblClick : function(g, row, col){
39652         this.startEditing(row, col);
39653     },
39654
39655     onEditComplete : function(ed, value, startValue){
39656         this.editing = false;
39657         this.activeEditor = null;
39658         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39659         var r = ed.record;
39660         var field = this.colModel.getDataIndex(ed.col);
39661         var e = {
39662             grid: this,
39663             record: r,
39664             field: field,
39665             originalValue: startValue,
39666             value: value,
39667             row: ed.row,
39668             column: ed.col,
39669             cancel:false,
39670             editor: ed
39671         };
39672         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39673         cell.show();
39674           
39675         if(String(value) !== String(startValue)){
39676             
39677             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39678                 r.set(field, e.value);
39679                 // if we are dealing with a combo box..
39680                 // then we also set the 'name' colum to be the displayField
39681                 if (ed.field.displayField && ed.field.name) {
39682                     r.set(ed.field.name, ed.field.el.dom.value);
39683                 }
39684                 
39685                 delete e.cancel; //?? why!!!
39686                 this.fireEvent("afteredit", e);
39687             }
39688         } else {
39689             this.fireEvent("afteredit", e); // always fire it!
39690         }
39691         this.view.focusCell(ed.row, ed.col);
39692     },
39693
39694     /**
39695      * Starts editing the specified for the specified row/column
39696      * @param {Number} rowIndex
39697      * @param {Number} colIndex
39698      */
39699     startEditing : function(row, col){
39700         this.stopEditing();
39701         if(this.colModel.isCellEditable(col, row)){
39702             this.view.ensureVisible(row, col, true);
39703           
39704             var r = this.dataSource.getAt(row);
39705             var field = this.colModel.getDataIndex(col);
39706             var cell = Roo.get(this.view.getCell(row,col));
39707             var e = {
39708                 grid: this,
39709                 record: r,
39710                 field: field,
39711                 value: r.data[field],
39712                 row: row,
39713                 column: col,
39714                 cancel:false 
39715             };
39716             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39717                 this.editing = true;
39718                 var ed = this.colModel.getCellEditor(col, row);
39719                 
39720                 if (!ed) {
39721                     return;
39722                 }
39723                 if(!ed.rendered){
39724                     ed.render(ed.parentEl || document.body);
39725                 }
39726                 ed.field.reset();
39727                
39728                 cell.hide();
39729                 
39730                 (function(){ // complex but required for focus issues in safari, ie and opera
39731                     ed.row = row;
39732                     ed.col = col;
39733                     ed.record = r;
39734                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39735                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39736                     this.activeEditor = ed;
39737                     var v = r.data[field];
39738                     ed.startEdit(this.view.getCell(row, col), v);
39739                     // combo's with 'displayField and name set
39740                     if (ed.field.displayField && ed.field.name) {
39741                         ed.field.el.dom.value = r.data[ed.field.name];
39742                     }
39743                     
39744                     
39745                 }).defer(50, this);
39746             }
39747         }
39748     },
39749         
39750     /**
39751      * Stops any active editing
39752      */
39753     stopEditing : function(){
39754         if(this.activeEditor){
39755             this.activeEditor.completeEdit();
39756         }
39757         this.activeEditor = null;
39758     },
39759         
39760          /**
39761      * Called to get grid's drag proxy text, by default returns this.ddText.
39762      * @return {String}
39763      */
39764     getDragDropText : function(){
39765         var count = this.selModel.getSelectedCell() ? 1 : 0;
39766         return String.format(this.ddText, count, count == 1 ? '' : 's');
39767     }
39768         
39769 });/*
39770  * Based on:
39771  * Ext JS Library 1.1.1
39772  * Copyright(c) 2006-2007, Ext JS, LLC.
39773  *
39774  * Originally Released Under LGPL - original licence link has changed is not relivant.
39775  *
39776  * Fork - LGPL
39777  * <script type="text/javascript">
39778  */
39779
39780 // private - not really -- you end up using it !
39781 // This is a support class used internally by the Grid components
39782
39783 /**
39784  * @class Roo.grid.GridEditor
39785  * @extends Roo.Editor
39786  * Class for creating and editable grid elements.
39787  * @param {Object} config any settings (must include field)
39788  */
39789 Roo.grid.GridEditor = function(field, config){
39790     if (!config && field.field) {
39791         config = field;
39792         field = Roo.factory(config.field, Roo.form);
39793     }
39794     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39795     field.monitorTab = false;
39796 };
39797
39798 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39799     
39800     /**
39801      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39802      */
39803     
39804     alignment: "tl-tl",
39805     autoSize: "width",
39806     hideEl : false,
39807     cls: "x-small-editor x-grid-editor",
39808     shim:false,
39809     shadow:"frame"
39810 });/*
39811  * Based on:
39812  * Ext JS Library 1.1.1
39813  * Copyright(c) 2006-2007, Ext JS, LLC.
39814  *
39815  * Originally Released Under LGPL - original licence link has changed is not relivant.
39816  *
39817  * Fork - LGPL
39818  * <script type="text/javascript">
39819  */
39820   
39821
39822   
39823 Roo.grid.PropertyRecord = Roo.data.Record.create([
39824     {name:'name',type:'string'},  'value'
39825 ]);
39826
39827
39828 Roo.grid.PropertyStore = function(grid, source){
39829     this.grid = grid;
39830     this.store = new Roo.data.Store({
39831         recordType : Roo.grid.PropertyRecord
39832     });
39833     this.store.on('update', this.onUpdate,  this);
39834     if(source){
39835         this.setSource(source);
39836     }
39837     Roo.grid.PropertyStore.superclass.constructor.call(this);
39838 };
39839
39840
39841
39842 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39843     setSource : function(o){
39844         this.source = o;
39845         this.store.removeAll();
39846         var data = [];
39847         for(var k in o){
39848             if(this.isEditableValue(o[k])){
39849                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39850             }
39851         }
39852         this.store.loadRecords({records: data}, {}, true);
39853     },
39854
39855     onUpdate : function(ds, record, type){
39856         if(type == Roo.data.Record.EDIT){
39857             var v = record.data['value'];
39858             var oldValue = record.modified['value'];
39859             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39860                 this.source[record.id] = v;
39861                 record.commit();
39862                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39863             }else{
39864                 record.reject();
39865             }
39866         }
39867     },
39868
39869     getProperty : function(row){
39870        return this.store.getAt(row);
39871     },
39872
39873     isEditableValue: function(val){
39874         if(val && val instanceof Date){
39875             return true;
39876         }else if(typeof val == 'object' || typeof val == 'function'){
39877             return false;
39878         }
39879         return true;
39880     },
39881
39882     setValue : function(prop, value){
39883         this.source[prop] = value;
39884         this.store.getById(prop).set('value', value);
39885     },
39886
39887     getSource : function(){
39888         return this.source;
39889     }
39890 });
39891
39892 Roo.grid.PropertyColumnModel = function(grid, store){
39893     this.grid = grid;
39894     var g = Roo.grid;
39895     g.PropertyColumnModel.superclass.constructor.call(this, [
39896         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39897         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39898     ]);
39899     this.store = store;
39900     this.bselect = Roo.DomHelper.append(document.body, {
39901         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39902             {tag: 'option', value: 'true', html: 'true'},
39903             {tag: 'option', value: 'false', html: 'false'}
39904         ]
39905     });
39906     Roo.id(this.bselect);
39907     var f = Roo.form;
39908     this.editors = {
39909         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39910         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39911         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39912         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39913         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39914     };
39915     this.renderCellDelegate = this.renderCell.createDelegate(this);
39916     this.renderPropDelegate = this.renderProp.createDelegate(this);
39917 };
39918
39919 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39920     
39921     
39922     nameText : 'Name',
39923     valueText : 'Value',
39924     
39925     dateFormat : 'm/j/Y',
39926     
39927     
39928     renderDate : function(dateVal){
39929         return dateVal.dateFormat(this.dateFormat);
39930     },
39931
39932     renderBool : function(bVal){
39933         return bVal ? 'true' : 'false';
39934     },
39935
39936     isCellEditable : function(colIndex, rowIndex){
39937         return colIndex == 1;
39938     },
39939
39940     getRenderer : function(col){
39941         return col == 1 ?
39942             this.renderCellDelegate : this.renderPropDelegate;
39943     },
39944
39945     renderProp : function(v){
39946         return this.getPropertyName(v);
39947     },
39948
39949     renderCell : function(val){
39950         var rv = val;
39951         if(val instanceof Date){
39952             rv = this.renderDate(val);
39953         }else if(typeof val == 'boolean'){
39954             rv = this.renderBool(val);
39955         }
39956         return Roo.util.Format.htmlEncode(rv);
39957     },
39958
39959     getPropertyName : function(name){
39960         var pn = this.grid.propertyNames;
39961         return pn && pn[name] ? pn[name] : name;
39962     },
39963
39964     getCellEditor : function(colIndex, rowIndex){
39965         var p = this.store.getProperty(rowIndex);
39966         var n = p.data['name'], val = p.data['value'];
39967         
39968         if(typeof(this.grid.customEditors[n]) == 'string'){
39969             return this.editors[this.grid.customEditors[n]];
39970         }
39971         if(typeof(this.grid.customEditors[n]) != 'undefined'){
39972             return this.grid.customEditors[n];
39973         }
39974         if(val instanceof Date){
39975             return this.editors['date'];
39976         }else if(typeof val == 'number'){
39977             return this.editors['number'];
39978         }else if(typeof val == 'boolean'){
39979             return this.editors['boolean'];
39980         }else{
39981             return this.editors['string'];
39982         }
39983     }
39984 });
39985
39986 /**
39987  * @class Roo.grid.PropertyGrid
39988  * @extends Roo.grid.EditorGrid
39989  * This class represents the  interface of a component based property grid control.
39990  * <br><br>Usage:<pre><code>
39991  var grid = new Roo.grid.PropertyGrid("my-container-id", {
39992       
39993  });
39994  // set any options
39995  grid.render();
39996  * </code></pre>
39997   
39998  * @constructor
39999  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40000  * The container MUST have some type of size defined for the grid to fill. The container will be
40001  * automatically set to position relative if it isn't already.
40002  * @param {Object} config A config object that sets properties on this grid.
40003  */
40004 Roo.grid.PropertyGrid = function(container, config){
40005     config = config || {};
40006     var store = new Roo.grid.PropertyStore(this);
40007     this.store = store;
40008     var cm = new Roo.grid.PropertyColumnModel(this, store);
40009     store.store.sort('name', 'ASC');
40010     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40011         ds: store.store,
40012         cm: cm,
40013         enableColLock:false,
40014         enableColumnMove:false,
40015         stripeRows:false,
40016         trackMouseOver: false,
40017         clicksToEdit:1
40018     }, config));
40019     this.getGridEl().addClass('x-props-grid');
40020     this.lastEditRow = null;
40021     this.on('columnresize', this.onColumnResize, this);
40022     this.addEvents({
40023          /**
40024              * @event beforepropertychange
40025              * Fires before a property changes (return false to stop?)
40026              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40027              * @param {String} id Record Id
40028              * @param {String} newval New Value
40029          * @param {String} oldval Old Value
40030              */
40031         "beforepropertychange": true,
40032         /**
40033              * @event propertychange
40034              * Fires after a property changes
40035              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40036              * @param {String} id Record Id
40037              * @param {String} newval New Value
40038          * @param {String} oldval Old Value
40039              */
40040         "propertychange": true
40041     });
40042     this.customEditors = this.customEditors || {};
40043 };
40044 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40045     
40046      /**
40047      * @cfg {Object} customEditors map of colnames=> custom editors.
40048      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40049      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40050      * false disables editing of the field.
40051          */
40052     
40053       /**
40054      * @cfg {Object} propertyNames map of property Names to their displayed value
40055          */
40056     
40057     render : function(){
40058         Roo.grid.PropertyGrid.superclass.render.call(this);
40059         this.autoSize.defer(100, this);
40060     },
40061
40062     autoSize : function(){
40063         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40064         if(this.view){
40065             this.view.fitColumns();
40066         }
40067     },
40068
40069     onColumnResize : function(){
40070         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40071         this.autoSize();
40072     },
40073     /**
40074      * Sets the data for the Grid
40075      * accepts a Key => Value object of all the elements avaiable.
40076      * @param {Object} data  to appear in grid.
40077      */
40078     setSource : function(source){
40079         this.store.setSource(source);
40080         //this.autoSize();
40081     },
40082     /**
40083      * Gets all the data from the grid.
40084      * @return {Object} data  data stored in grid
40085      */
40086     getSource : function(){
40087         return this.store.getSource();
40088     }
40089 });/*
40090  * Based on:
40091  * Ext JS Library 1.1.1
40092  * Copyright(c) 2006-2007, Ext JS, LLC.
40093  *
40094  * Originally Released Under LGPL - original licence link has changed is not relivant.
40095  *
40096  * Fork - LGPL
40097  * <script type="text/javascript">
40098  */
40099  
40100 /**
40101  * @class Roo.LoadMask
40102  * A simple utility class for generically masking elements while loading data.  If the element being masked has
40103  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
40104  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
40105  * element's UpdateManager load indicator and will be destroyed after the initial load.
40106  * @constructor
40107  * Create a new LoadMask
40108  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
40109  * @param {Object} config The config object
40110  */
40111 Roo.LoadMask = function(el, config){
40112     this.el = Roo.get(el);
40113     Roo.apply(this, config);
40114     if(this.store){
40115         this.store.on('beforeload', this.onBeforeLoad, this);
40116         this.store.on('load', this.onLoad, this);
40117         this.store.on('loadexception', this.onLoadException, this);
40118         this.removeMask = false;
40119     }else{
40120         var um = this.el.getUpdateManager();
40121         um.showLoadIndicator = false; // disable the default indicator
40122         um.on('beforeupdate', this.onBeforeLoad, this);
40123         um.on('update', this.onLoad, this);
40124         um.on('failure', this.onLoad, this);
40125         this.removeMask = true;
40126     }
40127 };
40128
40129 Roo.LoadMask.prototype = {
40130     /**
40131      * @cfg {Boolean} removeMask
40132      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
40133      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
40134      */
40135     /**
40136      * @cfg {String} msg
40137      * The text to display in a centered loading message box (defaults to 'Loading...')
40138      */
40139     msg : 'Loading...',
40140     /**
40141      * @cfg {String} msgCls
40142      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
40143      */
40144     msgCls : 'x-mask-loading',
40145
40146     /**
40147      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
40148      * @type Boolean
40149      */
40150     disabled: false,
40151
40152     /**
40153      * Disables the mask to prevent it from being displayed
40154      */
40155     disable : function(){
40156        this.disabled = true;
40157     },
40158
40159     /**
40160      * Enables the mask so that it can be displayed
40161      */
40162     enable : function(){
40163         this.disabled = false;
40164     },
40165     
40166     onLoadException : function()
40167     {
40168         Roo.log(arguments);
40169         
40170         if (typeof(arguments[3]) != 'undefined') {
40171             Roo.MessageBox.alert("Error loading",arguments[3]);
40172         } 
40173         /*
40174         try {
40175             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
40176                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
40177             }   
40178         } catch(e) {
40179             
40180         }
40181         */
40182     
40183         
40184         
40185         this.el.unmask(this.removeMask);
40186     },
40187     // private
40188     onLoad : function()
40189     {
40190         this.el.unmask(this.removeMask);
40191     },
40192
40193     // private
40194     onBeforeLoad : function(){
40195         if(!this.disabled){
40196             this.el.mask(this.msg, this.msgCls);
40197         }
40198     },
40199
40200     // private
40201     destroy : function(){
40202         if(this.store){
40203             this.store.un('beforeload', this.onBeforeLoad, this);
40204             this.store.un('load', this.onLoad, this);
40205             this.store.un('loadexception', this.onLoadException, this);
40206         }else{
40207             var um = this.el.getUpdateManager();
40208             um.un('beforeupdate', this.onBeforeLoad, this);
40209             um.un('update', this.onLoad, this);
40210             um.un('failure', this.onLoad, this);
40211         }
40212     }
40213 };/*
40214  * Based on:
40215  * Ext JS Library 1.1.1
40216  * Copyright(c) 2006-2007, Ext JS, LLC.
40217  *
40218  * Originally Released Under LGPL - original licence link has changed is not relivant.
40219  *
40220  * Fork - LGPL
40221  * <script type="text/javascript">
40222  */
40223
40224
40225 /**
40226  * @class Roo.XTemplate
40227  * @extends Roo.Template
40228  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
40229 <pre><code>
40230 var t = new Roo.XTemplate(
40231         '&lt;select name="{name}"&gt;',
40232                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
40233         '&lt;/select&gt;'
40234 );
40235  
40236 // then append, applying the master template values
40237  </code></pre>
40238  *
40239  * Supported features:
40240  *
40241  *  Tags:
40242
40243 <pre><code>
40244       {a_variable} - output encoded.
40245       {a_variable.format:("Y-m-d")} - call a method on the variable
40246       {a_variable:raw} - unencoded output
40247       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
40248       {a_variable:this.method_on_template(...)} - call a method on the template object.
40249  
40250 </code></pre>
40251  *  The tpl tag:
40252 <pre><code>
40253         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
40254         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
40255         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
40256         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
40257   
40258         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
40259         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
40260 </code></pre>
40261  *      
40262  */
40263 Roo.XTemplate = function()
40264 {
40265     Roo.XTemplate.superclass.constructor.apply(this, arguments);
40266     if (this.html) {
40267         this.compile();
40268     }
40269 };
40270
40271
40272 Roo.extend(Roo.XTemplate, Roo.Template, {
40273
40274     /**
40275      * The various sub templates
40276      */
40277     tpls : false,
40278     /**
40279      *
40280      * basic tag replacing syntax
40281      * WORD:WORD()
40282      *
40283      * // you can fake an object call by doing this
40284      *  x.t:(test,tesT) 
40285      * 
40286      */
40287     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
40288
40289     /**
40290      * compile the template
40291      *
40292      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
40293      *
40294      */
40295     compile: function()
40296     {
40297         var s = this.html;
40298      
40299         s = ['<tpl>', s, '</tpl>'].join('');
40300     
40301         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
40302             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
40303             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
40304             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
40305             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
40306             m,
40307             id     = 0,
40308             tpls   = [];
40309     
40310         while(true == !!(m = s.match(re))){
40311             var forMatch   = m[0].match(nameRe),
40312                 ifMatch   = m[0].match(ifRe),
40313                 execMatch   = m[0].match(execRe),
40314                 namedMatch   = m[0].match(namedRe),
40315                 
40316                 exp  = null, 
40317                 fn   = null,
40318                 exec = null,
40319                 name = forMatch && forMatch[1] ? forMatch[1] : '';
40320                 
40321             if (ifMatch) {
40322                 // if - puts fn into test..
40323                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
40324                 if(exp){
40325                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
40326                 }
40327             }
40328             
40329             if (execMatch) {
40330                 // exec - calls a function... returns empty if true is  returned.
40331                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
40332                 if(exp){
40333                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
40334                 }
40335             }
40336             
40337             
40338             if (name) {
40339                 // for = 
40340                 switch(name){
40341                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
40342                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
40343                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
40344                 }
40345             }
40346             var uid = namedMatch ? namedMatch[1] : id;
40347             
40348             
40349             tpls.push({
40350                 id:     namedMatch ? namedMatch[1] : id,
40351                 target: name,
40352                 exec:   exec,
40353                 test:   fn,
40354                 body:   m[1] || ''
40355             });
40356             if (namedMatch) {
40357                 s = s.replace(m[0], '');
40358             } else { 
40359                 s = s.replace(m[0], '{xtpl'+ id + '}');
40360             }
40361             ++id;
40362         }
40363         this.tpls = [];
40364         for(var i = tpls.length-1; i >= 0; --i){
40365             this.compileTpl(tpls[i]);
40366             this.tpls[tpls[i].id] = tpls[i];
40367         }
40368         this.master = tpls[tpls.length-1];
40369         return this;
40370     },
40371     /**
40372      * same as applyTemplate, except it's done to one of the subTemplates
40373      * when using named templates, you can do:
40374      *
40375      * var str = pl.applySubTemplate('your-name', values);
40376      *
40377      * 
40378      * @param {Number} id of the template
40379      * @param {Object} values to apply to template
40380      * @param {Object} parent (normaly the instance of this object)
40381      */
40382     applySubTemplate : function(id, values, parent)
40383     {
40384         
40385         
40386         var t = this.tpls[id];
40387         
40388         
40389         try { 
40390             if(t.test && !t.test.call(this, values, parent)){
40391                 return '';
40392             }
40393         } catch(e) {
40394             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
40395             Roo.log(e.toString());
40396             Roo.log(t.test);
40397             return ''
40398         }
40399         try { 
40400             
40401             if(t.exec && t.exec.call(this, values, parent)){
40402                 return '';
40403             }
40404         } catch(e) {
40405             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
40406             Roo.log(e.toString());
40407             Roo.log(t.exec);
40408             return ''
40409         }
40410         try {
40411             var vs = t.target ? t.target.call(this, values, parent) : values;
40412             parent = t.target ? values : parent;
40413             if(t.target && vs instanceof Array){
40414                 var buf = [];
40415                 for(var i = 0, len = vs.length; i < len; i++){
40416                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
40417                 }
40418                 return buf.join('');
40419             }
40420             return t.compiled.call(this, vs, parent);
40421         } catch (e) {
40422             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
40423             Roo.log(e.toString());
40424             Roo.log(t.compiled);
40425             return '';
40426         }
40427     },
40428
40429     compileTpl : function(tpl)
40430     {
40431         var fm = Roo.util.Format;
40432         var useF = this.disableFormats !== true;
40433         var sep = Roo.isGecko ? "+" : ",";
40434         var undef = function(str) {
40435             Roo.log("Property not found :"  + str);
40436             return '';
40437         };
40438         
40439         var fn = function(m, name, format, args)
40440         {
40441             //Roo.log(arguments);
40442             args = args ? args.replace(/\\'/g,"'") : args;
40443             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
40444             if (typeof(format) == 'undefined') {
40445                 format= 'htmlEncode';
40446             }
40447             if (format == 'raw' ) {
40448                 format = false;
40449             }
40450             
40451             if(name.substr(0, 4) == 'xtpl'){
40452                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
40453             }
40454             
40455             // build an array of options to determine if value is undefined..
40456             
40457             // basically get 'xxxx.yyyy' then do
40458             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
40459             //    (function () { Roo.log("Property not found"); return ''; })() :
40460             //    ......
40461             
40462             var udef_ar = [];
40463             var lookfor = '';
40464             Roo.each(name.split('.'), function(st) {
40465                 lookfor += (lookfor.length ? '.': '') + st;
40466                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
40467             });
40468             
40469             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
40470             
40471             
40472             if(format && useF){
40473                 
40474                 args = args ? ',' + args : "";
40475                  
40476                 if(format.substr(0, 5) != "this."){
40477                     format = "fm." + format + '(';
40478                 }else{
40479                     format = 'this.call("'+ format.substr(5) + '", ';
40480                     args = ", values";
40481                 }
40482                 
40483                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
40484             }
40485              
40486             if (args.length) {
40487                 // called with xxyx.yuu:(test,test)
40488                 // change to ()
40489                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
40490             }
40491             // raw.. - :raw modifier..
40492             return "'"+ sep + udef_st  + name + ")"+sep+"'";
40493             
40494         };
40495         var body;
40496         // branched to use + in gecko and [].join() in others
40497         if(Roo.isGecko){
40498             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
40499                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
40500                     "';};};";
40501         }else{
40502             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
40503             body.push(tpl.body.replace(/(\r\n|\n)/g,
40504                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
40505             body.push("'].join('');};};");
40506             body = body.join('');
40507         }
40508         
40509         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
40510        
40511         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
40512         eval(body);
40513         
40514         return this;
40515     },
40516
40517     applyTemplate : function(values){
40518         return this.master.compiled.call(this, values, {});
40519         //var s = this.subs;
40520     },
40521
40522     apply : function(){
40523         return this.applyTemplate.apply(this, arguments);
40524     }
40525
40526  });
40527
40528 Roo.XTemplate.from = function(el){
40529     el = Roo.getDom(el);
40530     return new Roo.XTemplate(el.value || el.innerHTML);
40531 };/*
40532  * Original code for Roojs - LGPL
40533  * <script type="text/javascript">
40534  */
40535  
40536 /**
40537  * @class Roo.XComponent
40538  * A delayed Element creator...
40539  * Or a way to group chunks of interface together.
40540  * 
40541  * Mypart.xyx = new Roo.XComponent({
40542
40543     parent : 'Mypart.xyz', // empty == document.element.!!
40544     order : '001',
40545     name : 'xxxx'
40546     region : 'xxxx'
40547     disabled : function() {} 
40548      
40549     tree : function() { // return an tree of xtype declared components
40550         var MODULE = this;
40551         return 
40552         {
40553             xtype : 'NestedLayoutPanel',
40554             // technicall
40555         }
40556      ]
40557  *})
40558  *
40559  *
40560  * It can be used to build a big heiracy, with parent etc.
40561  * or you can just use this to render a single compoent to a dom element
40562  * MYPART.render(Roo.Element | String(id) | dom_element )
40563  * 
40564  * @extends Roo.util.Observable
40565  * @constructor
40566  * @param cfg {Object} configuration of component
40567  * 
40568  */
40569 Roo.XComponent = function(cfg) {
40570     Roo.apply(this, cfg);
40571     this.addEvents({ 
40572         /**
40573              * @event built
40574              * Fires when this the componnt is built
40575              * @param {Roo.XComponent} c the component
40576              */
40577         'built' : true
40578         
40579     });
40580     this.region = this.region || 'center'; // default..
40581     Roo.XComponent.register(this);
40582     this.modules = false;
40583     this.el = false; // where the layout goes..
40584     
40585     
40586 }
40587 Roo.extend(Roo.XComponent, Roo.util.Observable, {
40588     /**
40589      * @property el
40590      * The created element (with Roo.factory())
40591      * @type {Roo.Layout}
40592      */
40593     el  : false,
40594     
40595     /**
40596      * @property el
40597      * for BC  - use el in new code
40598      * @type {Roo.Layout}
40599      */
40600     panel : false,
40601     
40602     /**
40603      * @property layout
40604      * for BC  - use el in new code
40605      * @type {Roo.Layout}
40606      */
40607     layout : false,
40608     
40609      /**
40610      * @cfg {Function|boolean} disabled
40611      * If this module is disabled by some rule, return true from the funtion
40612      */
40613     disabled : false,
40614     
40615     /**
40616      * @cfg {String} parent 
40617      * Name of parent element which it get xtype added to..
40618      */
40619     parent: false,
40620     
40621     /**
40622      * @cfg {String} order
40623      * Used to set the order in which elements are created (usefull for multiple tabs)
40624      */
40625     
40626     order : false,
40627     /**
40628      * @cfg {String} name
40629      * String to display while loading.
40630      */
40631     name : false,
40632     /**
40633      * @cfg {String} region
40634      * Region to render component to (defaults to center)
40635      */
40636     region : 'center',
40637     
40638     /**
40639      * @cfg {Array} items
40640      * A single item array - the first element is the root of the tree..
40641      * It's done this way to stay compatible with the Xtype system...
40642      */
40643     items : false,
40644     
40645     /**
40646      * @property _tree
40647      * The method that retuns the tree of parts that make up this compoennt 
40648      * @type {function}
40649      */
40650     _tree  : false,
40651     
40652      /**
40653      * render
40654      * render element to dom or tree
40655      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40656      */
40657     
40658     render : function(el)
40659     {
40660         
40661         el = el || false;
40662         var hp = this.parent ? 1 : 0;
40663         
40664         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40665             // if parent is a '#.....' string, then let's use that..
40666             var ename = this.parent.substr(1)
40667             this.parent = false;
40668             el = Roo.get(ename);
40669             if (!el) {
40670                 Roo.log("Warning - element can not be found :#" + ename );
40671                 return;
40672             }
40673         }
40674         
40675         
40676         if (!this.parent) {
40677             
40678             el = el ? Roo.get(el) : false;      
40679             
40680             // it's a top level one..
40681             this.parent =  {
40682                 el : new Roo.BorderLayout(el || document.body, {
40683                 
40684                      center: {
40685                          titlebar: false,
40686                          autoScroll:false,
40687                          closeOnTab: true,
40688                          tabPosition: 'top',
40689                           //resizeTabs: true,
40690                          alwaysShowTabs: el && hp? false :  true,
40691                          hideTabs: el || !hp ? true :  false,
40692                          minTabWidth: 140
40693                      }
40694                  })
40695             }
40696         }
40697         
40698                 if (!this.parent.el) {
40699                         // probably an old style ctor, which has been disabled.
40700                         return;
40701                         
40702                 }
40703                 // The 'tree' method is  '_tree now' 
40704             
40705         var tree = this._tree ? this._tree() : this.tree();
40706         tree.region = tree.region || this.region;
40707         this.el = this.parent.el.addxtype(tree);
40708         this.fireEvent('built', this);
40709         
40710         this.panel = this.el;
40711         this.layout = this.panel.layout;
40712                 this.parentLayout = this.parent.layout  || false;  
40713          
40714     }
40715     
40716 });
40717
40718 Roo.apply(Roo.XComponent, {
40719     /**
40720      * @property  hideProgress
40721      * true to disable the building progress bar.. usefull on single page renders.
40722      * @type Boolean
40723      */
40724     hideProgress : false,
40725     /**
40726      * @property  buildCompleted
40727      * True when the builder has completed building the interface.
40728      * @type Boolean
40729      */
40730     buildCompleted : false,
40731      
40732     /**
40733      * @property  topModule
40734      * the upper most module - uses document.element as it's constructor.
40735      * @type Object
40736      */
40737      
40738     topModule  : false,
40739       
40740     /**
40741      * @property  modules
40742      * array of modules to be created by registration system.
40743      * @type {Array} of Roo.XComponent
40744      */
40745     
40746     modules : [],
40747     /**
40748      * @property  elmodules
40749      * array of modules to be created by which use #ID 
40750      * @type {Array} of Roo.XComponent
40751      */
40752      
40753     elmodules : [],
40754
40755     
40756     /**
40757      * Register components to be built later.
40758      *
40759      * This solves the following issues
40760      * - Building is not done on page load, but after an authentication process has occured.
40761      * - Interface elements are registered on page load
40762      * - Parent Interface elements may not be loaded before child, so this handles that..
40763      * 
40764      *
40765      * example:
40766      * 
40767      * MyApp.register({
40768           order : '000001',
40769           module : 'Pman.Tab.projectMgr',
40770           region : 'center',
40771           parent : 'Pman.layout',
40772           disabled : false,  // or use a function..
40773         })
40774      
40775      * * @param {Object} details about module
40776      */
40777     register : function(obj) {
40778                 
40779         Roo.XComponent.event.fireEvent('register', obj);
40780         switch(typeof(obj.disabled) ) {
40781                 
40782             case 'undefined':
40783                 break;
40784             
40785             case 'function':
40786                 if ( obj.disabled() ) {
40787                         return;
40788                 }
40789                 break;
40790             
40791             default:
40792                 if (obj.disabled) {
40793                         return;
40794                 }
40795                 break;
40796         }
40797                 
40798         this.modules.push(obj);
40799          
40800     },
40801     /**
40802      * convert a string to an object..
40803      * eg. 'AAA.BBB' -> finds AAA.BBB
40804
40805      */
40806     
40807     toObject : function(str)
40808     {
40809         if (!str || typeof(str) == 'object') {
40810             return str;
40811         }
40812         if (str.substring(0,1) == '#') {
40813             return str;
40814         }
40815
40816         var ar = str.split('.');
40817         var rt, o;
40818         rt = ar.shift();
40819             /** eval:var:o */
40820         try {
40821             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40822         } catch (e) {
40823             throw "Module not found : " + str;
40824         }
40825         
40826         if (o === false) {
40827             throw "Module not found : " + str;
40828         }
40829         Roo.each(ar, function(e) {
40830             if (typeof(o[e]) == 'undefined') {
40831                 throw "Module not found : " + str;
40832             }
40833             o = o[e];
40834         });
40835         
40836         return o;
40837         
40838     },
40839     
40840     
40841     /**
40842      * move modules into their correct place in the tree..
40843      * 
40844      */
40845     preBuild : function ()
40846     {
40847         var _t = this;
40848         Roo.each(this.modules , function (obj)
40849         {
40850             Roo.XComponent.event.fireEvent('beforebuild', obj);
40851             
40852             var opar = obj.parent;
40853             try { 
40854                 obj.parent = this.toObject(opar);
40855             } catch(e) {
40856                 Roo.log("parent:toObject failed: " + e.toString());
40857                 return;
40858             }
40859             
40860             if (!obj.parent) {
40861                 Roo.debug && Roo.log("GOT top level module");
40862                 Roo.debug && Roo.log(obj);
40863                 obj.modules = new Roo.util.MixedCollection(false, 
40864                     function(o) { return o.order + '' }
40865                 );
40866                 this.topModule = obj;
40867                 return;
40868             }
40869                         // parent is a string (usually a dom element name..)
40870             if (typeof(obj.parent) == 'string') {
40871                 this.elmodules.push(obj);
40872                 return;
40873             }
40874             if (obj.parent.constructor != Roo.XComponent) {
40875                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40876             }
40877             if (!obj.parent.modules) {
40878                 obj.parent.modules = new Roo.util.MixedCollection(false, 
40879                     function(o) { return o.order + '' }
40880                 );
40881             }
40882             if (obj.parent.disabled) {
40883                 obj.disabled = true;
40884             }
40885             obj.parent.modules.add(obj);
40886         }, this);
40887     },
40888     
40889      /**
40890      * make a list of modules to build.
40891      * @return {Array} list of modules. 
40892      */ 
40893     
40894     buildOrder : function()
40895     {
40896         var _this = this;
40897         var cmp = function(a,b) {   
40898             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40899         };
40900         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40901             throw "No top level modules to build";
40902         }
40903         
40904         // make a flat list in order of modules to build.
40905         var mods = this.topModule ? [ this.topModule ] : [];
40906                 
40907         
40908         // elmodules (is a list of DOM based modules )
40909         Roo.each(this.elmodules, function(e) {
40910             mods.push(e);
40911             if (!this.topModule &&
40912                 typeof(e.parent) == 'string' &&
40913                 e.parent.substring(0,1) == '#' &&
40914                 Roo.get(e.parent.substr(1))
40915                ) {
40916                 
40917                 _this.topModule = e;
40918             }
40919             
40920         });
40921
40922         
40923         // add modules to their parents..
40924         var addMod = function(m) {
40925             Roo.debug && Roo.log("build Order: add: " + m.name);
40926                 
40927             mods.push(m);
40928             if (m.modules && !m.disabled) {
40929                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40930                 m.modules.keySort('ASC',  cmp );
40931                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40932     
40933                 m.modules.each(addMod);
40934             } else {
40935                 Roo.debug && Roo.log("build Order: no child modules");
40936             }
40937             // not sure if this is used any more..
40938             if (m.finalize) {
40939                 m.finalize.name = m.name + " (clean up) ";
40940                 mods.push(m.finalize);
40941             }
40942             
40943         }
40944         if (this.topModule && this.topModule.modules) { 
40945             this.topModule.modules.keySort('ASC',  cmp );
40946             this.topModule.modules.each(addMod);
40947         } 
40948         return mods;
40949     },
40950     
40951      /**
40952      * Build the registered modules.
40953      * @param {Object} parent element.
40954      * @param {Function} optional method to call after module has been added.
40955      * 
40956      */ 
40957    
40958     build : function() 
40959     {
40960         
40961         this.preBuild();
40962         var mods = this.buildOrder();
40963       
40964         //this.allmods = mods;
40965         //Roo.debug && Roo.log(mods);
40966         //return;
40967         if (!mods.length) { // should not happen
40968             throw "NO modules!!!";
40969         }
40970         
40971         
40972         var msg = "Building Interface...";
40973         // flash it up as modal - so we store the mask!?
40974         if (!this.hideProgress) {
40975             Roo.MessageBox.show({ title: 'loading' });
40976             Roo.MessageBox.show({
40977                title: "Please wait...",
40978                msg: msg,
40979                width:450,
40980                progress:true,
40981                closable:false,
40982                modal: false
40983               
40984             });
40985         }
40986         var total = mods.length;
40987         
40988         var _this = this;
40989         var progressRun = function() {
40990             if (!mods.length) {
40991                 Roo.debug && Roo.log('hide?');
40992                 if (!this.hideProgress) {
40993                     Roo.MessageBox.hide();
40994                 }
40995                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
40996                 
40997                 // THE END...
40998                 return false;   
40999             }
41000             
41001             var m = mods.shift();
41002             
41003             
41004             Roo.debug && Roo.log(m);
41005             // not sure if this is supported any more.. - modules that are are just function
41006             if (typeof(m) == 'function') { 
41007                 m.call(this);
41008                 return progressRun.defer(10, _this);
41009             } 
41010             
41011             
41012             msg = "Building Interface " + (total  - mods.length) + 
41013                     " of " + total + 
41014                     (m.name ? (' - ' + m.name) : '');
41015                         Roo.debug && Roo.log(msg);
41016             if (!this.hideProgress) { 
41017                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
41018             }
41019             
41020          
41021             // is the module disabled?
41022             var disabled = (typeof(m.disabled) == 'function') ?
41023                 m.disabled.call(m.module.disabled) : m.disabled;    
41024             
41025             
41026             if (disabled) {
41027                 return progressRun(); // we do not update the display!
41028             }
41029             
41030             // now build 
41031             
41032                         
41033                         
41034             m.render();
41035             // it's 10 on top level, and 1 on others??? why...
41036             return progressRun.defer(10, _this);
41037              
41038         }
41039         progressRun.defer(1, _this);
41040      
41041         
41042         
41043     },
41044         
41045         
41046         /**
41047          * Event Object.
41048          *
41049          *
41050          */
41051         event: false, 
41052     /**
41053          * wrapper for event.on - aliased later..  
41054          * Typically use to register a event handler for register:
41055          *
41056          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
41057          *
41058          */
41059     on : false
41060    
41061     
41062     
41063 });
41064
41065 Roo.XComponent.event = new Roo.util.Observable({
41066                 events : { 
41067                         /**
41068                          * @event register
41069                          * Fires when an Component is registered,
41070                          * set the disable property on the Component to stop registration.
41071                          * @param {Roo.XComponent} c the component being registerd.
41072                          * 
41073                          */
41074                         'register' : true,
41075             /**
41076                          * @event beforebuild
41077                          * Fires before each Component is built
41078                          * can be used to apply permissions.
41079                          * @param {Roo.XComponent} c the component being registerd.
41080                          * 
41081                          */
41082                         'beforebuild' : true,
41083                         /**
41084                          * @event buildcomplete
41085                          * Fires on the top level element when all elements have been built
41086                          * @param {Roo.XComponent} the top level component.
41087                          */
41088                         'buildcomplete' : true
41089                         
41090                 }
41091 });
41092
41093 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
41094