examples/resizable/basic.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 resize
13533          * Fired after a resize.
13534          * @param {Roo.Resizable} this
13535          * @param {Number} width The new width
13536          * @param {Number} height The new height
13537          * @param {Roo.EventObject} e The mouseup event
13538          */
13539         "resize" : true
13540     });
13541
13542     if(this.width !== null && this.height !== null){
13543         this.resizeTo(this.width, this.height);
13544     }else{
13545         this.updateChildSize();
13546     }
13547     if(Roo.isIE){
13548         this.el.dom.style.zoom = 1;
13549     }
13550     Roo.Resizable.superclass.constructor.call(this);
13551 };
13552
13553 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13554         resizeChild : false,
13555         adjustments : [0, 0],
13556         minWidth : 5,
13557         minHeight : 5,
13558         maxWidth : 10000,
13559         maxHeight : 10000,
13560         enabled : true,
13561         animate : false,
13562         duration : .35,
13563         dynamic : false,
13564         handles : false,
13565         multiDirectional : false,
13566         disableTrackOver : false,
13567         easing : 'easeOutStrong',
13568         widthIncrement : 0,
13569         heightIncrement : 0,
13570         pinned : false,
13571         width : null,
13572         height : null,
13573         preserveRatio : false,
13574         transparent: false,
13575         minX: 0,
13576         minY: 0,
13577         draggable: false,
13578
13579         /**
13580          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13581          */
13582         constrainTo: undefined,
13583         /**
13584          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13585          */
13586         resizeRegion: undefined,
13587
13588
13589     /**
13590      * Perform a manual resize
13591      * @param {Number} width
13592      * @param {Number} height
13593      */
13594     resizeTo : function(width, height){
13595         this.el.setSize(width, height);
13596         this.updateChildSize();
13597         this.fireEvent("resize", this, width, height, null);
13598     },
13599
13600     // private
13601     startSizing : function(e, handle){
13602         this.fireEvent("beforeresize", this, e);
13603         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13604
13605             if(!this.overlay){
13606                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13607                 this.overlay.unselectable();
13608                 this.overlay.enableDisplayMode("block");
13609                 this.overlay.on("mousemove", this.onMouseMove, this);
13610                 this.overlay.on("mouseup", this.onMouseUp, this);
13611             }
13612             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13613
13614             this.resizing = true;
13615             this.startBox = this.el.getBox();
13616             this.startPoint = e.getXY();
13617             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13618                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13619
13620             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13621             this.overlay.show();
13622
13623             if(this.constrainTo) {
13624                 var ct = Roo.get(this.constrainTo);
13625                 this.resizeRegion = ct.getRegion().adjust(
13626                     ct.getFrameWidth('t'),
13627                     ct.getFrameWidth('l'),
13628                     -ct.getFrameWidth('b'),
13629                     -ct.getFrameWidth('r')
13630                 );
13631             }
13632
13633             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13634             this.proxy.show();
13635             this.proxy.setBox(this.startBox);
13636             if(!this.dynamic){
13637                 this.proxy.setStyle('visibility', 'visible');
13638             }
13639         }
13640     },
13641
13642     // private
13643     onMouseDown : function(handle, e){
13644         if(this.enabled){
13645             e.stopEvent();
13646             this.activeHandle = handle;
13647             this.startSizing(e, handle);
13648         }
13649     },
13650
13651     // private
13652     onMouseUp : function(e){
13653         var size = this.resizeElement();
13654         this.resizing = false;
13655         this.handleOut();
13656         this.overlay.hide();
13657         this.proxy.hide();
13658         this.fireEvent("resize", this, size.width, size.height, e);
13659     },
13660
13661     // private
13662     updateChildSize : function(){
13663         
13664         if(this.resizeChild){
13665             Roo.log('in?');
13666             var el = this.el;
13667             var child = this.resizeChild;
13668             var adj = this.adjustments;
13669             if(el.dom.offsetWidth){
13670                 var b = el.getSize(true);
13671                 child.setSize(b.width+adj[0], b.height+adj[1]);
13672             }
13673             // Second call here for IE
13674             // The first call enables instant resizing and
13675             // the second call corrects scroll bars if they
13676             // exist
13677             if(Roo.isIE){
13678                 setTimeout(function(){
13679                     if(el.dom.offsetWidth){
13680                         var b = el.getSize(true);
13681                         child.setSize(b.width+adj[0], b.height+adj[1]);
13682                     }
13683                 }, 10);
13684             }
13685         }
13686     },
13687
13688     // private
13689     snap : function(value, inc, min){
13690         if(!inc || !value) return value;
13691         var newValue = value;
13692         var m = value % inc;
13693         if(m > 0){
13694             if(m > (inc/2)){
13695                 newValue = value + (inc-m);
13696             }else{
13697                 newValue = value - m;
13698             }
13699         }
13700         return Math.max(min, newValue);
13701     },
13702
13703     // private
13704     resizeElement : function(){
13705         var box = this.proxy.getBox();
13706         if(this.updateBox){
13707             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13708         }else{
13709             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13710         }
13711         this.updateChildSize();
13712         if(!this.dynamic){
13713             this.proxy.hide();
13714         }
13715         return box;
13716     },
13717
13718     // private
13719     constrain : function(v, diff, m, mx){
13720         if(v - diff < m){
13721             diff = v - m;
13722         }else if(v - diff > mx){
13723             diff = mx - v;
13724         }
13725         return diff;
13726     },
13727
13728     // private
13729     onMouseMove : function(e){
13730         if(this.enabled){
13731             try{// try catch so if something goes wrong the user doesn't get hung
13732
13733             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13734                 return;
13735             }
13736
13737             //var curXY = this.startPoint;
13738             var curSize = this.curSize || this.startBox;
13739             var x = this.startBox.x, y = this.startBox.y;
13740             var ox = x, oy = y;
13741             var w = curSize.width, h = curSize.height;
13742             var ow = w, oh = h;
13743             var mw = this.minWidth, mh = this.minHeight;
13744             var mxw = this.maxWidth, mxh = this.maxHeight;
13745             var wi = this.widthIncrement;
13746             var hi = this.heightIncrement;
13747
13748             var eventXY = e.getXY();
13749             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13750             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13751
13752             var pos = this.activeHandle.position;
13753
13754             switch(pos){
13755                 case "east":
13756                     w += diffX;
13757                     w = Math.min(Math.max(mw, w), mxw);
13758                     break;
13759              
13760                 case "south":
13761                     h += diffY;
13762                     h = Math.min(Math.max(mh, h), mxh);
13763                     break;
13764                 case "southeast":
13765                     w += diffX;
13766                     h += diffY;
13767                     w = Math.min(Math.max(mw, w), mxw);
13768                     h = Math.min(Math.max(mh, h), mxh);
13769                     break;
13770                 case "north":
13771                     diffY = this.constrain(h, diffY, mh, mxh);
13772                     y += diffY;
13773                     h -= diffY;
13774                     break;
13775                 case "hdrag":
13776                     
13777                     if (wi) {
13778                         var adiffX = Math.abs(diffX);
13779                         var sub = (adiffX % wi); // how much 
13780                         if (sub > (wi/2)) { // far enough to snap
13781                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13782                         } else {
13783                             // remove difference.. 
13784                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13785                         }
13786                     }
13787                     x += diffX;
13788                     x = Math.max(this.minX, x);
13789                     break;
13790                 case "west":
13791                     diffX = this.constrain(w, diffX, mw, mxw);
13792                     x += diffX;
13793                     w -= diffX;
13794                     break;
13795                 case "northeast":
13796                     w += diffX;
13797                     w = Math.min(Math.max(mw, w), mxw);
13798                     diffY = this.constrain(h, diffY, mh, mxh);
13799                     y += diffY;
13800                     h -= diffY;
13801                     break;
13802                 case "northwest":
13803                     diffX = this.constrain(w, diffX, mw, mxw);
13804                     diffY = this.constrain(h, diffY, mh, mxh);
13805                     y += diffY;
13806                     h -= diffY;
13807                     x += diffX;
13808                     w -= diffX;
13809                     break;
13810                case "southwest":
13811                     diffX = this.constrain(w, diffX, mw, mxw);
13812                     h += diffY;
13813                     h = Math.min(Math.max(mh, h), mxh);
13814                     x += diffX;
13815                     w -= diffX;
13816                     break;
13817             }
13818
13819             var sw = this.snap(w, wi, mw);
13820             var sh = this.snap(h, hi, mh);
13821             if(sw != w || sh != h){
13822                 switch(pos){
13823                     case "northeast":
13824                         y -= sh - h;
13825                     break;
13826                     case "north":
13827                         y -= sh - h;
13828                         break;
13829                     case "southwest":
13830                         x -= sw - w;
13831                     break;
13832                     case "west":
13833                         x -= sw - w;
13834                         break;
13835                     case "northwest":
13836                         x -= sw - w;
13837                         y -= sh - h;
13838                     break;
13839                 }
13840                 w = sw;
13841                 h = sh;
13842             }
13843
13844             if(this.preserveRatio){
13845                 switch(pos){
13846                     case "southeast":
13847                     case "east":
13848                         h = oh * (w/ow);
13849                         h = Math.min(Math.max(mh, h), mxh);
13850                         w = ow * (h/oh);
13851                        break;
13852                     case "south":
13853                         w = ow * (h/oh);
13854                         w = Math.min(Math.max(mw, w), mxw);
13855                         h = oh * (w/ow);
13856                         break;
13857                     case "northeast":
13858                         w = ow * (h/oh);
13859                         w = Math.min(Math.max(mw, w), mxw);
13860                         h = oh * (w/ow);
13861                     break;
13862                     case "north":
13863                         var tw = w;
13864                         w = ow * (h/oh);
13865                         w = Math.min(Math.max(mw, w), mxw);
13866                         h = oh * (w/ow);
13867                         x += (tw - w) / 2;
13868                         break;
13869                     case "southwest":
13870                         h = oh * (w/ow);
13871                         h = Math.min(Math.max(mh, h), mxh);
13872                         var tw = w;
13873                         w = ow * (h/oh);
13874                         x += tw - w;
13875                         break;
13876                     case "west":
13877                         var th = h;
13878                         h = oh * (w/ow);
13879                         h = Math.min(Math.max(mh, h), mxh);
13880                         y += (th - h) / 2;
13881                         var tw = w;
13882                         w = ow * (h/oh);
13883                         x += tw - w;
13884                        break;
13885                     case "northwest":
13886                         var tw = w;
13887                         var th = h;
13888                         h = oh * (w/ow);
13889                         h = Math.min(Math.max(mh, h), mxh);
13890                         w = ow * (h/oh);
13891                         y += th - h;
13892                         x += tw - w;
13893                        break;
13894
13895                 }
13896             }
13897             if (pos == 'hdrag') {
13898                 w = ow;
13899             }
13900             this.proxy.setBounds(x, y, w, h);
13901             if(this.dynamic){
13902                 this.resizeElement();
13903             }
13904             }catch(e){}
13905         }
13906     },
13907
13908     // private
13909     handleOver : function(){
13910         if(this.enabled){
13911             this.el.addClass("x-resizable-over");
13912         }
13913     },
13914
13915     // private
13916     handleOut : function(){
13917         if(!this.resizing){
13918             this.el.removeClass("x-resizable-over");
13919         }
13920     },
13921
13922     /**
13923      * Returns the element this component is bound to.
13924      * @return {Roo.Element}
13925      */
13926     getEl : function(){
13927         return this.el;
13928     },
13929
13930     /**
13931      * Returns the resizeChild element (or null).
13932      * @return {Roo.Element}
13933      */
13934     getResizeChild : function(){
13935         return this.resizeChild;
13936     },
13937
13938     /**
13939      * Destroys this resizable. If the element was wrapped and
13940      * removeEl is not true then the element remains.
13941      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13942      */
13943     destroy : function(removeEl){
13944         this.proxy.remove();
13945         if(this.overlay){
13946             this.overlay.removeAllListeners();
13947             this.overlay.remove();
13948         }
13949         var ps = Roo.Resizable.positions;
13950         for(var k in ps){
13951             if(typeof ps[k] != "function" && this[ps[k]]){
13952                 var h = this[ps[k]];
13953                 h.el.removeAllListeners();
13954                 h.el.remove();
13955             }
13956         }
13957         if(removeEl){
13958             this.el.update("");
13959             this.el.remove();
13960         }
13961     }
13962 });
13963
13964 // private
13965 // hash to map config positions to true positions
13966 Roo.Resizable.positions = {
13967     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13968     hd: "hdrag"
13969 };
13970
13971 // private
13972 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13973     if(!this.tpl){
13974         // only initialize the template if resizable is used
13975         var tpl = Roo.DomHelper.createTemplate(
13976             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13977         );
13978         tpl.compile();
13979         Roo.Resizable.Handle.prototype.tpl = tpl;
13980     }
13981     this.position = pos;
13982     this.rz = rz;
13983     // show north drag fro topdra
13984     var handlepos = pos == 'hdrag' ? 'north' : pos;
13985     
13986     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13987     if (pos == 'hdrag') {
13988         this.el.setStyle('cursor', 'pointer');
13989     }
13990     this.el.unselectable();
13991     if(transparent){
13992         this.el.setOpacity(0);
13993     }
13994     this.el.on("mousedown", this.onMouseDown, this);
13995     if(!disableTrackOver){
13996         this.el.on("mouseover", this.onMouseOver, this);
13997         this.el.on("mouseout", this.onMouseOut, this);
13998     }
13999 };
14000
14001 // private
14002 Roo.Resizable.Handle.prototype = {
14003     afterResize : function(rz){
14004         // do nothing
14005     },
14006     // private
14007     onMouseDown : function(e){
14008         this.rz.onMouseDown(this, e);
14009     },
14010     // private
14011     onMouseOver : function(e){
14012         this.rz.handleOver(this, e);
14013     },
14014     // private
14015     onMouseOut : function(e){
14016         this.rz.handleOut(this, e);
14017     }
14018 };/*
14019  * Based on:
14020  * Ext JS Library 1.1.1
14021  * Copyright(c) 2006-2007, Ext JS, LLC.
14022  *
14023  * Originally Released Under LGPL - original licence link has changed is not relivant.
14024  *
14025  * Fork - LGPL
14026  * <script type="text/javascript">
14027  */
14028
14029 /**
14030  * @class Roo.Editor
14031  * @extends Roo.Component
14032  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14033  * @constructor
14034  * Create a new Editor
14035  * @param {Roo.form.Field} field The Field object (or descendant)
14036  * @param {Object} config The config object
14037  */
14038 Roo.Editor = function(field, config){
14039     Roo.Editor.superclass.constructor.call(this, config);
14040     this.field = field;
14041     this.addEvents({
14042         /**
14043              * @event beforestartedit
14044              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14045              * false from the handler of this event.
14046              * @param {Editor} this
14047              * @param {Roo.Element} boundEl The underlying element bound to this editor
14048              * @param {Mixed} value The field value being set
14049              */
14050         "beforestartedit" : true,
14051         /**
14052              * @event startedit
14053              * Fires when this editor is displayed
14054              * @param {Roo.Element} boundEl The underlying element bound to this editor
14055              * @param {Mixed} value The starting field value
14056              */
14057         "startedit" : true,
14058         /**
14059              * @event beforecomplete
14060              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14061              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14062              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14063              * event will not fire since no edit actually occurred.
14064              * @param {Editor} this
14065              * @param {Mixed} value The current field value
14066              * @param {Mixed} startValue The original field value
14067              */
14068         "beforecomplete" : true,
14069         /**
14070              * @event complete
14071              * Fires after editing is complete and any changed value has been written to the underlying field.
14072              * @param {Editor} this
14073              * @param {Mixed} value The current field value
14074              * @param {Mixed} startValue The original field value
14075              */
14076         "complete" : true,
14077         /**
14078          * @event specialkey
14079          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14080          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14081          * @param {Roo.form.Field} this
14082          * @param {Roo.EventObject} e The event object
14083          */
14084         "specialkey" : true
14085     });
14086 };
14087
14088 Roo.extend(Roo.Editor, Roo.Component, {
14089     /**
14090      * @cfg {Boolean/String} autosize
14091      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14092      * or "height" to adopt the height only (defaults to false)
14093      */
14094     /**
14095      * @cfg {Boolean} revertInvalid
14096      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14097      * validation fails (defaults to true)
14098      */
14099     /**
14100      * @cfg {Boolean} ignoreNoChange
14101      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14102      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14103      * will never be ignored.
14104      */
14105     /**
14106      * @cfg {Boolean} hideEl
14107      * False to keep the bound element visible while the editor is displayed (defaults to true)
14108      */
14109     /**
14110      * @cfg {Mixed} value
14111      * The data value of the underlying field (defaults to "")
14112      */
14113     value : "",
14114     /**
14115      * @cfg {String} alignment
14116      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14117      */
14118     alignment: "c-c?",
14119     /**
14120      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14121      * for bottom-right shadow (defaults to "frame")
14122      */
14123     shadow : "frame",
14124     /**
14125      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14126      */
14127     constrain : false,
14128     /**
14129      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14130      */
14131     completeOnEnter : false,
14132     /**
14133      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14134      */
14135     cancelOnEsc : false,
14136     /**
14137      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14138      */
14139     updateEl : false,
14140
14141     // private
14142     onRender : function(ct, position){
14143         this.el = new Roo.Layer({
14144             shadow: this.shadow,
14145             cls: "x-editor",
14146             parentEl : ct,
14147             shim : this.shim,
14148             shadowOffset:4,
14149             id: this.id,
14150             constrain: this.constrain
14151         });
14152         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14153         if(this.field.msgTarget != 'title'){
14154             this.field.msgTarget = 'qtip';
14155         }
14156         this.field.render(this.el);
14157         if(Roo.isGecko){
14158             this.field.el.dom.setAttribute('autocomplete', 'off');
14159         }
14160         this.field.on("specialkey", this.onSpecialKey, this);
14161         if(this.swallowKeys){
14162             this.field.el.swallowEvent(['keydown','keypress']);
14163         }
14164         this.field.show();
14165         this.field.on("blur", this.onBlur, this);
14166         if(this.field.grow){
14167             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14168         }
14169     },
14170
14171     onSpecialKey : function(field, e)
14172     {
14173         //Roo.log('editor onSpecialKey');
14174         if(this.completeOnEnter && e.getKey() == e.ENTER){
14175             e.stopEvent();
14176             this.completeEdit();
14177             return;
14178         }
14179         // do not fire special key otherwise it might hide close the editor...
14180         if(e.getKey() == e.ENTER){    
14181             return;
14182         }
14183         if(this.cancelOnEsc && e.getKey() == e.ESC){
14184             this.cancelEdit();
14185             return;
14186         } 
14187         this.fireEvent('specialkey', field, e);
14188     
14189     },
14190
14191     /**
14192      * Starts the editing process and shows the editor.
14193      * @param {String/HTMLElement/Element} el The element to edit
14194      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14195       * to the innerHTML of el.
14196      */
14197     startEdit : function(el, value){
14198         if(this.editing){
14199             this.completeEdit();
14200         }
14201         this.boundEl = Roo.get(el);
14202         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14203         if(!this.rendered){
14204             this.render(this.parentEl || document.body);
14205         }
14206         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14207             return;
14208         }
14209         this.startValue = v;
14210         this.field.setValue(v);
14211         if(this.autoSize){
14212             var sz = this.boundEl.getSize();
14213             switch(this.autoSize){
14214                 case "width":
14215                 this.setSize(sz.width,  "");
14216                 break;
14217                 case "height":
14218                 this.setSize("",  sz.height);
14219                 break;
14220                 default:
14221                 this.setSize(sz.width,  sz.height);
14222             }
14223         }
14224         this.el.alignTo(this.boundEl, this.alignment);
14225         this.editing = true;
14226         if(Roo.QuickTips){
14227             Roo.QuickTips.disable();
14228         }
14229         this.show();
14230     },
14231
14232     /**
14233      * Sets the height and width of this editor.
14234      * @param {Number} width The new width
14235      * @param {Number} height The new height
14236      */
14237     setSize : function(w, h){
14238         this.field.setSize(w, h);
14239         if(this.el){
14240             this.el.sync();
14241         }
14242     },
14243
14244     /**
14245      * Realigns the editor to the bound field based on the current alignment config value.
14246      */
14247     realign : function(){
14248         this.el.alignTo(this.boundEl, this.alignment);
14249     },
14250
14251     /**
14252      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14253      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14254      */
14255     completeEdit : function(remainVisible){
14256         if(!this.editing){
14257             return;
14258         }
14259         var v = this.getValue();
14260         if(this.revertInvalid !== false && !this.field.isValid()){
14261             v = this.startValue;
14262             this.cancelEdit(true);
14263         }
14264         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14265             this.editing = false;
14266             this.hide();
14267             return;
14268         }
14269         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14270             this.editing = false;
14271             if(this.updateEl && this.boundEl){
14272                 this.boundEl.update(v);
14273             }
14274             if(remainVisible !== true){
14275                 this.hide();
14276             }
14277             this.fireEvent("complete", this, v, this.startValue);
14278         }
14279     },
14280
14281     // private
14282     onShow : function(){
14283         this.el.show();
14284         if(this.hideEl !== false){
14285             this.boundEl.hide();
14286         }
14287         this.field.show();
14288         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14289             this.fixIEFocus = true;
14290             this.deferredFocus.defer(50, this);
14291         }else{
14292             this.field.focus();
14293         }
14294         this.fireEvent("startedit", this.boundEl, this.startValue);
14295     },
14296
14297     deferredFocus : function(){
14298         if(this.editing){
14299             this.field.focus();
14300         }
14301     },
14302
14303     /**
14304      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14305      * reverted to the original starting value.
14306      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14307      * cancel (defaults to false)
14308      */
14309     cancelEdit : function(remainVisible){
14310         if(this.editing){
14311             this.setValue(this.startValue);
14312             if(remainVisible !== true){
14313                 this.hide();
14314             }
14315         }
14316     },
14317
14318     // private
14319     onBlur : function(){
14320         if(this.allowBlur !== true && this.editing){
14321             this.completeEdit();
14322         }
14323     },
14324
14325     // private
14326     onHide : function(){
14327         if(this.editing){
14328             this.completeEdit();
14329             return;
14330         }
14331         this.field.blur();
14332         if(this.field.collapse){
14333             this.field.collapse();
14334         }
14335         this.el.hide();
14336         if(this.hideEl !== false){
14337             this.boundEl.show();
14338         }
14339         if(Roo.QuickTips){
14340             Roo.QuickTips.enable();
14341         }
14342     },
14343
14344     /**
14345      * Sets the data value of the editor
14346      * @param {Mixed} value Any valid value supported by the underlying field
14347      */
14348     setValue : function(v){
14349         this.field.setValue(v);
14350     },
14351
14352     /**
14353      * Gets the data value of the editor
14354      * @return {Mixed} The data value
14355      */
14356     getValue : function(){
14357         return this.field.getValue();
14358     }
14359 });/*
14360  * Based on:
14361  * Ext JS Library 1.1.1
14362  * Copyright(c) 2006-2007, Ext JS, LLC.
14363  *
14364  * Originally Released Under LGPL - original licence link has changed is not relivant.
14365  *
14366  * Fork - LGPL
14367  * <script type="text/javascript">
14368  */
14369  
14370 /**
14371  * @class Roo.BasicDialog
14372  * @extends Roo.util.Observable
14373  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14374  * <pre><code>
14375 var dlg = new Roo.BasicDialog("my-dlg", {
14376     height: 200,
14377     width: 300,
14378     minHeight: 100,
14379     minWidth: 150,
14380     modal: true,
14381     proxyDrag: true,
14382     shadow: true
14383 });
14384 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14385 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14386 dlg.addButton('Cancel', dlg.hide, dlg);
14387 dlg.show();
14388 </code></pre>
14389   <b>A Dialog should always be a direct child of the body element.</b>
14390  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14391  * @cfg {String} title Default text to display in the title bar (defaults to null)
14392  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14393  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14394  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14395  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14396  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14397  * (defaults to null with no animation)
14398  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14399  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14400  * property for valid values (defaults to 'all')
14401  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14402  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14403  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14404  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14405  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14406  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14407  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14408  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14409  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14410  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14411  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14412  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14413  * draggable = true (defaults to false)
14414  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14415  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14416  * shadow (defaults to false)
14417  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14418  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14419  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14420  * @cfg {Array} buttons Array of buttons
14421  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14422  * @constructor
14423  * Create a new BasicDialog.
14424  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14425  * @param {Object} config Configuration options
14426  */
14427 Roo.BasicDialog = function(el, config){
14428     this.el = Roo.get(el);
14429     var dh = Roo.DomHelper;
14430     if(!this.el && config && config.autoCreate){
14431         if(typeof config.autoCreate == "object"){
14432             if(!config.autoCreate.id){
14433                 config.autoCreate.id = el;
14434             }
14435             this.el = dh.append(document.body,
14436                         config.autoCreate, true);
14437         }else{
14438             this.el = dh.append(document.body,
14439                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14440         }
14441     }
14442     el = this.el;
14443     el.setDisplayed(true);
14444     el.hide = this.hideAction;
14445     this.id = el.id;
14446     el.addClass("x-dlg");
14447
14448     Roo.apply(this, config);
14449
14450     this.proxy = el.createProxy("x-dlg-proxy");
14451     this.proxy.hide = this.hideAction;
14452     this.proxy.setOpacity(.5);
14453     this.proxy.hide();
14454
14455     if(config.width){
14456         el.setWidth(config.width);
14457     }
14458     if(config.height){
14459         el.setHeight(config.height);
14460     }
14461     this.size = el.getSize();
14462     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14463         this.xy = [config.x,config.y];
14464     }else{
14465         this.xy = el.getCenterXY(true);
14466     }
14467     /** The header element @type Roo.Element */
14468     this.header = el.child("> .x-dlg-hd");
14469     /** The body element @type Roo.Element */
14470     this.body = el.child("> .x-dlg-bd");
14471     /** The footer element @type Roo.Element */
14472     this.footer = el.child("> .x-dlg-ft");
14473
14474     if(!this.header){
14475         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14476     }
14477     if(!this.body){
14478         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14479     }
14480
14481     this.header.unselectable();
14482     if(this.title){
14483         this.header.update(this.title);
14484     }
14485     // this element allows the dialog to be focused for keyboard event
14486     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14487     this.focusEl.swallowEvent("click", true);
14488
14489     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14490
14491     // wrap the body and footer for special rendering
14492     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14493     if(this.footer){
14494         this.bwrap.dom.appendChild(this.footer.dom);
14495     }
14496
14497     this.bg = this.el.createChild({
14498         tag: "div", cls:"x-dlg-bg",
14499         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14500     });
14501     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14502
14503
14504     if(this.autoScroll !== false && !this.autoTabs){
14505         this.body.setStyle("overflow", "auto");
14506     }
14507
14508     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14509
14510     if(this.closable !== false){
14511         this.el.addClass("x-dlg-closable");
14512         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14513         this.close.on("click", this.closeClick, this);
14514         this.close.addClassOnOver("x-dlg-close-over");
14515     }
14516     if(this.collapsible !== false){
14517         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14518         this.collapseBtn.on("click", this.collapseClick, this);
14519         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14520         this.header.on("dblclick", this.collapseClick, this);
14521     }
14522     if(this.resizable !== false){
14523         this.el.addClass("x-dlg-resizable");
14524         this.resizer = new Roo.Resizable(el, {
14525             minWidth: this.minWidth || 80,
14526             minHeight:this.minHeight || 80,
14527             handles: this.resizeHandles || "all",
14528             pinned: true
14529         });
14530         this.resizer.on("beforeresize", this.beforeResize, this);
14531         this.resizer.on("resize", this.onResize, this);
14532     }
14533     if(this.draggable !== false){
14534         el.addClass("x-dlg-draggable");
14535         if (!this.proxyDrag) {
14536             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14537         }
14538         else {
14539             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14540         }
14541         dd.setHandleElId(this.header.id);
14542         dd.endDrag = this.endMove.createDelegate(this);
14543         dd.startDrag = this.startMove.createDelegate(this);
14544         dd.onDrag = this.onDrag.createDelegate(this);
14545         dd.scroll = false;
14546         this.dd = dd;
14547     }
14548     if(this.modal){
14549         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14550         this.mask.enableDisplayMode("block");
14551         this.mask.hide();
14552         this.el.addClass("x-dlg-modal");
14553     }
14554     if(this.shadow){
14555         this.shadow = new Roo.Shadow({
14556             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14557             offset : this.shadowOffset
14558         });
14559     }else{
14560         this.shadowOffset = 0;
14561     }
14562     if(Roo.useShims && this.shim !== false){
14563         this.shim = this.el.createShim();
14564         this.shim.hide = this.hideAction;
14565         this.shim.hide();
14566     }else{
14567         this.shim = false;
14568     }
14569     if(this.autoTabs){
14570         this.initTabs();
14571     }
14572     if (this.buttons) { 
14573         var bts= this.buttons;
14574         this.buttons = [];
14575         Roo.each(bts, function(b) {
14576             this.addButton(b);
14577         }, this);
14578     }
14579     
14580     
14581     this.addEvents({
14582         /**
14583          * @event keydown
14584          * Fires when a key is pressed
14585          * @param {Roo.BasicDialog} this
14586          * @param {Roo.EventObject} e
14587          */
14588         "keydown" : true,
14589         /**
14590          * @event move
14591          * Fires when this dialog is moved by the user.
14592          * @param {Roo.BasicDialog} this
14593          * @param {Number} x The new page X
14594          * @param {Number} y The new page Y
14595          */
14596         "move" : true,
14597         /**
14598          * @event resize
14599          * Fires when this dialog is resized by the user.
14600          * @param {Roo.BasicDialog} this
14601          * @param {Number} width The new width
14602          * @param {Number} height The new height
14603          */
14604         "resize" : true,
14605         /**
14606          * @event beforehide
14607          * Fires before this dialog is hidden.
14608          * @param {Roo.BasicDialog} this
14609          */
14610         "beforehide" : true,
14611         /**
14612          * @event hide
14613          * Fires when this dialog is hidden.
14614          * @param {Roo.BasicDialog} this
14615          */
14616         "hide" : true,
14617         /**
14618          * @event beforeshow
14619          * Fires before this dialog is shown.
14620          * @param {Roo.BasicDialog} this
14621          */
14622         "beforeshow" : true,
14623         /**
14624          * @event show
14625          * Fires when this dialog is shown.
14626          * @param {Roo.BasicDialog} this
14627          */
14628         "show" : true
14629     });
14630     el.on("keydown", this.onKeyDown, this);
14631     el.on("mousedown", this.toFront, this);
14632     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14633     this.el.hide();
14634     Roo.DialogManager.register(this);
14635     Roo.BasicDialog.superclass.constructor.call(this);
14636 };
14637
14638 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14639     shadowOffset: Roo.isIE ? 6 : 5,
14640     minHeight: 80,
14641     minWidth: 200,
14642     minButtonWidth: 75,
14643     defaultButton: null,
14644     buttonAlign: "right",
14645     tabTag: 'div',
14646     firstShow: true,
14647
14648     /**
14649      * Sets the dialog title text
14650      * @param {String} text The title text to display
14651      * @return {Roo.BasicDialog} this
14652      */
14653     setTitle : function(text){
14654         this.header.update(text);
14655         return this;
14656     },
14657
14658     // private
14659     closeClick : function(){
14660         this.hide();
14661     },
14662
14663     // private
14664     collapseClick : function(){
14665         this[this.collapsed ? "expand" : "collapse"]();
14666     },
14667
14668     /**
14669      * Collapses the dialog to its minimized state (only the title bar is visible).
14670      * Equivalent to the user clicking the collapse dialog button.
14671      */
14672     collapse : function(){
14673         if(!this.collapsed){
14674             this.collapsed = true;
14675             this.el.addClass("x-dlg-collapsed");
14676             this.restoreHeight = this.el.getHeight();
14677             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14678         }
14679     },
14680
14681     /**
14682      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14683      * clicking the expand dialog button.
14684      */
14685     expand : function(){
14686         if(this.collapsed){
14687             this.collapsed = false;
14688             this.el.removeClass("x-dlg-collapsed");
14689             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14690         }
14691     },
14692
14693     /**
14694      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14695      * @return {Roo.TabPanel} The tabs component
14696      */
14697     initTabs : function(){
14698         var tabs = this.getTabs();
14699         while(tabs.getTab(0)){
14700             tabs.removeTab(0);
14701         }
14702         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14703             var dom = el.dom;
14704             tabs.addTab(Roo.id(dom), dom.title);
14705             dom.title = "";
14706         });
14707         tabs.activate(0);
14708         return tabs;
14709     },
14710
14711     // private
14712     beforeResize : function(){
14713         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14714     },
14715
14716     // private
14717     onResize : function(){
14718         this.refreshSize();
14719         this.syncBodyHeight();
14720         this.adjustAssets();
14721         this.focus();
14722         this.fireEvent("resize", this, this.size.width, this.size.height);
14723     },
14724
14725     // private
14726     onKeyDown : function(e){
14727         if(this.isVisible()){
14728             this.fireEvent("keydown", this, e);
14729         }
14730     },
14731
14732     /**
14733      * Resizes the dialog.
14734      * @param {Number} width
14735      * @param {Number} height
14736      * @return {Roo.BasicDialog} this
14737      */
14738     resizeTo : function(width, height){
14739         this.el.setSize(width, height);
14740         this.size = {width: width, height: height};
14741         this.syncBodyHeight();
14742         if(this.fixedcenter){
14743             this.center();
14744         }
14745         if(this.isVisible()){
14746             this.constrainXY();
14747             this.adjustAssets();
14748         }
14749         this.fireEvent("resize", this, width, height);
14750         return this;
14751     },
14752
14753
14754     /**
14755      * Resizes the dialog to fit the specified content size.
14756      * @param {Number} width
14757      * @param {Number} height
14758      * @return {Roo.BasicDialog} this
14759      */
14760     setContentSize : function(w, h){
14761         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14762         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14763         //if(!this.el.isBorderBox()){
14764             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14765             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14766         //}
14767         if(this.tabs){
14768             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14769             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14770         }
14771         this.resizeTo(w, h);
14772         return this;
14773     },
14774
14775     /**
14776      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14777      * executed in response to a particular key being pressed while the dialog is active.
14778      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14779      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14780      * @param {Function} fn The function to call
14781      * @param {Object} scope (optional) The scope of the function
14782      * @return {Roo.BasicDialog} this
14783      */
14784     addKeyListener : function(key, fn, scope){
14785         var keyCode, shift, ctrl, alt;
14786         if(typeof key == "object" && !(key instanceof Array)){
14787             keyCode = key["key"];
14788             shift = key["shift"];
14789             ctrl = key["ctrl"];
14790             alt = key["alt"];
14791         }else{
14792             keyCode = key;
14793         }
14794         var handler = function(dlg, e){
14795             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14796                 var k = e.getKey();
14797                 if(keyCode instanceof Array){
14798                     for(var i = 0, len = keyCode.length; i < len; i++){
14799                         if(keyCode[i] == k){
14800                           fn.call(scope || window, dlg, k, e);
14801                           return;
14802                         }
14803                     }
14804                 }else{
14805                     if(k == keyCode){
14806                         fn.call(scope || window, dlg, k, e);
14807                     }
14808                 }
14809             }
14810         };
14811         this.on("keydown", handler);
14812         return this;
14813     },
14814
14815     /**
14816      * Returns the TabPanel component (creates it if it doesn't exist).
14817      * Note: If you wish to simply check for the existence of tabs without creating them,
14818      * check for a null 'tabs' property.
14819      * @return {Roo.TabPanel} The tabs component
14820      */
14821     getTabs : function(){
14822         if(!this.tabs){
14823             this.el.addClass("x-dlg-auto-tabs");
14824             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14825             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14826         }
14827         return this.tabs;
14828     },
14829
14830     /**
14831      * Adds a button to the footer section of the dialog.
14832      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14833      * object or a valid Roo.DomHelper element config
14834      * @param {Function} handler The function called when the button is clicked
14835      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14836      * @return {Roo.Button} The new button
14837      */
14838     addButton : function(config, handler, scope){
14839         var dh = Roo.DomHelper;
14840         if(!this.footer){
14841             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14842         }
14843         if(!this.btnContainer){
14844             var tb = this.footer.createChild({
14845
14846                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14847                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14848             }, null, true);
14849             this.btnContainer = tb.firstChild.firstChild.firstChild;
14850         }
14851         var bconfig = {
14852             handler: handler,
14853             scope: scope,
14854             minWidth: this.minButtonWidth,
14855             hideParent:true
14856         };
14857         if(typeof config == "string"){
14858             bconfig.text = config;
14859         }else{
14860             if(config.tag){
14861                 bconfig.dhconfig = config;
14862             }else{
14863                 Roo.apply(bconfig, config);
14864             }
14865         }
14866         var fc = false;
14867         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14868             bconfig.position = Math.max(0, bconfig.position);
14869             fc = this.btnContainer.childNodes[bconfig.position];
14870         }
14871          
14872         var btn = new Roo.Button(
14873             fc ? 
14874                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14875                 : this.btnContainer.appendChild(document.createElement("td")),
14876             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14877             bconfig
14878         );
14879         this.syncBodyHeight();
14880         if(!this.buttons){
14881             /**
14882              * Array of all the buttons that have been added to this dialog via addButton
14883              * @type Array
14884              */
14885             this.buttons = [];
14886         }
14887         this.buttons.push(btn);
14888         return btn;
14889     },
14890
14891     /**
14892      * Sets the default button to be focused when the dialog is displayed.
14893      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14894      * @return {Roo.BasicDialog} this
14895      */
14896     setDefaultButton : function(btn){
14897         this.defaultButton = btn;
14898         return this;
14899     },
14900
14901     // private
14902     getHeaderFooterHeight : function(safe){
14903         var height = 0;
14904         if(this.header){
14905            height += this.header.getHeight();
14906         }
14907         if(this.footer){
14908            var fm = this.footer.getMargins();
14909             height += (this.footer.getHeight()+fm.top+fm.bottom);
14910         }
14911         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14912         height += this.centerBg.getPadding("tb");
14913         return height;
14914     },
14915
14916     // private
14917     syncBodyHeight : function()
14918     {
14919         var bd = this.body, // the text
14920             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14921             bw = this.bwrap;
14922         var height = this.size.height - this.getHeaderFooterHeight(false);
14923         bd.setHeight(height-bd.getMargins("tb"));
14924         var hh = this.header.getHeight();
14925         var h = this.size.height-hh;
14926         cb.setHeight(h);
14927         
14928         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14929         bw.setHeight(h-cb.getPadding("tb"));
14930         
14931         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14932         bd.setWidth(bw.getWidth(true));
14933         if(this.tabs){
14934             this.tabs.syncHeight();
14935             if(Roo.isIE){
14936                 this.tabs.el.repaint();
14937             }
14938         }
14939     },
14940
14941     /**
14942      * Restores the previous state of the dialog if Roo.state is configured.
14943      * @return {Roo.BasicDialog} this
14944      */
14945     restoreState : function(){
14946         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14947         if(box && box.width){
14948             this.xy = [box.x, box.y];
14949             this.resizeTo(box.width, box.height);
14950         }
14951         return this;
14952     },
14953
14954     // private
14955     beforeShow : function(){
14956         this.expand();
14957         if(this.fixedcenter){
14958             this.xy = this.el.getCenterXY(true);
14959         }
14960         if(this.modal){
14961             Roo.get(document.body).addClass("x-body-masked");
14962             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14963             this.mask.show();
14964         }
14965         this.constrainXY();
14966     },
14967
14968     // private
14969     animShow : function(){
14970         var b = Roo.get(this.animateTarget).getBox();
14971         this.proxy.setSize(b.width, b.height);
14972         this.proxy.setLocation(b.x, b.y);
14973         this.proxy.show();
14974         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14975                     true, .35, this.showEl.createDelegate(this));
14976     },
14977
14978     /**
14979      * Shows the dialog.
14980      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14981      * @return {Roo.BasicDialog} this
14982      */
14983     show : function(animateTarget){
14984         if (this.fireEvent("beforeshow", this) === false){
14985             return;
14986         }
14987         if(this.syncHeightBeforeShow){
14988             this.syncBodyHeight();
14989         }else if(this.firstShow){
14990             this.firstShow = false;
14991             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14992         }
14993         this.animateTarget = animateTarget || this.animateTarget;
14994         if(!this.el.isVisible()){
14995             this.beforeShow();
14996             if(this.animateTarget && Roo.get(this.animateTarget)){
14997                 this.animShow();
14998             }else{
14999                 this.showEl();
15000             }
15001         }
15002         return this;
15003     },
15004
15005     // private
15006     showEl : function(){
15007         this.proxy.hide();
15008         this.el.setXY(this.xy);
15009         this.el.show();
15010         this.adjustAssets(true);
15011         this.toFront();
15012         this.focus();
15013         // IE peekaboo bug - fix found by Dave Fenwick
15014         if(Roo.isIE){
15015             this.el.repaint();
15016         }
15017         this.fireEvent("show", this);
15018     },
15019
15020     /**
15021      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
15022      * dialog itself will receive focus.
15023      */
15024     focus : function(){
15025         if(this.defaultButton){
15026             this.defaultButton.focus();
15027         }else{
15028             this.focusEl.focus();
15029         }
15030     },
15031
15032     // private
15033     constrainXY : function(){
15034         if(this.constraintoviewport !== false){
15035             if(!this.viewSize){
15036                 if(this.container){
15037                     var s = this.container.getSize();
15038                     this.viewSize = [s.width, s.height];
15039                 }else{
15040                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15041                 }
15042             }
15043             var s = Roo.get(this.container||document).getScroll();
15044
15045             var x = this.xy[0], y = this.xy[1];
15046             var w = this.size.width, h = this.size.height;
15047             var vw = this.viewSize[0], vh = this.viewSize[1];
15048             // only move it if it needs it
15049             var moved = false;
15050             // first validate right/bottom
15051             if(x + w > vw+s.left){
15052                 x = vw - w;
15053                 moved = true;
15054             }
15055             if(y + h > vh+s.top){
15056                 y = vh - h;
15057                 moved = true;
15058             }
15059             // then make sure top/left isn't negative
15060             if(x < s.left){
15061                 x = s.left;
15062                 moved = true;
15063             }
15064             if(y < s.top){
15065                 y = s.top;
15066                 moved = true;
15067             }
15068             if(moved){
15069                 // cache xy
15070                 this.xy = [x, y];
15071                 if(this.isVisible()){
15072                     this.el.setLocation(x, y);
15073                     this.adjustAssets();
15074                 }
15075             }
15076         }
15077     },
15078
15079     // private
15080     onDrag : function(){
15081         if(!this.proxyDrag){
15082             this.xy = this.el.getXY();
15083             this.adjustAssets();
15084         }
15085     },
15086
15087     // private
15088     adjustAssets : function(doShow){
15089         var x = this.xy[0], y = this.xy[1];
15090         var w = this.size.width, h = this.size.height;
15091         if(doShow === true){
15092             if(this.shadow){
15093                 this.shadow.show(this.el);
15094             }
15095             if(this.shim){
15096                 this.shim.show();
15097             }
15098         }
15099         if(this.shadow && this.shadow.isVisible()){
15100             this.shadow.show(this.el);
15101         }
15102         if(this.shim && this.shim.isVisible()){
15103             this.shim.setBounds(x, y, w, h);
15104         }
15105     },
15106
15107     // private
15108     adjustViewport : function(w, h){
15109         if(!w || !h){
15110             w = Roo.lib.Dom.getViewWidth();
15111             h = Roo.lib.Dom.getViewHeight();
15112         }
15113         // cache the size
15114         this.viewSize = [w, h];
15115         if(this.modal && this.mask.isVisible()){
15116             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15117             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15118         }
15119         if(this.isVisible()){
15120             this.constrainXY();
15121         }
15122     },
15123
15124     /**
15125      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15126      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15127      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15128      */
15129     destroy : function(removeEl){
15130         if(this.isVisible()){
15131             this.animateTarget = null;
15132             this.hide();
15133         }
15134         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15135         if(this.tabs){
15136             this.tabs.destroy(removeEl);
15137         }
15138         Roo.destroy(
15139              this.shim,
15140              this.proxy,
15141              this.resizer,
15142              this.close,
15143              this.mask
15144         );
15145         if(this.dd){
15146             this.dd.unreg();
15147         }
15148         if(this.buttons){
15149            for(var i = 0, len = this.buttons.length; i < len; i++){
15150                this.buttons[i].destroy();
15151            }
15152         }
15153         this.el.removeAllListeners();
15154         if(removeEl === true){
15155             this.el.update("");
15156             this.el.remove();
15157         }
15158         Roo.DialogManager.unregister(this);
15159     },
15160
15161     // private
15162     startMove : function(){
15163         if(this.proxyDrag){
15164             this.proxy.show();
15165         }
15166         if(this.constraintoviewport !== false){
15167             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15168         }
15169     },
15170
15171     // private
15172     endMove : function(){
15173         if(!this.proxyDrag){
15174             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15175         }else{
15176             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15177             this.proxy.hide();
15178         }
15179         this.refreshSize();
15180         this.adjustAssets();
15181         this.focus();
15182         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15183     },
15184
15185     /**
15186      * Brings this dialog to the front of any other visible dialogs
15187      * @return {Roo.BasicDialog} this
15188      */
15189     toFront : function(){
15190         Roo.DialogManager.bringToFront(this);
15191         return this;
15192     },
15193
15194     /**
15195      * Sends this dialog to the back (under) of any other visible dialogs
15196      * @return {Roo.BasicDialog} this
15197      */
15198     toBack : function(){
15199         Roo.DialogManager.sendToBack(this);
15200         return this;
15201     },
15202
15203     /**
15204      * Centers this dialog in the viewport
15205      * @return {Roo.BasicDialog} this
15206      */
15207     center : function(){
15208         var xy = this.el.getCenterXY(true);
15209         this.moveTo(xy[0], xy[1]);
15210         return this;
15211     },
15212
15213     /**
15214      * Moves the dialog's top-left corner to the specified point
15215      * @param {Number} x
15216      * @param {Number} y
15217      * @return {Roo.BasicDialog} this
15218      */
15219     moveTo : function(x, y){
15220         this.xy = [x,y];
15221         if(this.isVisible()){
15222             this.el.setXY(this.xy);
15223             this.adjustAssets();
15224         }
15225         return this;
15226     },
15227
15228     /**
15229      * Aligns the dialog to the specified element
15230      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15231      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15232      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15233      * @return {Roo.BasicDialog} this
15234      */
15235     alignTo : function(element, position, offsets){
15236         this.xy = this.el.getAlignToXY(element, position, offsets);
15237         if(this.isVisible()){
15238             this.el.setXY(this.xy);
15239             this.adjustAssets();
15240         }
15241         return this;
15242     },
15243
15244     /**
15245      * Anchors an element to another element and realigns it when the window is resized.
15246      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15247      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15248      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15249      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15250      * is a number, it is used as the buffer delay (defaults to 50ms).
15251      * @return {Roo.BasicDialog} this
15252      */
15253     anchorTo : function(el, alignment, offsets, monitorScroll){
15254         var action = function(){
15255             this.alignTo(el, alignment, offsets);
15256         };
15257         Roo.EventManager.onWindowResize(action, this);
15258         var tm = typeof monitorScroll;
15259         if(tm != 'undefined'){
15260             Roo.EventManager.on(window, 'scroll', action, this,
15261                 {buffer: tm == 'number' ? monitorScroll : 50});
15262         }
15263         action.call(this);
15264         return this;
15265     },
15266
15267     /**
15268      * Returns true if the dialog is visible
15269      * @return {Boolean}
15270      */
15271     isVisible : function(){
15272         return this.el.isVisible();
15273     },
15274
15275     // private
15276     animHide : function(callback){
15277         var b = Roo.get(this.animateTarget).getBox();
15278         this.proxy.show();
15279         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15280         this.el.hide();
15281         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15282                     this.hideEl.createDelegate(this, [callback]));
15283     },
15284
15285     /**
15286      * Hides the dialog.
15287      * @param {Function} callback (optional) Function to call when the dialog is hidden
15288      * @return {Roo.BasicDialog} this
15289      */
15290     hide : function(callback){
15291         if (this.fireEvent("beforehide", this) === false){
15292             return;
15293         }
15294         if(this.shadow){
15295             this.shadow.hide();
15296         }
15297         if(this.shim) {
15298           this.shim.hide();
15299         }
15300         // sometimes animateTarget seems to get set.. causing problems...
15301         // this just double checks..
15302         if(this.animateTarget && Roo.get(this.animateTarget)) {
15303            this.animHide(callback);
15304         }else{
15305             this.el.hide();
15306             this.hideEl(callback);
15307         }
15308         return this;
15309     },
15310
15311     // private
15312     hideEl : function(callback){
15313         this.proxy.hide();
15314         if(this.modal){
15315             this.mask.hide();
15316             Roo.get(document.body).removeClass("x-body-masked");
15317         }
15318         this.fireEvent("hide", this);
15319         if(typeof callback == "function"){
15320             callback();
15321         }
15322     },
15323
15324     // private
15325     hideAction : function(){
15326         this.setLeft("-10000px");
15327         this.setTop("-10000px");
15328         this.setStyle("visibility", "hidden");
15329     },
15330
15331     // private
15332     refreshSize : function(){
15333         this.size = this.el.getSize();
15334         this.xy = this.el.getXY();
15335         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15336     },
15337
15338     // private
15339     // z-index is managed by the DialogManager and may be overwritten at any time
15340     setZIndex : function(index){
15341         if(this.modal){
15342             this.mask.setStyle("z-index", index);
15343         }
15344         if(this.shim){
15345             this.shim.setStyle("z-index", ++index);
15346         }
15347         if(this.shadow){
15348             this.shadow.setZIndex(++index);
15349         }
15350         this.el.setStyle("z-index", ++index);
15351         if(this.proxy){
15352             this.proxy.setStyle("z-index", ++index);
15353         }
15354         if(this.resizer){
15355             this.resizer.proxy.setStyle("z-index", ++index);
15356         }
15357
15358         this.lastZIndex = index;
15359     },
15360
15361     /**
15362      * Returns the element for this dialog
15363      * @return {Roo.Element} The underlying dialog Element
15364      */
15365     getEl : function(){
15366         return this.el;
15367     }
15368 });
15369
15370 /**
15371  * @class Roo.DialogManager
15372  * Provides global access to BasicDialogs that have been created and
15373  * support for z-indexing (layering) multiple open dialogs.
15374  */
15375 Roo.DialogManager = function(){
15376     var list = {};
15377     var accessList = [];
15378     var front = null;
15379
15380     // private
15381     var sortDialogs = function(d1, d2){
15382         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15383     };
15384
15385     // private
15386     var orderDialogs = function(){
15387         accessList.sort(sortDialogs);
15388         var seed = Roo.DialogManager.zseed;
15389         for(var i = 0, len = accessList.length; i < len; i++){
15390             var dlg = accessList[i];
15391             if(dlg){
15392                 dlg.setZIndex(seed + (i*10));
15393             }
15394         }
15395     };
15396
15397     return {
15398         /**
15399          * The starting z-index for BasicDialogs (defaults to 9000)
15400          * @type Number The z-index value
15401          */
15402         zseed : 9000,
15403
15404         // private
15405         register : function(dlg){
15406             list[dlg.id] = dlg;
15407             accessList.push(dlg);
15408         },
15409
15410         // private
15411         unregister : function(dlg){
15412             delete list[dlg.id];
15413             var i=0;
15414             var len=0;
15415             if(!accessList.indexOf){
15416                 for(  i = 0, len = accessList.length; i < len; i++){
15417                     if(accessList[i] == dlg){
15418                         accessList.splice(i, 1);
15419                         return;
15420                     }
15421                 }
15422             }else{
15423                  i = accessList.indexOf(dlg);
15424                 if(i != -1){
15425                     accessList.splice(i, 1);
15426                 }
15427             }
15428         },
15429
15430         /**
15431          * Gets a registered dialog by id
15432          * @param {String/Object} id The id of the dialog or a dialog
15433          * @return {Roo.BasicDialog} this
15434          */
15435         get : function(id){
15436             return typeof id == "object" ? id : list[id];
15437         },
15438
15439         /**
15440          * Brings the specified dialog to the front
15441          * @param {String/Object} dlg The id of the dialog or a dialog
15442          * @return {Roo.BasicDialog} this
15443          */
15444         bringToFront : function(dlg){
15445             dlg = this.get(dlg);
15446             if(dlg != front){
15447                 front = dlg;
15448                 dlg._lastAccess = new Date().getTime();
15449                 orderDialogs();
15450             }
15451             return dlg;
15452         },
15453
15454         /**
15455          * Sends the specified dialog to the back
15456          * @param {String/Object} dlg The id of the dialog or a dialog
15457          * @return {Roo.BasicDialog} this
15458          */
15459         sendToBack : function(dlg){
15460             dlg = this.get(dlg);
15461             dlg._lastAccess = -(new Date().getTime());
15462             orderDialogs();
15463             return dlg;
15464         },
15465
15466         /**
15467          * Hides all dialogs
15468          */
15469         hideAll : function(){
15470             for(var id in list){
15471                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15472                     list[id].hide();
15473                 }
15474             }
15475         }
15476     };
15477 }();
15478
15479 /**
15480  * @class Roo.LayoutDialog
15481  * @extends Roo.BasicDialog
15482  * Dialog which provides adjustments for working with a layout in a Dialog.
15483  * Add your necessary layout config options to the dialog's config.<br>
15484  * Example usage (including a nested layout):
15485  * <pre><code>
15486 if(!dialog){
15487     dialog = new Roo.LayoutDialog("download-dlg", {
15488         modal: true,
15489         width:600,
15490         height:450,
15491         shadow:true,
15492         minWidth:500,
15493         minHeight:350,
15494         autoTabs:true,
15495         proxyDrag:true,
15496         // layout config merges with the dialog config
15497         center:{
15498             tabPosition: "top",
15499             alwaysShowTabs: true
15500         }
15501     });
15502     dialog.addKeyListener(27, dialog.hide, dialog);
15503     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15504     dialog.addButton("Build It!", this.getDownload, this);
15505
15506     // we can even add nested layouts
15507     var innerLayout = new Roo.BorderLayout("dl-inner", {
15508         east: {
15509             initialSize: 200,
15510             autoScroll:true,
15511             split:true
15512         },
15513         center: {
15514             autoScroll:true
15515         }
15516     });
15517     innerLayout.beginUpdate();
15518     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15519     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15520     innerLayout.endUpdate(true);
15521
15522     var layout = dialog.getLayout();
15523     layout.beginUpdate();
15524     layout.add("center", new Roo.ContentPanel("standard-panel",
15525                         {title: "Download the Source", fitToFrame:true}));
15526     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15527                {title: "Build your own roo.js"}));
15528     layout.getRegion("center").showPanel(sp);
15529     layout.endUpdate();
15530 }
15531 </code></pre>
15532     * @constructor
15533     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15534     * @param {Object} config configuration options
15535   */
15536 Roo.LayoutDialog = function(el, cfg){
15537     
15538     var config=  cfg;
15539     if (typeof(cfg) == 'undefined') {
15540         config = Roo.apply({}, el);
15541         // not sure why we use documentElement here.. - it should always be body.
15542         // IE7 borks horribly if we use documentElement.
15543         // webkit also does not like documentElement - it creates a body element...
15544         el = Roo.get( document.body || document.documentElement ).createChild();
15545         //config.autoCreate = true;
15546     }
15547     
15548     
15549     config.autoTabs = false;
15550     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15551     this.body.setStyle({overflow:"hidden", position:"relative"});
15552     this.layout = new Roo.BorderLayout(this.body.dom, config);
15553     this.layout.monitorWindowResize = false;
15554     this.el.addClass("x-dlg-auto-layout");
15555     // fix case when center region overwrites center function
15556     this.center = Roo.BasicDialog.prototype.center;
15557     this.on("show", this.layout.layout, this.layout, true);
15558     if (config.items) {
15559         var xitems = config.items;
15560         delete config.items;
15561         Roo.each(xitems, this.addxtype, this);
15562     }
15563     
15564     
15565 };
15566 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15567     /**
15568      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15569      * @deprecated
15570      */
15571     endUpdate : function(){
15572         this.layout.endUpdate();
15573     },
15574
15575     /**
15576      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15577      *  @deprecated
15578      */
15579     beginUpdate : function(){
15580         this.layout.beginUpdate();
15581     },
15582
15583     /**
15584      * Get the BorderLayout for this dialog
15585      * @return {Roo.BorderLayout}
15586      */
15587     getLayout : function(){
15588         return this.layout;
15589     },
15590
15591     showEl : function(){
15592         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15593         if(Roo.isIE7){
15594             this.layout.layout();
15595         }
15596     },
15597
15598     // private
15599     // Use the syncHeightBeforeShow config option to control this automatically
15600     syncBodyHeight : function(){
15601         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15602         if(this.layout){this.layout.layout();}
15603     },
15604     
15605       /**
15606      * Add an xtype element (actually adds to the layout.)
15607      * @return {Object} xdata xtype object data.
15608      */
15609     
15610     addxtype : function(c) {
15611         return this.layout.addxtype(c);
15612     }
15613 });/*
15614  * Based on:
15615  * Ext JS Library 1.1.1
15616  * Copyright(c) 2006-2007, Ext JS, LLC.
15617  *
15618  * Originally Released Under LGPL - original licence link has changed is not relivant.
15619  *
15620  * Fork - LGPL
15621  * <script type="text/javascript">
15622  */
15623  
15624 /**
15625  * @class Roo.MessageBox
15626  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15627  * Example usage:
15628  *<pre><code>
15629 // Basic alert:
15630 Roo.Msg.alert('Status', 'Changes saved successfully.');
15631
15632 // Prompt for user data:
15633 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15634     if (btn == 'ok'){
15635         // process text value...
15636     }
15637 });
15638
15639 // Show a dialog using config options:
15640 Roo.Msg.show({
15641    title:'Save Changes?',
15642    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15643    buttons: Roo.Msg.YESNOCANCEL,
15644    fn: processResult,
15645    animEl: 'elId'
15646 });
15647 </code></pre>
15648  * @singleton
15649  */
15650 Roo.MessageBox = function(){
15651     var dlg, opt, mask, waitTimer;
15652     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15653     var buttons, activeTextEl, bwidth;
15654
15655     // private
15656     var handleButton = function(button){
15657         dlg.hide();
15658         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15659     };
15660
15661     // private
15662     var handleHide = function(){
15663         if(opt && opt.cls){
15664             dlg.el.removeClass(opt.cls);
15665         }
15666         if(waitTimer){
15667             Roo.TaskMgr.stop(waitTimer);
15668             waitTimer = null;
15669         }
15670     };
15671
15672     // private
15673     var updateButtons = function(b){
15674         var width = 0;
15675         if(!b){
15676             buttons["ok"].hide();
15677             buttons["cancel"].hide();
15678             buttons["yes"].hide();
15679             buttons["no"].hide();
15680             dlg.footer.dom.style.display = 'none';
15681             return width;
15682         }
15683         dlg.footer.dom.style.display = '';
15684         for(var k in buttons){
15685             if(typeof buttons[k] != "function"){
15686                 if(b[k]){
15687                     buttons[k].show();
15688                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15689                     width += buttons[k].el.getWidth()+15;
15690                 }else{
15691                     buttons[k].hide();
15692                 }
15693             }
15694         }
15695         return width;
15696     };
15697
15698     // private
15699     var handleEsc = function(d, k, e){
15700         if(opt && opt.closable !== false){
15701             dlg.hide();
15702         }
15703         if(e){
15704             e.stopEvent();
15705         }
15706     };
15707
15708     return {
15709         /**
15710          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15711          * @return {Roo.BasicDialog} The BasicDialog element
15712          */
15713         getDialog : function(){
15714            if(!dlg){
15715                 dlg = new Roo.BasicDialog("x-msg-box", {
15716                     autoCreate : true,
15717                     shadow: true,
15718                     draggable: true,
15719                     resizable:false,
15720                     constraintoviewport:false,
15721                     fixedcenter:true,
15722                     collapsible : false,
15723                     shim:true,
15724                     modal: true,
15725                     width:400, height:100,
15726                     buttonAlign:"center",
15727                     closeClick : function(){
15728                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15729                             handleButton("no");
15730                         }else{
15731                             handleButton("cancel");
15732                         }
15733                     }
15734                 });
15735                 dlg.on("hide", handleHide);
15736                 mask = dlg.mask;
15737                 dlg.addKeyListener(27, handleEsc);
15738                 buttons = {};
15739                 var bt = this.buttonText;
15740                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15741                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15742                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15743                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15744                 bodyEl = dlg.body.createChild({
15745
15746                     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>'
15747                 });
15748                 msgEl = bodyEl.dom.firstChild;
15749                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15750                 textboxEl.enableDisplayMode();
15751                 textboxEl.addKeyListener([10,13], function(){
15752                     if(dlg.isVisible() && opt && opt.buttons){
15753                         if(opt.buttons.ok){
15754                             handleButton("ok");
15755                         }else if(opt.buttons.yes){
15756                             handleButton("yes");
15757                         }
15758                     }
15759                 });
15760                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15761                 textareaEl.enableDisplayMode();
15762                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15763                 progressEl.enableDisplayMode();
15764                 var pf = progressEl.dom.firstChild;
15765                 if (pf) {
15766                     pp = Roo.get(pf.firstChild);
15767                     pp.setHeight(pf.offsetHeight);
15768                 }
15769                 
15770             }
15771             return dlg;
15772         },
15773
15774         /**
15775          * Updates the message box body text
15776          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15777          * the XHTML-compliant non-breaking space character '&amp;#160;')
15778          * @return {Roo.MessageBox} This message box
15779          */
15780         updateText : function(text){
15781             if(!dlg.isVisible() && !opt.width){
15782                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15783             }
15784             msgEl.innerHTML = text || '&#160;';
15785       
15786             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15787             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15788             var w = Math.max(
15789                     Math.min(opt.width || cw , this.maxWidth), 
15790                     Math.max(opt.minWidth || this.minWidth, bwidth)
15791             );
15792             if(opt.prompt){
15793                 activeTextEl.setWidth(w);
15794             }
15795             if(dlg.isVisible()){
15796                 dlg.fixedcenter = false;
15797             }
15798             // to big, make it scroll. = But as usual stupid IE does not support
15799             // !important..
15800             
15801             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15802                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15803                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15804             } else {
15805                 bodyEl.dom.style.height = '';
15806                 bodyEl.dom.style.overflowY = '';
15807             }
15808             if (cw > w) {
15809                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15810             } else {
15811                 bodyEl.dom.style.overflowX = '';
15812             }
15813             
15814             dlg.setContentSize(w, bodyEl.getHeight());
15815             if(dlg.isVisible()){
15816                 dlg.fixedcenter = true;
15817             }
15818             return this;
15819         },
15820
15821         /**
15822          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15823          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15824          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15825          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15826          * @return {Roo.MessageBox} This message box
15827          */
15828         updateProgress : function(value, text){
15829             if(text){
15830                 this.updateText(text);
15831             }
15832             if (pp) { // weird bug on my firefox - for some reason this is not defined
15833                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15834             }
15835             return this;
15836         },        
15837
15838         /**
15839          * Returns true if the message box is currently displayed
15840          * @return {Boolean} True if the message box is visible, else false
15841          */
15842         isVisible : function(){
15843             return dlg && dlg.isVisible();  
15844         },
15845
15846         /**
15847          * Hides the message box if it is displayed
15848          */
15849         hide : function(){
15850             if(this.isVisible()){
15851                 dlg.hide();
15852             }  
15853         },
15854
15855         /**
15856          * Displays a new message box, or reinitializes an existing message box, based on the config options
15857          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15858          * The following config object properties are supported:
15859          * <pre>
15860 Property    Type             Description
15861 ----------  ---------------  ------------------------------------------------------------------------------------
15862 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15863                                    closes (defaults to undefined)
15864 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15865                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15866 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15867                                    progress and wait dialogs will ignore this property and always hide the
15868                                    close button as they can only be closed programmatically.
15869 cls               String           A custom CSS class to apply to the message box element
15870 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15871                                    displayed (defaults to 75)
15872 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15873                                    function will be btn (the name of the button that was clicked, if applicable,
15874                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15875                                    Progress and wait dialogs will ignore this option since they do not respond to
15876                                    user actions and can only be closed programmatically, so any required function
15877                                    should be called by the same code after it closes the dialog.
15878 icon              String           A CSS class that provides a background image to be used as an icon for
15879                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15880 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15881 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15882 modal             Boolean          False to allow user interaction with the page while the message box is
15883                                    displayed (defaults to true)
15884 msg               String           A string that will replace the existing message box body text (defaults
15885                                    to the XHTML-compliant non-breaking space character '&#160;')
15886 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15887 progress          Boolean          True to display a progress bar (defaults to false)
15888 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15889 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15890 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15891 title             String           The title text
15892 value             String           The string value to set into the active textbox element if displayed
15893 wait              Boolean          True to display a progress bar (defaults to false)
15894 width             Number           The width of the dialog in pixels
15895 </pre>
15896          *
15897          * Example usage:
15898          * <pre><code>
15899 Roo.Msg.show({
15900    title: 'Address',
15901    msg: 'Please enter your address:',
15902    width: 300,
15903    buttons: Roo.MessageBox.OKCANCEL,
15904    multiline: true,
15905    fn: saveAddress,
15906    animEl: 'addAddressBtn'
15907 });
15908 </code></pre>
15909          * @param {Object} config Configuration options
15910          * @return {Roo.MessageBox} This message box
15911          */
15912         show : function(options)
15913         {
15914             
15915             // this causes nightmares if you show one dialog after another
15916             // especially on callbacks..
15917              
15918             if(this.isVisible()){
15919                 
15920                 this.hide();
15921                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15922                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15923                 Roo.log("New Dialog Message:" +  options.msg )
15924                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15925                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15926                 
15927             }
15928             var d = this.getDialog();
15929             opt = options;
15930             d.setTitle(opt.title || "&#160;");
15931             d.close.setDisplayed(opt.closable !== false);
15932             activeTextEl = textboxEl;
15933             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15934             if(opt.prompt){
15935                 if(opt.multiline){
15936                     textboxEl.hide();
15937                     textareaEl.show();
15938                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15939                         opt.multiline : this.defaultTextHeight);
15940                     activeTextEl = textareaEl;
15941                 }else{
15942                     textboxEl.show();
15943                     textareaEl.hide();
15944                 }
15945             }else{
15946                 textboxEl.hide();
15947                 textareaEl.hide();
15948             }
15949             progressEl.setDisplayed(opt.progress === true);
15950             this.updateProgress(0);
15951             activeTextEl.dom.value = opt.value || "";
15952             if(opt.prompt){
15953                 dlg.setDefaultButton(activeTextEl);
15954             }else{
15955                 var bs = opt.buttons;
15956                 var db = null;
15957                 if(bs && bs.ok){
15958                     db = buttons["ok"];
15959                 }else if(bs && bs.yes){
15960                     db = buttons["yes"];
15961                 }
15962                 dlg.setDefaultButton(db);
15963             }
15964             bwidth = updateButtons(opt.buttons);
15965             this.updateText(opt.msg);
15966             if(opt.cls){
15967                 d.el.addClass(opt.cls);
15968             }
15969             d.proxyDrag = opt.proxyDrag === true;
15970             d.modal = opt.modal !== false;
15971             d.mask = opt.modal !== false ? mask : false;
15972             if(!d.isVisible()){
15973                 // force it to the end of the z-index stack so it gets a cursor in FF
15974                 document.body.appendChild(dlg.el.dom);
15975                 d.animateTarget = null;
15976                 d.show(options.animEl);
15977             }
15978             return this;
15979         },
15980
15981         /**
15982          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15983          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15984          * and closing the message box when the process is complete.
15985          * @param {String} title The title bar text
15986          * @param {String} msg The message box body text
15987          * @return {Roo.MessageBox} This message box
15988          */
15989         progress : function(title, msg){
15990             this.show({
15991                 title : title,
15992                 msg : msg,
15993                 buttons: false,
15994                 progress:true,
15995                 closable:false,
15996                 minWidth: this.minProgressWidth,
15997                 modal : true
15998             });
15999             return this;
16000         },
16001
16002         /**
16003          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16004          * If a callback function is passed it will be called after the user clicks the button, and the
16005          * id of the button that was clicked will be passed as the only parameter to the callback
16006          * (could also be the top-right close button).
16007          * @param {String} title The title bar text
16008          * @param {String} msg The message box body text
16009          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16010          * @param {Object} scope (optional) The scope of the callback function
16011          * @return {Roo.MessageBox} This message box
16012          */
16013         alert : function(title, msg, fn, scope){
16014             this.show({
16015                 title : title,
16016                 msg : msg,
16017                 buttons: this.OK,
16018                 fn: fn,
16019                 scope : scope,
16020                 modal : true
16021             });
16022             return this;
16023         },
16024
16025         /**
16026          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
16027          * interaction while waiting for a long-running process to complete that does not have defined intervals.
16028          * You are responsible for closing the message box when the process is complete.
16029          * @param {String} msg The message box body text
16030          * @param {String} title (optional) The title bar text
16031          * @return {Roo.MessageBox} This message box
16032          */
16033         wait : function(msg, title){
16034             this.show({
16035                 title : title,
16036                 msg : msg,
16037                 buttons: false,
16038                 closable:false,
16039                 progress:true,
16040                 modal:true,
16041                 width:300,
16042                 wait:true
16043             });
16044             waitTimer = Roo.TaskMgr.start({
16045                 run: function(i){
16046                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16047                 },
16048                 interval: 1000
16049             });
16050             return this;
16051         },
16052
16053         /**
16054          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16055          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16056          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16057          * @param {String} title The title bar text
16058          * @param {String} msg The message box body text
16059          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16060          * @param {Object} scope (optional) The scope of the callback function
16061          * @return {Roo.MessageBox} This message box
16062          */
16063         confirm : function(title, msg, fn, scope){
16064             this.show({
16065                 title : title,
16066                 msg : msg,
16067                 buttons: this.YESNO,
16068                 fn: fn,
16069                 scope : scope,
16070                 modal : true
16071             });
16072             return this;
16073         },
16074
16075         /**
16076          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16077          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16078          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16079          * (could also be the top-right close button) and the text that was entered will be passed as the two
16080          * parameters to the callback.
16081          * @param {String} title The title bar text
16082          * @param {String} msg The message box body text
16083          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16084          * @param {Object} scope (optional) The scope of the callback function
16085          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16086          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16087          * @return {Roo.MessageBox} This message box
16088          */
16089         prompt : function(title, msg, fn, scope, multiline){
16090             this.show({
16091                 title : title,
16092                 msg : msg,
16093                 buttons: this.OKCANCEL,
16094                 fn: fn,
16095                 minWidth:250,
16096                 scope : scope,
16097                 prompt:true,
16098                 multiline: multiline,
16099                 modal : true
16100             });
16101             return this;
16102         },
16103
16104         /**
16105          * Button config that displays a single OK button
16106          * @type Object
16107          */
16108         OK : {ok:true},
16109         /**
16110          * Button config that displays Yes and No buttons
16111          * @type Object
16112          */
16113         YESNO : {yes:true, no:true},
16114         /**
16115          * Button config that displays OK and Cancel buttons
16116          * @type Object
16117          */
16118         OKCANCEL : {ok:true, cancel:true},
16119         /**
16120          * Button config that displays Yes, No and Cancel buttons
16121          * @type Object
16122          */
16123         YESNOCANCEL : {yes:true, no:true, cancel:true},
16124
16125         /**
16126          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16127          * @type Number
16128          */
16129         defaultTextHeight : 75,
16130         /**
16131          * The maximum width in pixels of the message box (defaults to 600)
16132          * @type Number
16133          */
16134         maxWidth : 600,
16135         /**
16136          * The minimum width in pixels of the message box (defaults to 100)
16137          * @type Number
16138          */
16139         minWidth : 100,
16140         /**
16141          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16142          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16143          * @type Number
16144          */
16145         minProgressWidth : 250,
16146         /**
16147          * An object containing the default button text strings that can be overriden for localized language support.
16148          * Supported properties are: ok, cancel, yes and no.
16149          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16150          * @type Object
16151          */
16152         buttonText : {
16153             ok : "OK",
16154             cancel : "Cancel",
16155             yes : "Yes",
16156             no : "No"
16157         }
16158     };
16159 }();
16160
16161 /**
16162  * Shorthand for {@link Roo.MessageBox}
16163  */
16164 Roo.Msg = Roo.MessageBox;/*
16165  * Based on:
16166  * Ext JS Library 1.1.1
16167  * Copyright(c) 2006-2007, Ext JS, LLC.
16168  *
16169  * Originally Released Under LGPL - original licence link has changed is not relivant.
16170  *
16171  * Fork - LGPL
16172  * <script type="text/javascript">
16173  */
16174 /**
16175  * @class Roo.QuickTips
16176  * Provides attractive and customizable tooltips for any element.
16177  * @singleton
16178  */
16179 Roo.QuickTips = function(){
16180     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16181     var ce, bd, xy, dd;
16182     var visible = false, disabled = true, inited = false;
16183     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16184     
16185     var onOver = function(e){
16186         if(disabled){
16187             return;
16188         }
16189         var t = e.getTarget();
16190         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16191             return;
16192         }
16193         if(ce && t == ce.el){
16194             clearTimeout(hideProc);
16195             return;
16196         }
16197         if(t && tagEls[t.id]){
16198             tagEls[t.id].el = t;
16199             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16200             return;
16201         }
16202         var ttp, et = Roo.fly(t);
16203         var ns = cfg.namespace;
16204         if(tm.interceptTitles && t.title){
16205             ttp = t.title;
16206             t.qtip = ttp;
16207             t.removeAttribute("title");
16208             e.preventDefault();
16209         }else{
16210             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16211         }
16212         if(ttp){
16213             showProc = show.defer(tm.showDelay, tm, [{
16214                 el: t, 
16215                 text: ttp, 
16216                 width: et.getAttributeNS(ns, cfg.width),
16217                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16218                 title: et.getAttributeNS(ns, cfg.title),
16219                     cls: et.getAttributeNS(ns, cfg.cls)
16220             }]);
16221         }
16222     };
16223     
16224     var onOut = function(e){
16225         clearTimeout(showProc);
16226         var t = e.getTarget();
16227         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16228             hideProc = setTimeout(hide, tm.hideDelay);
16229         }
16230     };
16231     
16232     var onMove = function(e){
16233         if(disabled){
16234             return;
16235         }
16236         xy = e.getXY();
16237         xy[1] += 18;
16238         if(tm.trackMouse && ce){
16239             el.setXY(xy);
16240         }
16241     };
16242     
16243     var onDown = function(e){
16244         clearTimeout(showProc);
16245         clearTimeout(hideProc);
16246         if(!e.within(el)){
16247             if(tm.hideOnClick){
16248                 hide();
16249                 tm.disable();
16250                 tm.enable.defer(100, tm);
16251             }
16252         }
16253     };
16254     
16255     var getPad = function(){
16256         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16257     };
16258
16259     var show = function(o){
16260         if(disabled){
16261             return;
16262         }
16263         clearTimeout(dismissProc);
16264         ce = o;
16265         if(removeCls){ // in case manually hidden
16266             el.removeClass(removeCls);
16267             removeCls = null;
16268         }
16269         if(ce.cls){
16270             el.addClass(ce.cls);
16271             removeCls = ce.cls;
16272         }
16273         if(ce.title){
16274             tipTitle.update(ce.title);
16275             tipTitle.show();
16276         }else{
16277             tipTitle.update('');
16278             tipTitle.hide();
16279         }
16280         el.dom.style.width  = tm.maxWidth+'px';
16281         //tipBody.dom.style.width = '';
16282         tipBodyText.update(o.text);
16283         var p = getPad(), w = ce.width;
16284         if(!w){
16285             var td = tipBodyText.dom;
16286             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16287             if(aw > tm.maxWidth){
16288                 w = tm.maxWidth;
16289             }else if(aw < tm.minWidth){
16290                 w = tm.minWidth;
16291             }else{
16292                 w = aw;
16293             }
16294         }
16295         //tipBody.setWidth(w);
16296         el.setWidth(parseInt(w, 10) + p);
16297         if(ce.autoHide === false){
16298             close.setDisplayed(true);
16299             if(dd){
16300                 dd.unlock();
16301             }
16302         }else{
16303             close.setDisplayed(false);
16304             if(dd){
16305                 dd.lock();
16306             }
16307         }
16308         if(xy){
16309             el.avoidY = xy[1]-18;
16310             el.setXY(xy);
16311         }
16312         if(tm.animate){
16313             el.setOpacity(.1);
16314             el.setStyle("visibility", "visible");
16315             el.fadeIn({callback: afterShow});
16316         }else{
16317             afterShow();
16318         }
16319     };
16320     
16321     var afterShow = function(){
16322         if(ce){
16323             el.show();
16324             esc.enable();
16325             if(tm.autoDismiss && ce.autoHide !== false){
16326                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16327             }
16328         }
16329     };
16330     
16331     var hide = function(noanim){
16332         clearTimeout(dismissProc);
16333         clearTimeout(hideProc);
16334         ce = null;
16335         if(el.isVisible()){
16336             esc.disable();
16337             if(noanim !== true && tm.animate){
16338                 el.fadeOut({callback: afterHide});
16339             }else{
16340                 afterHide();
16341             } 
16342         }
16343     };
16344     
16345     var afterHide = function(){
16346         el.hide();
16347         if(removeCls){
16348             el.removeClass(removeCls);
16349             removeCls = null;
16350         }
16351     };
16352     
16353     return {
16354         /**
16355         * @cfg {Number} minWidth
16356         * The minimum width of the quick tip (defaults to 40)
16357         */
16358        minWidth : 40,
16359         /**
16360         * @cfg {Number} maxWidth
16361         * The maximum width of the quick tip (defaults to 300)
16362         */
16363        maxWidth : 300,
16364         /**
16365         * @cfg {Boolean} interceptTitles
16366         * True to automatically use the element's DOM title value if available (defaults to false)
16367         */
16368        interceptTitles : false,
16369         /**
16370         * @cfg {Boolean} trackMouse
16371         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16372         */
16373        trackMouse : false,
16374         /**
16375         * @cfg {Boolean} hideOnClick
16376         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16377         */
16378        hideOnClick : true,
16379         /**
16380         * @cfg {Number} showDelay
16381         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16382         */
16383        showDelay : 500,
16384         /**
16385         * @cfg {Number} hideDelay
16386         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16387         */
16388        hideDelay : 200,
16389         /**
16390         * @cfg {Boolean} autoHide
16391         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16392         * Used in conjunction with hideDelay.
16393         */
16394        autoHide : true,
16395         /**
16396         * @cfg {Boolean}
16397         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16398         * (defaults to true).  Used in conjunction with autoDismissDelay.
16399         */
16400        autoDismiss : true,
16401         /**
16402         * @cfg {Number}
16403         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16404         */
16405        autoDismissDelay : 5000,
16406        /**
16407         * @cfg {Boolean} animate
16408         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16409         */
16410        animate : false,
16411
16412        /**
16413         * @cfg {String} title
16414         * Title text to display (defaults to '').  This can be any valid HTML markup.
16415         */
16416         title: '',
16417        /**
16418         * @cfg {String} text
16419         * Body text to display (defaults to '').  This can be any valid HTML markup.
16420         */
16421         text : '',
16422        /**
16423         * @cfg {String} cls
16424         * A CSS class to apply to the base quick tip element (defaults to '').
16425         */
16426         cls : '',
16427        /**
16428         * @cfg {Number} width
16429         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16430         * minWidth or maxWidth.
16431         */
16432         width : null,
16433
16434     /**
16435      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16436      * or display QuickTips in a page.
16437      */
16438        init : function(){
16439           tm = Roo.QuickTips;
16440           cfg = tm.tagConfig;
16441           if(!inited){
16442               if(!Roo.isReady){ // allow calling of init() before onReady
16443                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16444                   return;
16445               }
16446               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16447               el.fxDefaults = {stopFx: true};
16448               // maximum custom styling
16449               //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>');
16450               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>');              
16451               tipTitle = el.child('h3');
16452               tipTitle.enableDisplayMode("block");
16453               tipBody = el.child('div.x-tip-bd');
16454               tipBodyText = el.child('div.x-tip-bd-inner');
16455               //bdLeft = el.child('div.x-tip-bd-left');
16456               //bdRight = el.child('div.x-tip-bd-right');
16457               close = el.child('div.x-tip-close');
16458               close.enableDisplayMode("block");
16459               close.on("click", hide);
16460               var d = Roo.get(document);
16461               d.on("mousedown", onDown);
16462               d.on("mouseover", onOver);
16463               d.on("mouseout", onOut);
16464               d.on("mousemove", onMove);
16465               esc = d.addKeyListener(27, hide);
16466               esc.disable();
16467               if(Roo.dd.DD){
16468                   dd = el.initDD("default", null, {
16469                       onDrag : function(){
16470                           el.sync();  
16471                       }
16472                   });
16473                   dd.setHandleElId(tipTitle.id);
16474                   dd.lock();
16475               }
16476               inited = true;
16477           }
16478           this.enable(); 
16479        },
16480
16481     /**
16482      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16483      * are supported:
16484      * <pre>
16485 Property    Type                   Description
16486 ----------  ---------------------  ------------------------------------------------------------------------
16487 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16488      * </ul>
16489      * @param {Object} config The config object
16490      */
16491        register : function(config){
16492            var cs = config instanceof Array ? config : arguments;
16493            for(var i = 0, len = cs.length; i < len; i++) {
16494                var c = cs[i];
16495                var target = c.target;
16496                if(target){
16497                    if(target instanceof Array){
16498                        for(var j = 0, jlen = target.length; j < jlen; j++){
16499                            tagEls[target[j]] = c;
16500                        }
16501                    }else{
16502                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16503                    }
16504                }
16505            }
16506        },
16507
16508     /**
16509      * Removes this quick tip from its element and destroys it.
16510      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16511      */
16512        unregister : function(el){
16513            delete tagEls[Roo.id(el)];
16514        },
16515
16516     /**
16517      * Enable this quick tip.
16518      */
16519        enable : function(){
16520            if(inited && disabled){
16521                locks.pop();
16522                if(locks.length < 1){
16523                    disabled = false;
16524                }
16525            }
16526        },
16527
16528     /**
16529      * Disable this quick tip.
16530      */
16531        disable : function(){
16532           disabled = true;
16533           clearTimeout(showProc);
16534           clearTimeout(hideProc);
16535           clearTimeout(dismissProc);
16536           if(ce){
16537               hide(true);
16538           }
16539           locks.push(1);
16540        },
16541
16542     /**
16543      * Returns true if the quick tip is enabled, else false.
16544      */
16545        isEnabled : function(){
16546             return !disabled;
16547        },
16548
16549         // private
16550        tagConfig : {
16551            namespace : "ext",
16552            attribute : "qtip",
16553            width : "width",
16554            target : "target",
16555            title : "qtitle",
16556            hide : "hide",
16557            cls : "qclass"
16558        }
16559    };
16560 }();
16561
16562 // backwards compat
16563 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16564  * Based on:
16565  * Ext JS Library 1.1.1
16566  * Copyright(c) 2006-2007, Ext JS, LLC.
16567  *
16568  * Originally Released Under LGPL - original licence link has changed is not relivant.
16569  *
16570  * Fork - LGPL
16571  * <script type="text/javascript">
16572  */
16573  
16574
16575 /**
16576  * @class Roo.tree.TreePanel
16577  * @extends Roo.data.Tree
16578
16579  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16580  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16581  * @cfg {Boolean} enableDD true to enable drag and drop
16582  * @cfg {Boolean} enableDrag true to enable just drag
16583  * @cfg {Boolean} enableDrop true to enable just drop
16584  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16585  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16586  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16587  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16588  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16589  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16590  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16591  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16592  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16593  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16594  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16595  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16596  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16597  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16598  * @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>
16599  * @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>
16600  * 
16601  * @constructor
16602  * @param {String/HTMLElement/Element} el The container element
16603  * @param {Object} config
16604  */
16605 Roo.tree.TreePanel = function(el, config){
16606     var root = false;
16607     var loader = false;
16608     if (config.root) {
16609         root = config.root;
16610         delete config.root;
16611     }
16612     if (config.loader) {
16613         loader = config.loader;
16614         delete config.loader;
16615     }
16616     
16617     Roo.apply(this, config);
16618     Roo.tree.TreePanel.superclass.constructor.call(this);
16619     this.el = Roo.get(el);
16620     this.el.addClass('x-tree');
16621     //console.log(root);
16622     if (root) {
16623         this.setRootNode( Roo.factory(root, Roo.tree));
16624     }
16625     if (loader) {
16626         this.loader = Roo.factory(loader, Roo.tree);
16627     }
16628    /**
16629     * Read-only. The id of the container element becomes this TreePanel's id.
16630     */
16631     this.id = this.el.id;
16632     this.addEvents({
16633         /**
16634         * @event beforeload
16635         * Fires before a node is loaded, return false to cancel
16636         * @param {Node} node The node being loaded
16637         */
16638         "beforeload" : true,
16639         /**
16640         * @event load
16641         * Fires when a node is loaded
16642         * @param {Node} node The node that was loaded
16643         */
16644         "load" : true,
16645         /**
16646         * @event textchange
16647         * Fires when the text for a node is changed
16648         * @param {Node} node The node
16649         * @param {String} text The new text
16650         * @param {String} oldText The old text
16651         */
16652         "textchange" : true,
16653         /**
16654         * @event beforeexpand
16655         * Fires before a node is expanded, return false to cancel.
16656         * @param {Node} node The node
16657         * @param {Boolean} deep
16658         * @param {Boolean} anim
16659         */
16660         "beforeexpand" : true,
16661         /**
16662         * @event beforecollapse
16663         * Fires before a node is collapsed, return false to cancel.
16664         * @param {Node} node The node
16665         * @param {Boolean} deep
16666         * @param {Boolean} anim
16667         */
16668         "beforecollapse" : true,
16669         /**
16670         * @event expand
16671         * Fires when a node is expanded
16672         * @param {Node} node The node
16673         */
16674         "expand" : true,
16675         /**
16676         * @event disabledchange
16677         * Fires when the disabled status of a node changes
16678         * @param {Node} node The node
16679         * @param {Boolean} disabled
16680         */
16681         "disabledchange" : true,
16682         /**
16683         * @event collapse
16684         * Fires when a node is collapsed
16685         * @param {Node} node The node
16686         */
16687         "collapse" : true,
16688         /**
16689         * @event beforeclick
16690         * Fires before click processing on a node. Return false to cancel the default action.
16691         * @param {Node} node The node
16692         * @param {Roo.EventObject} e The event object
16693         */
16694         "beforeclick":true,
16695         /**
16696         * @event checkchange
16697         * Fires when a node with a checkbox's checked property changes
16698         * @param {Node} this This node
16699         * @param {Boolean} checked
16700         */
16701         "checkchange":true,
16702         /**
16703         * @event click
16704         * Fires when a node is clicked
16705         * @param {Node} node The node
16706         * @param {Roo.EventObject} e The event object
16707         */
16708         "click":true,
16709         /**
16710         * @event dblclick
16711         * Fires when a node is double clicked
16712         * @param {Node} node The node
16713         * @param {Roo.EventObject} e The event object
16714         */
16715         "dblclick":true,
16716         /**
16717         * @event contextmenu
16718         * Fires when a node is right clicked
16719         * @param {Node} node The node
16720         * @param {Roo.EventObject} e The event object
16721         */
16722         "contextmenu":true,
16723         /**
16724         * @event beforechildrenrendered
16725         * Fires right before the child nodes for a node are rendered
16726         * @param {Node} node The node
16727         */
16728         "beforechildrenrendered":true,
16729         /**
16730         * @event startdrag
16731         * Fires when a node starts being dragged
16732         * @param {Roo.tree.TreePanel} this
16733         * @param {Roo.tree.TreeNode} node
16734         * @param {event} e The raw browser event
16735         */ 
16736        "startdrag" : true,
16737        /**
16738         * @event enddrag
16739         * Fires when a drag operation is complete
16740         * @param {Roo.tree.TreePanel} this
16741         * @param {Roo.tree.TreeNode} node
16742         * @param {event} e The raw browser event
16743         */
16744        "enddrag" : true,
16745        /**
16746         * @event dragdrop
16747         * Fires when a dragged node is dropped on a valid DD target
16748         * @param {Roo.tree.TreePanel} this
16749         * @param {Roo.tree.TreeNode} node
16750         * @param {DD} dd The dd it was dropped on
16751         * @param {event} e The raw browser event
16752         */
16753        "dragdrop" : true,
16754        /**
16755         * @event beforenodedrop
16756         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16757         * passed to handlers has the following properties:<br />
16758         * <ul style="padding:5px;padding-left:16px;">
16759         * <li>tree - The TreePanel</li>
16760         * <li>target - The node being targeted for the drop</li>
16761         * <li>data - The drag data from the drag source</li>
16762         * <li>point - The point of the drop - append, above or below</li>
16763         * <li>source - The drag source</li>
16764         * <li>rawEvent - Raw mouse event</li>
16765         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16766         * to be inserted by setting them on this object.</li>
16767         * <li>cancel - Set this to true to cancel the drop.</li>
16768         * </ul>
16769         * @param {Object} dropEvent
16770         */
16771        "beforenodedrop" : true,
16772        /**
16773         * @event nodedrop
16774         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16775         * passed to handlers has the following properties:<br />
16776         * <ul style="padding:5px;padding-left:16px;">
16777         * <li>tree - The TreePanel</li>
16778         * <li>target - The node being targeted for the drop</li>
16779         * <li>data - The drag data from the drag source</li>
16780         * <li>point - The point of the drop - append, above or below</li>
16781         * <li>source - The drag source</li>
16782         * <li>rawEvent - Raw mouse event</li>
16783         * <li>dropNode - Dropped node(s).</li>
16784         * </ul>
16785         * @param {Object} dropEvent
16786         */
16787        "nodedrop" : true,
16788         /**
16789         * @event nodedragover
16790         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16791         * passed to handlers has the following properties:<br />
16792         * <ul style="padding:5px;padding-left:16px;">
16793         * <li>tree - The TreePanel</li>
16794         * <li>target - The node being targeted for the drop</li>
16795         * <li>data - The drag data from the drag source</li>
16796         * <li>point - The point of the drop - append, above or below</li>
16797         * <li>source - The drag source</li>
16798         * <li>rawEvent - Raw mouse event</li>
16799         * <li>dropNode - Drop node(s) provided by the source.</li>
16800         * <li>cancel - Set this to true to signal drop not allowed.</li>
16801         * </ul>
16802         * @param {Object} dragOverEvent
16803         */
16804        "nodedragover" : true
16805         
16806     });
16807     if(this.singleExpand){
16808        this.on("beforeexpand", this.restrictExpand, this);
16809     }
16810     if (this.editor) {
16811         this.editor.tree = this;
16812         this.editor = Roo.factory(this.editor, Roo.tree);
16813     }
16814     
16815     if (this.selModel) {
16816         this.selModel = Roo.factory(this.selModel, Roo.tree);
16817     }
16818    
16819 };
16820 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16821     rootVisible : true,
16822     animate: Roo.enableFx,
16823     lines : true,
16824     enableDD : false,
16825     hlDrop : Roo.enableFx,
16826   
16827     renderer: false,
16828     
16829     rendererTip: false,
16830     // private
16831     restrictExpand : function(node){
16832         var p = node.parentNode;
16833         if(p){
16834             if(p.expandedChild && p.expandedChild.parentNode == p){
16835                 p.expandedChild.collapse();
16836             }
16837             p.expandedChild = node;
16838         }
16839     },
16840
16841     // private override
16842     setRootNode : function(node){
16843         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16844         if(!this.rootVisible){
16845             node.ui = new Roo.tree.RootTreeNodeUI(node);
16846         }
16847         return node;
16848     },
16849
16850     /**
16851      * Returns the container element for this TreePanel
16852      */
16853     getEl : function(){
16854         return this.el;
16855     },
16856
16857     /**
16858      * Returns the default TreeLoader for this TreePanel
16859      */
16860     getLoader : function(){
16861         return this.loader;
16862     },
16863
16864     /**
16865      * Expand all nodes
16866      */
16867     expandAll : function(){
16868         this.root.expand(true);
16869     },
16870
16871     /**
16872      * Collapse all nodes
16873      */
16874     collapseAll : function(){
16875         this.root.collapse(true);
16876     },
16877
16878     /**
16879      * Returns the selection model used by this TreePanel
16880      */
16881     getSelectionModel : function(){
16882         if(!this.selModel){
16883             this.selModel = new Roo.tree.DefaultSelectionModel();
16884         }
16885         return this.selModel;
16886     },
16887
16888     /**
16889      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16890      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16891      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16892      * @return {Array}
16893      */
16894     getChecked : function(a, startNode){
16895         startNode = startNode || this.root;
16896         var r = [];
16897         var f = function(){
16898             if(this.attributes.checked){
16899                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16900             }
16901         }
16902         startNode.cascade(f);
16903         return r;
16904     },
16905
16906     /**
16907      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16908      * @param {String} path
16909      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16910      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16911      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16912      */
16913     expandPath : function(path, attr, callback){
16914         attr = attr || "id";
16915         var keys = path.split(this.pathSeparator);
16916         var curNode = this.root;
16917         if(curNode.attributes[attr] != keys[1]){ // invalid root
16918             if(callback){
16919                 callback(false, null);
16920             }
16921             return;
16922         }
16923         var index = 1;
16924         var f = function(){
16925             if(++index == keys.length){
16926                 if(callback){
16927                     callback(true, curNode);
16928                 }
16929                 return;
16930             }
16931             var c = curNode.findChild(attr, keys[index]);
16932             if(!c){
16933                 if(callback){
16934                     callback(false, curNode);
16935                 }
16936                 return;
16937             }
16938             curNode = c;
16939             c.expand(false, false, f);
16940         };
16941         curNode.expand(false, false, f);
16942     },
16943
16944     /**
16945      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16946      * @param {String} path
16947      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16948      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16949      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16950      */
16951     selectPath : function(path, attr, callback){
16952         attr = attr || "id";
16953         var keys = path.split(this.pathSeparator);
16954         var v = keys.pop();
16955         if(keys.length > 0){
16956             var f = function(success, node){
16957                 if(success && node){
16958                     var n = node.findChild(attr, v);
16959                     if(n){
16960                         n.select();
16961                         if(callback){
16962                             callback(true, n);
16963                         }
16964                     }else if(callback){
16965                         callback(false, n);
16966                     }
16967                 }else{
16968                     if(callback){
16969                         callback(false, n);
16970                     }
16971                 }
16972             };
16973             this.expandPath(keys.join(this.pathSeparator), attr, f);
16974         }else{
16975             this.root.select();
16976             if(callback){
16977                 callback(true, this.root);
16978             }
16979         }
16980     },
16981
16982     getTreeEl : function(){
16983         return this.el;
16984     },
16985
16986     /**
16987      * Trigger rendering of this TreePanel
16988      */
16989     render : function(){
16990         if (this.innerCt) {
16991             return this; // stop it rendering more than once!!
16992         }
16993         
16994         this.innerCt = this.el.createChild({tag:"ul",
16995                cls:"x-tree-root-ct " +
16996                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16997
16998         if(this.containerScroll){
16999             Roo.dd.ScrollManager.register(this.el);
17000         }
17001         if((this.enableDD || this.enableDrop) && !this.dropZone){
17002            /**
17003             * The dropZone used by this tree if drop is enabled
17004             * @type Roo.tree.TreeDropZone
17005             */
17006              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17007                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17008            });
17009         }
17010         if((this.enableDD || this.enableDrag) && !this.dragZone){
17011            /**
17012             * The dragZone used by this tree if drag is enabled
17013             * @type Roo.tree.TreeDragZone
17014             */
17015             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17016                ddGroup: this.ddGroup || "TreeDD",
17017                scroll: this.ddScroll
17018            });
17019         }
17020         this.getSelectionModel().init(this);
17021         if (!this.root) {
17022             Roo.log("ROOT not set in tree");
17023             return this;
17024         }
17025         this.root.render();
17026         if(!this.rootVisible){
17027             this.root.renderChildren();
17028         }
17029         return this;
17030     }
17031 });/*
17032  * Based on:
17033  * Ext JS Library 1.1.1
17034  * Copyright(c) 2006-2007, Ext JS, LLC.
17035  *
17036  * Originally Released Under LGPL - original licence link has changed is not relivant.
17037  *
17038  * Fork - LGPL
17039  * <script type="text/javascript">
17040  */
17041  
17042
17043 /**
17044  * @class Roo.tree.DefaultSelectionModel
17045  * @extends Roo.util.Observable
17046  * The default single selection for a TreePanel.
17047  * @param {Object} cfg Configuration
17048  */
17049 Roo.tree.DefaultSelectionModel = function(cfg){
17050    this.selNode = null;
17051    
17052    
17053    
17054    this.addEvents({
17055        /**
17056         * @event selectionchange
17057         * Fires when the selected node changes
17058         * @param {DefaultSelectionModel} this
17059         * @param {TreeNode} node the new selection
17060         */
17061        "selectionchange" : true,
17062
17063        /**
17064         * @event beforeselect
17065         * Fires before the selected node changes, return false to cancel the change
17066         * @param {DefaultSelectionModel} this
17067         * @param {TreeNode} node the new selection
17068         * @param {TreeNode} node the old selection
17069         */
17070        "beforeselect" : true
17071    });
17072    
17073     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17074 };
17075
17076 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17077     init : function(tree){
17078         this.tree = tree;
17079         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17080         tree.on("click", this.onNodeClick, this);
17081     },
17082     
17083     onNodeClick : function(node, e){
17084         if (e.ctrlKey && this.selNode == node)  {
17085             this.unselect(node);
17086             return;
17087         }
17088         this.select(node);
17089     },
17090     
17091     /**
17092      * Select a node.
17093      * @param {TreeNode} node The node to select
17094      * @return {TreeNode} The selected node
17095      */
17096     select : function(node){
17097         var last = this.selNode;
17098         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17099             if(last){
17100                 last.ui.onSelectedChange(false);
17101             }
17102             this.selNode = node;
17103             node.ui.onSelectedChange(true);
17104             this.fireEvent("selectionchange", this, node, last);
17105         }
17106         return node;
17107     },
17108     
17109     /**
17110      * Deselect a node.
17111      * @param {TreeNode} node The node to unselect
17112      */
17113     unselect : function(node){
17114         if(this.selNode == node){
17115             this.clearSelections();
17116         }    
17117     },
17118     
17119     /**
17120      * Clear all selections
17121      */
17122     clearSelections : function(){
17123         var n = this.selNode;
17124         if(n){
17125             n.ui.onSelectedChange(false);
17126             this.selNode = null;
17127             this.fireEvent("selectionchange", this, null);
17128         }
17129         return n;
17130     },
17131     
17132     /**
17133      * Get the selected node
17134      * @return {TreeNode} The selected node
17135      */
17136     getSelectedNode : function(){
17137         return this.selNode;    
17138     },
17139     
17140     /**
17141      * Returns true if the node is selected
17142      * @param {TreeNode} node The node to check
17143      * @return {Boolean}
17144      */
17145     isSelected : function(node){
17146         return this.selNode == node;  
17147     },
17148
17149     /**
17150      * Selects the node above the selected node in the tree, intelligently walking the nodes
17151      * @return TreeNode The new selection
17152      */
17153     selectPrevious : function(){
17154         var s = this.selNode || this.lastSelNode;
17155         if(!s){
17156             return null;
17157         }
17158         var ps = s.previousSibling;
17159         if(ps){
17160             if(!ps.isExpanded() || ps.childNodes.length < 1){
17161                 return this.select(ps);
17162             } else{
17163                 var lc = ps.lastChild;
17164                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17165                     lc = lc.lastChild;
17166                 }
17167                 return this.select(lc);
17168             }
17169         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17170             return this.select(s.parentNode);
17171         }
17172         return null;
17173     },
17174
17175     /**
17176      * Selects the node above the selected node in the tree, intelligently walking the nodes
17177      * @return TreeNode The new selection
17178      */
17179     selectNext : function(){
17180         var s = this.selNode || this.lastSelNode;
17181         if(!s){
17182             return null;
17183         }
17184         if(s.firstChild && s.isExpanded()){
17185              return this.select(s.firstChild);
17186          }else if(s.nextSibling){
17187              return this.select(s.nextSibling);
17188          }else if(s.parentNode){
17189             var newS = null;
17190             s.parentNode.bubble(function(){
17191                 if(this.nextSibling){
17192                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17193                     return false;
17194                 }
17195             });
17196             return newS;
17197          }
17198         return null;
17199     },
17200
17201     onKeyDown : function(e){
17202         var s = this.selNode || this.lastSelNode;
17203         // undesirable, but required
17204         var sm = this;
17205         if(!s){
17206             return;
17207         }
17208         var k = e.getKey();
17209         switch(k){
17210              case e.DOWN:
17211                  e.stopEvent();
17212                  this.selectNext();
17213              break;
17214              case e.UP:
17215                  e.stopEvent();
17216                  this.selectPrevious();
17217              break;
17218              case e.RIGHT:
17219                  e.preventDefault();
17220                  if(s.hasChildNodes()){
17221                      if(!s.isExpanded()){
17222                          s.expand();
17223                      }else if(s.firstChild){
17224                          this.select(s.firstChild, e);
17225                      }
17226                  }
17227              break;
17228              case e.LEFT:
17229                  e.preventDefault();
17230                  if(s.hasChildNodes() && s.isExpanded()){
17231                      s.collapse();
17232                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17233                      this.select(s.parentNode, e);
17234                  }
17235              break;
17236         };
17237     }
17238 });
17239
17240 /**
17241  * @class Roo.tree.MultiSelectionModel
17242  * @extends Roo.util.Observable
17243  * Multi selection for a TreePanel.
17244  * @param {Object} cfg Configuration
17245  */
17246 Roo.tree.MultiSelectionModel = function(){
17247    this.selNodes = [];
17248    this.selMap = {};
17249    this.addEvents({
17250        /**
17251         * @event selectionchange
17252         * Fires when the selected nodes change
17253         * @param {MultiSelectionModel} this
17254         * @param {Array} nodes Array of the selected nodes
17255         */
17256        "selectionchange" : true
17257    });
17258    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17259    
17260 };
17261
17262 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17263     init : function(tree){
17264         this.tree = tree;
17265         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17266         tree.on("click", this.onNodeClick, this);
17267     },
17268     
17269     onNodeClick : function(node, e){
17270         this.select(node, e, e.ctrlKey);
17271     },
17272     
17273     /**
17274      * Select a node.
17275      * @param {TreeNode} node The node to select
17276      * @param {EventObject} e (optional) An event associated with the selection
17277      * @param {Boolean} keepExisting True to retain existing selections
17278      * @return {TreeNode} The selected node
17279      */
17280     select : function(node, e, keepExisting){
17281         if(keepExisting !== true){
17282             this.clearSelections(true);
17283         }
17284         if(this.isSelected(node)){
17285             this.lastSelNode = node;
17286             return node;
17287         }
17288         this.selNodes.push(node);
17289         this.selMap[node.id] = node;
17290         this.lastSelNode = node;
17291         node.ui.onSelectedChange(true);
17292         this.fireEvent("selectionchange", this, this.selNodes);
17293         return node;
17294     },
17295     
17296     /**
17297      * Deselect a node.
17298      * @param {TreeNode} node The node to unselect
17299      */
17300     unselect : function(node){
17301         if(this.selMap[node.id]){
17302             node.ui.onSelectedChange(false);
17303             var sn = this.selNodes;
17304             var index = -1;
17305             if(sn.indexOf){
17306                 index = sn.indexOf(node);
17307             }else{
17308                 for(var i = 0, len = sn.length; i < len; i++){
17309                     if(sn[i] == node){
17310                         index = i;
17311                         break;
17312                     }
17313                 }
17314             }
17315             if(index != -1){
17316                 this.selNodes.splice(index, 1);
17317             }
17318             delete this.selMap[node.id];
17319             this.fireEvent("selectionchange", this, this.selNodes);
17320         }
17321     },
17322     
17323     /**
17324      * Clear all selections
17325      */
17326     clearSelections : function(suppressEvent){
17327         var sn = this.selNodes;
17328         if(sn.length > 0){
17329             for(var i = 0, len = sn.length; i < len; i++){
17330                 sn[i].ui.onSelectedChange(false);
17331             }
17332             this.selNodes = [];
17333             this.selMap = {};
17334             if(suppressEvent !== true){
17335                 this.fireEvent("selectionchange", this, this.selNodes);
17336             }
17337         }
17338     },
17339     
17340     /**
17341      * Returns true if the node is selected
17342      * @param {TreeNode} node The node to check
17343      * @return {Boolean}
17344      */
17345     isSelected : function(node){
17346         return this.selMap[node.id] ? true : false;  
17347     },
17348     
17349     /**
17350      * Returns an array of the selected nodes
17351      * @return {Array}
17352      */
17353     getSelectedNodes : function(){
17354         return this.selNodes;    
17355     },
17356
17357     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17358
17359     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17360
17361     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17362 });/*
17363  * Based on:
17364  * Ext JS Library 1.1.1
17365  * Copyright(c) 2006-2007, Ext JS, LLC.
17366  *
17367  * Originally Released Under LGPL - original licence link has changed is not relivant.
17368  *
17369  * Fork - LGPL
17370  * <script type="text/javascript">
17371  */
17372  
17373 /**
17374  * @class Roo.tree.TreeNode
17375  * @extends Roo.data.Node
17376  * @cfg {String} text The text for this node
17377  * @cfg {Boolean} expanded true to start the node expanded
17378  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17379  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17380  * @cfg {Boolean} disabled true to start the node disabled
17381  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17382  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17383  * @cfg {String} cls A css class to be added to the node
17384  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17385  * @cfg {String} href URL of the link used for the node (defaults to #)
17386  * @cfg {String} hrefTarget target frame for the link
17387  * @cfg {String} qtip An Ext QuickTip for the node
17388  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17389  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17390  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17391  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17392  * (defaults to undefined with no checkbox rendered)
17393  * @constructor
17394  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17395  */
17396 Roo.tree.TreeNode = function(attributes){
17397     attributes = attributes || {};
17398     if(typeof attributes == "string"){
17399         attributes = {text: attributes};
17400     }
17401     this.childrenRendered = false;
17402     this.rendered = false;
17403     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17404     this.expanded = attributes.expanded === true;
17405     this.isTarget = attributes.isTarget !== false;
17406     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17407     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17408
17409     /**
17410      * Read-only. The text for this node. To change it use setText().
17411      * @type String
17412      */
17413     this.text = attributes.text;
17414     /**
17415      * True if this node is disabled.
17416      * @type Boolean
17417      */
17418     this.disabled = attributes.disabled === true;
17419
17420     this.addEvents({
17421         /**
17422         * @event textchange
17423         * Fires when the text for this node is changed
17424         * @param {Node} this This node
17425         * @param {String} text The new text
17426         * @param {String} oldText The old text
17427         */
17428         "textchange" : true,
17429         /**
17430         * @event beforeexpand
17431         * Fires before this node is expanded, return false to cancel.
17432         * @param {Node} this This node
17433         * @param {Boolean} deep
17434         * @param {Boolean} anim
17435         */
17436         "beforeexpand" : true,
17437         /**
17438         * @event beforecollapse
17439         * Fires before this node is collapsed, return false to cancel.
17440         * @param {Node} this This node
17441         * @param {Boolean} deep
17442         * @param {Boolean} anim
17443         */
17444         "beforecollapse" : true,
17445         /**
17446         * @event expand
17447         * Fires when this node is expanded
17448         * @param {Node} this This node
17449         */
17450         "expand" : true,
17451         /**
17452         * @event disabledchange
17453         * Fires when the disabled status of this node changes
17454         * @param {Node} this This node
17455         * @param {Boolean} disabled
17456         */
17457         "disabledchange" : true,
17458         /**
17459         * @event collapse
17460         * Fires when this node is collapsed
17461         * @param {Node} this This node
17462         */
17463         "collapse" : true,
17464         /**
17465         * @event beforeclick
17466         * Fires before click processing. Return false to cancel the default action.
17467         * @param {Node} this This node
17468         * @param {Roo.EventObject} e The event object
17469         */
17470         "beforeclick":true,
17471         /**
17472         * @event checkchange
17473         * Fires when a node with a checkbox's checked property changes
17474         * @param {Node} this This node
17475         * @param {Boolean} checked
17476         */
17477         "checkchange":true,
17478         /**
17479         * @event click
17480         * Fires when this node is clicked
17481         * @param {Node} this This node
17482         * @param {Roo.EventObject} e The event object
17483         */
17484         "click":true,
17485         /**
17486         * @event dblclick
17487         * Fires when this node is double clicked
17488         * @param {Node} this This node
17489         * @param {Roo.EventObject} e The event object
17490         */
17491         "dblclick":true,
17492         /**
17493         * @event contextmenu
17494         * Fires when this node is right clicked
17495         * @param {Node} this This node
17496         * @param {Roo.EventObject} e The event object
17497         */
17498         "contextmenu":true,
17499         /**
17500         * @event beforechildrenrendered
17501         * Fires right before the child nodes for this node are rendered
17502         * @param {Node} this This node
17503         */
17504         "beforechildrenrendered":true
17505     });
17506
17507     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17508
17509     /**
17510      * Read-only. The UI for this node
17511      * @type TreeNodeUI
17512      */
17513     this.ui = new uiClass(this);
17514     
17515     // finally support items[]
17516     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17517         return;
17518     }
17519     
17520     
17521     Roo.each(this.attributes.items, function(c) {
17522         this.appendChild(Roo.factory(c,Roo.Tree));
17523     }, this);
17524     delete this.attributes.items;
17525     
17526     
17527     
17528 };
17529 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17530     preventHScroll: true,
17531     /**
17532      * Returns true if this node is expanded
17533      * @return {Boolean}
17534      */
17535     isExpanded : function(){
17536         return this.expanded;
17537     },
17538
17539     /**
17540      * Returns the UI object for this node
17541      * @return {TreeNodeUI}
17542      */
17543     getUI : function(){
17544         return this.ui;
17545     },
17546
17547     // private override
17548     setFirstChild : function(node){
17549         var of = this.firstChild;
17550         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17551         if(this.childrenRendered && of && node != of){
17552             of.renderIndent(true, true);
17553         }
17554         if(this.rendered){
17555             this.renderIndent(true, true);
17556         }
17557     },
17558
17559     // private override
17560     setLastChild : function(node){
17561         var ol = this.lastChild;
17562         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17563         if(this.childrenRendered && ol && node != ol){
17564             ol.renderIndent(true, true);
17565         }
17566         if(this.rendered){
17567             this.renderIndent(true, true);
17568         }
17569     },
17570
17571     // these methods are overridden to provide lazy rendering support
17572     // private override
17573     appendChild : function()
17574     {
17575         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17576         if(node && this.childrenRendered){
17577             node.render();
17578         }
17579         this.ui.updateExpandIcon();
17580         return node;
17581     },
17582
17583     // private override
17584     removeChild : function(node){
17585         this.ownerTree.getSelectionModel().unselect(node);
17586         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17587         // if it's been rendered remove dom node
17588         if(this.childrenRendered){
17589             node.ui.remove();
17590         }
17591         if(this.childNodes.length < 1){
17592             this.collapse(false, false);
17593         }else{
17594             this.ui.updateExpandIcon();
17595         }
17596         if(!this.firstChild) {
17597             this.childrenRendered = false;
17598         }
17599         return node;
17600     },
17601
17602     // private override
17603     insertBefore : function(node, refNode){
17604         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17605         if(newNode && refNode && this.childrenRendered){
17606             node.render();
17607         }
17608         this.ui.updateExpandIcon();
17609         return newNode;
17610     },
17611
17612     /**
17613      * Sets the text for this node
17614      * @param {String} text
17615      */
17616     setText : function(text){
17617         var oldText = this.text;
17618         this.text = text;
17619         this.attributes.text = text;
17620         if(this.rendered){ // event without subscribing
17621             this.ui.onTextChange(this, text, oldText);
17622         }
17623         this.fireEvent("textchange", this, text, oldText);
17624     },
17625
17626     /**
17627      * Triggers selection of this node
17628      */
17629     select : function(){
17630         this.getOwnerTree().getSelectionModel().select(this);
17631     },
17632
17633     /**
17634      * Triggers deselection of this node
17635      */
17636     unselect : function(){
17637         this.getOwnerTree().getSelectionModel().unselect(this);
17638     },
17639
17640     /**
17641      * Returns true if this node is selected
17642      * @return {Boolean}
17643      */
17644     isSelected : function(){
17645         return this.getOwnerTree().getSelectionModel().isSelected(this);
17646     },
17647
17648     /**
17649      * Expand this node.
17650      * @param {Boolean} deep (optional) True to expand all children as well
17651      * @param {Boolean} anim (optional) false to cancel the default animation
17652      * @param {Function} callback (optional) A callback to be called when
17653      * expanding this node completes (does not wait for deep expand to complete).
17654      * Called with 1 parameter, this node.
17655      */
17656     expand : function(deep, anim, callback){
17657         if(!this.expanded){
17658             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17659                 return;
17660             }
17661             if(!this.childrenRendered){
17662                 this.renderChildren();
17663             }
17664             this.expanded = true;
17665             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17666                 this.ui.animExpand(function(){
17667                     this.fireEvent("expand", this);
17668                     if(typeof callback == "function"){
17669                         callback(this);
17670                     }
17671                     if(deep === true){
17672                         this.expandChildNodes(true);
17673                     }
17674                 }.createDelegate(this));
17675                 return;
17676             }else{
17677                 this.ui.expand();
17678                 this.fireEvent("expand", this);
17679                 if(typeof callback == "function"){
17680                     callback(this);
17681                 }
17682             }
17683         }else{
17684            if(typeof callback == "function"){
17685                callback(this);
17686            }
17687         }
17688         if(deep === true){
17689             this.expandChildNodes(true);
17690         }
17691     },
17692
17693     isHiddenRoot : function(){
17694         return this.isRoot && !this.getOwnerTree().rootVisible;
17695     },
17696
17697     /**
17698      * Collapse this node.
17699      * @param {Boolean} deep (optional) True to collapse all children as well
17700      * @param {Boolean} anim (optional) false to cancel the default animation
17701      */
17702     collapse : function(deep, anim){
17703         if(this.expanded && !this.isHiddenRoot()){
17704             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17705                 return;
17706             }
17707             this.expanded = false;
17708             if((this.getOwnerTree().animate && anim !== false) || anim){
17709                 this.ui.animCollapse(function(){
17710                     this.fireEvent("collapse", this);
17711                     if(deep === true){
17712                         this.collapseChildNodes(true);
17713                     }
17714                 }.createDelegate(this));
17715                 return;
17716             }else{
17717                 this.ui.collapse();
17718                 this.fireEvent("collapse", this);
17719             }
17720         }
17721         if(deep === true){
17722             var cs = this.childNodes;
17723             for(var i = 0, len = cs.length; i < len; i++) {
17724                 cs[i].collapse(true, false);
17725             }
17726         }
17727     },
17728
17729     // private
17730     delayedExpand : function(delay){
17731         if(!this.expandProcId){
17732             this.expandProcId = this.expand.defer(delay, this);
17733         }
17734     },
17735
17736     // private
17737     cancelExpand : function(){
17738         if(this.expandProcId){
17739             clearTimeout(this.expandProcId);
17740         }
17741         this.expandProcId = false;
17742     },
17743
17744     /**
17745      * Toggles expanded/collapsed state of the node
17746      */
17747     toggle : function(){
17748         if(this.expanded){
17749             this.collapse();
17750         }else{
17751             this.expand();
17752         }
17753     },
17754
17755     /**
17756      * Ensures all parent nodes are expanded
17757      */
17758     ensureVisible : function(callback){
17759         var tree = this.getOwnerTree();
17760         tree.expandPath(this.parentNode.getPath(), false, function(){
17761             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17762             Roo.callback(callback);
17763         }.createDelegate(this));
17764     },
17765
17766     /**
17767      * Expand all child nodes
17768      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17769      */
17770     expandChildNodes : function(deep){
17771         var cs = this.childNodes;
17772         for(var i = 0, len = cs.length; i < len; i++) {
17773                 cs[i].expand(deep);
17774         }
17775     },
17776
17777     /**
17778      * Collapse all child nodes
17779      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17780      */
17781     collapseChildNodes : function(deep){
17782         var cs = this.childNodes;
17783         for(var i = 0, len = cs.length; i < len; i++) {
17784                 cs[i].collapse(deep);
17785         }
17786     },
17787
17788     /**
17789      * Disables this node
17790      */
17791     disable : function(){
17792         this.disabled = true;
17793         this.unselect();
17794         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17795             this.ui.onDisableChange(this, true);
17796         }
17797         this.fireEvent("disabledchange", this, true);
17798     },
17799
17800     /**
17801      * Enables this node
17802      */
17803     enable : function(){
17804         this.disabled = false;
17805         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17806             this.ui.onDisableChange(this, false);
17807         }
17808         this.fireEvent("disabledchange", this, false);
17809     },
17810
17811     // private
17812     renderChildren : function(suppressEvent){
17813         if(suppressEvent !== false){
17814             this.fireEvent("beforechildrenrendered", this);
17815         }
17816         var cs = this.childNodes;
17817         for(var i = 0, len = cs.length; i < len; i++){
17818             cs[i].render(true);
17819         }
17820         this.childrenRendered = true;
17821     },
17822
17823     // private
17824     sort : function(fn, scope){
17825         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17826         if(this.childrenRendered){
17827             var cs = this.childNodes;
17828             for(var i = 0, len = cs.length; i < len; i++){
17829                 cs[i].render(true);
17830             }
17831         }
17832     },
17833
17834     // private
17835     render : function(bulkRender){
17836         this.ui.render(bulkRender);
17837         if(!this.rendered){
17838             this.rendered = true;
17839             if(this.expanded){
17840                 this.expanded = false;
17841                 this.expand(false, false);
17842             }
17843         }
17844     },
17845
17846     // private
17847     renderIndent : function(deep, refresh){
17848         if(refresh){
17849             this.ui.childIndent = null;
17850         }
17851         this.ui.renderIndent();
17852         if(deep === true && this.childrenRendered){
17853             var cs = this.childNodes;
17854             for(var i = 0, len = cs.length; i < len; i++){
17855                 cs[i].renderIndent(true, refresh);
17856             }
17857         }
17858     }
17859 });/*
17860  * Based on:
17861  * Ext JS Library 1.1.1
17862  * Copyright(c) 2006-2007, Ext JS, LLC.
17863  *
17864  * Originally Released Under LGPL - original licence link has changed is not relivant.
17865  *
17866  * Fork - LGPL
17867  * <script type="text/javascript">
17868  */
17869  
17870 /**
17871  * @class Roo.tree.AsyncTreeNode
17872  * @extends Roo.tree.TreeNode
17873  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17874  * @constructor
17875  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17876  */
17877  Roo.tree.AsyncTreeNode = function(config){
17878     this.loaded = false;
17879     this.loading = false;
17880     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17881     /**
17882     * @event beforeload
17883     * Fires before this node is loaded, return false to cancel
17884     * @param {Node} this This node
17885     */
17886     this.addEvents({'beforeload':true, 'load': true});
17887     /**
17888     * @event load
17889     * Fires when this node is loaded
17890     * @param {Node} this This node
17891     */
17892     /**
17893      * The loader used by this node (defaults to using the tree's defined loader)
17894      * @type TreeLoader
17895      * @property loader
17896      */
17897 };
17898 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17899     expand : function(deep, anim, callback){
17900         if(this.loading){ // if an async load is already running, waiting til it's done
17901             var timer;
17902             var f = function(){
17903                 if(!this.loading){ // done loading
17904                     clearInterval(timer);
17905                     this.expand(deep, anim, callback);
17906                 }
17907             }.createDelegate(this);
17908             timer = setInterval(f, 200);
17909             return;
17910         }
17911         if(!this.loaded){
17912             if(this.fireEvent("beforeload", this) === false){
17913                 return;
17914             }
17915             this.loading = true;
17916             this.ui.beforeLoad(this);
17917             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17918             if(loader){
17919                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17920                 return;
17921             }
17922         }
17923         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17924     },
17925     
17926     /**
17927      * Returns true if this node is currently loading
17928      * @return {Boolean}
17929      */
17930     isLoading : function(){
17931         return this.loading;  
17932     },
17933     
17934     loadComplete : function(deep, anim, callback){
17935         this.loading = false;
17936         this.loaded = true;
17937         this.ui.afterLoad(this);
17938         this.fireEvent("load", this);
17939         this.expand(deep, anim, callback);
17940     },
17941     
17942     /**
17943      * Returns true if this node has been loaded
17944      * @return {Boolean}
17945      */
17946     isLoaded : function(){
17947         return this.loaded;
17948     },
17949     
17950     hasChildNodes : function(){
17951         if(!this.isLeaf() && !this.loaded){
17952             return true;
17953         }else{
17954             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17955         }
17956     },
17957
17958     /**
17959      * Trigger a reload for this node
17960      * @param {Function} callback
17961      */
17962     reload : function(callback){
17963         this.collapse(false, false);
17964         while(this.firstChild){
17965             this.removeChild(this.firstChild);
17966         }
17967         this.childrenRendered = false;
17968         this.loaded = false;
17969         if(this.isHiddenRoot()){
17970             this.expanded = false;
17971         }
17972         this.expand(false, false, callback);
17973     }
17974 });/*
17975  * Based on:
17976  * Ext JS Library 1.1.1
17977  * Copyright(c) 2006-2007, Ext JS, LLC.
17978  *
17979  * Originally Released Under LGPL - original licence link has changed is not relivant.
17980  *
17981  * Fork - LGPL
17982  * <script type="text/javascript">
17983  */
17984  
17985 /**
17986  * @class Roo.tree.TreeNodeUI
17987  * @constructor
17988  * @param {Object} node The node to render
17989  * The TreeNode UI implementation is separate from the
17990  * tree implementation. Unless you are customizing the tree UI,
17991  * you should never have to use this directly.
17992  */
17993 Roo.tree.TreeNodeUI = function(node){
17994     this.node = node;
17995     this.rendered = false;
17996     this.animating = false;
17997     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17998 };
17999
18000 Roo.tree.TreeNodeUI.prototype = {
18001     removeChild : function(node){
18002         if(this.rendered){
18003             this.ctNode.removeChild(node.ui.getEl());
18004         }
18005     },
18006
18007     beforeLoad : function(){
18008          this.addClass("x-tree-node-loading");
18009     },
18010
18011     afterLoad : function(){
18012          this.removeClass("x-tree-node-loading");
18013     },
18014
18015     onTextChange : function(node, text, oldText){
18016         if(this.rendered){
18017             this.textNode.innerHTML = text;
18018         }
18019     },
18020
18021     onDisableChange : function(node, state){
18022         this.disabled = state;
18023         if(state){
18024             this.addClass("x-tree-node-disabled");
18025         }else{
18026             this.removeClass("x-tree-node-disabled");
18027         }
18028     },
18029
18030     onSelectedChange : function(state){
18031         if(state){
18032             this.focus();
18033             this.addClass("x-tree-selected");
18034         }else{
18035             //this.blur();
18036             this.removeClass("x-tree-selected");
18037         }
18038     },
18039
18040     onMove : function(tree, node, oldParent, newParent, index, refNode){
18041         this.childIndent = null;
18042         if(this.rendered){
18043             var targetNode = newParent.ui.getContainer();
18044             if(!targetNode){//target not rendered
18045                 this.holder = document.createElement("div");
18046                 this.holder.appendChild(this.wrap);
18047                 return;
18048             }
18049             var insertBefore = refNode ? refNode.ui.getEl() : null;
18050             if(insertBefore){
18051                 targetNode.insertBefore(this.wrap, insertBefore);
18052             }else{
18053                 targetNode.appendChild(this.wrap);
18054             }
18055             this.node.renderIndent(true);
18056         }
18057     },
18058
18059     addClass : function(cls){
18060         if(this.elNode){
18061             Roo.fly(this.elNode).addClass(cls);
18062         }
18063     },
18064
18065     removeClass : function(cls){
18066         if(this.elNode){
18067             Roo.fly(this.elNode).removeClass(cls);
18068         }
18069     },
18070
18071     remove : function(){
18072         if(this.rendered){
18073             this.holder = document.createElement("div");
18074             this.holder.appendChild(this.wrap);
18075         }
18076     },
18077
18078     fireEvent : function(){
18079         return this.node.fireEvent.apply(this.node, arguments);
18080     },
18081
18082     initEvents : function(){
18083         this.node.on("move", this.onMove, this);
18084         var E = Roo.EventManager;
18085         var a = this.anchor;
18086
18087         var el = Roo.fly(a, '_treeui');
18088
18089         if(Roo.isOpera){ // opera render bug ignores the CSS
18090             el.setStyle("text-decoration", "none");
18091         }
18092
18093         el.on("click", this.onClick, this);
18094         el.on("dblclick", this.onDblClick, this);
18095
18096         if(this.checkbox){
18097             Roo.EventManager.on(this.checkbox,
18098                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18099         }
18100
18101         el.on("contextmenu", this.onContextMenu, this);
18102
18103         var icon = Roo.fly(this.iconNode);
18104         icon.on("click", this.onClick, this);
18105         icon.on("dblclick", this.onDblClick, this);
18106         icon.on("contextmenu", this.onContextMenu, this);
18107         E.on(this.ecNode, "click", this.ecClick, this, true);
18108
18109         if(this.node.disabled){
18110             this.addClass("x-tree-node-disabled");
18111         }
18112         if(this.node.hidden){
18113             this.addClass("x-tree-node-disabled");
18114         }
18115         var ot = this.node.getOwnerTree();
18116         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18117         if(dd && (!this.node.isRoot || ot.rootVisible)){
18118             Roo.dd.Registry.register(this.elNode, {
18119                 node: this.node,
18120                 handles: this.getDDHandles(),
18121                 isHandle: false
18122             });
18123         }
18124     },
18125
18126     getDDHandles : function(){
18127         return [this.iconNode, this.textNode];
18128     },
18129
18130     hide : function(){
18131         if(this.rendered){
18132             this.wrap.style.display = "none";
18133         }
18134     },
18135
18136     show : function(){
18137         if(this.rendered){
18138             this.wrap.style.display = "";
18139         }
18140     },
18141
18142     onContextMenu : function(e){
18143         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18144             e.preventDefault();
18145             this.focus();
18146             this.fireEvent("contextmenu", this.node, e);
18147         }
18148     },
18149
18150     onClick : function(e){
18151         if(this.dropping){
18152             e.stopEvent();
18153             return;
18154         }
18155         if(this.fireEvent("beforeclick", this.node, e) !== false){
18156             if(!this.disabled && this.node.attributes.href){
18157                 this.fireEvent("click", this.node, e);
18158                 return;
18159             }
18160             e.preventDefault();
18161             if(this.disabled){
18162                 return;
18163             }
18164
18165             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18166                 this.node.toggle();
18167             }
18168
18169             this.fireEvent("click", this.node, e);
18170         }else{
18171             e.stopEvent();
18172         }
18173     },
18174
18175     onDblClick : function(e){
18176         e.preventDefault();
18177         if(this.disabled){
18178             return;
18179         }
18180         if(this.checkbox){
18181             this.toggleCheck();
18182         }
18183         if(!this.animating && this.node.hasChildNodes()){
18184             this.node.toggle();
18185         }
18186         this.fireEvent("dblclick", this.node, e);
18187     },
18188
18189     onCheckChange : function(){
18190         var checked = this.checkbox.checked;
18191         this.node.attributes.checked = checked;
18192         this.fireEvent('checkchange', this.node, checked);
18193     },
18194
18195     ecClick : function(e){
18196         if(!this.animating && this.node.hasChildNodes()){
18197             this.node.toggle();
18198         }
18199     },
18200
18201     startDrop : function(){
18202         this.dropping = true;
18203     },
18204
18205     // delayed drop so the click event doesn't get fired on a drop
18206     endDrop : function(){
18207        setTimeout(function(){
18208            this.dropping = false;
18209        }.createDelegate(this), 50);
18210     },
18211
18212     expand : function(){
18213         this.updateExpandIcon();
18214         this.ctNode.style.display = "";
18215     },
18216
18217     focus : function(){
18218         if(!this.node.preventHScroll){
18219             try{this.anchor.focus();
18220             }catch(e){}
18221         }else if(!Roo.isIE){
18222             try{
18223                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18224                 var l = noscroll.scrollLeft;
18225                 this.anchor.focus();
18226                 noscroll.scrollLeft = l;
18227             }catch(e){}
18228         }
18229     },
18230
18231     toggleCheck : function(value){
18232         var cb = this.checkbox;
18233         if(cb){
18234             cb.checked = (value === undefined ? !cb.checked : value);
18235         }
18236     },
18237
18238     blur : function(){
18239         try{
18240             this.anchor.blur();
18241         }catch(e){}
18242     },
18243
18244     animExpand : function(callback){
18245         var ct = Roo.get(this.ctNode);
18246         ct.stopFx();
18247         if(!this.node.hasChildNodes()){
18248             this.updateExpandIcon();
18249             this.ctNode.style.display = "";
18250             Roo.callback(callback);
18251             return;
18252         }
18253         this.animating = true;
18254         this.updateExpandIcon();
18255
18256         ct.slideIn('t', {
18257            callback : function(){
18258                this.animating = false;
18259                Roo.callback(callback);
18260             },
18261             scope: this,
18262             duration: this.node.ownerTree.duration || .25
18263         });
18264     },
18265
18266     highlight : function(){
18267         var tree = this.node.getOwnerTree();
18268         Roo.fly(this.wrap).highlight(
18269             tree.hlColor || "C3DAF9",
18270             {endColor: tree.hlBaseColor}
18271         );
18272     },
18273
18274     collapse : function(){
18275         this.updateExpandIcon();
18276         this.ctNode.style.display = "none";
18277     },
18278
18279     animCollapse : function(callback){
18280         var ct = Roo.get(this.ctNode);
18281         ct.enableDisplayMode('block');
18282         ct.stopFx();
18283
18284         this.animating = true;
18285         this.updateExpandIcon();
18286
18287         ct.slideOut('t', {
18288             callback : function(){
18289                this.animating = false;
18290                Roo.callback(callback);
18291             },
18292             scope: this,
18293             duration: this.node.ownerTree.duration || .25
18294         });
18295     },
18296
18297     getContainer : function(){
18298         return this.ctNode;
18299     },
18300
18301     getEl : function(){
18302         return this.wrap;
18303     },
18304
18305     appendDDGhost : function(ghostNode){
18306         ghostNode.appendChild(this.elNode.cloneNode(true));
18307     },
18308
18309     getDDRepairXY : function(){
18310         return Roo.lib.Dom.getXY(this.iconNode);
18311     },
18312
18313     onRender : function(){
18314         this.render();
18315     },
18316
18317     render : function(bulkRender){
18318         var n = this.node, a = n.attributes;
18319         var targetNode = n.parentNode ?
18320               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18321
18322         if(!this.rendered){
18323             this.rendered = true;
18324
18325             this.renderElements(n, a, targetNode, bulkRender);
18326
18327             if(a.qtip){
18328                if(this.textNode.setAttributeNS){
18329                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18330                    if(a.qtipTitle){
18331                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18332                    }
18333                }else{
18334                    this.textNode.setAttribute("ext:qtip", a.qtip);
18335                    if(a.qtipTitle){
18336                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18337                    }
18338                }
18339             }else if(a.qtipCfg){
18340                 a.qtipCfg.target = Roo.id(this.textNode);
18341                 Roo.QuickTips.register(a.qtipCfg);
18342             }
18343             this.initEvents();
18344             if(!this.node.expanded){
18345                 this.updateExpandIcon();
18346             }
18347         }else{
18348             if(bulkRender === true) {
18349                 targetNode.appendChild(this.wrap);
18350             }
18351         }
18352     },
18353
18354     renderElements : function(n, a, targetNode, bulkRender)
18355     {
18356         // add some indent caching, this helps performance when rendering a large tree
18357         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18358         var t = n.getOwnerTree();
18359         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18360         if (typeof(n.attributes.html) != 'undefined') {
18361             txt = n.attributes.html;
18362         }
18363         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18364         var cb = typeof a.checked == 'boolean';
18365         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18366         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18367             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18368             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18369             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18370             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18371             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18372              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18373                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18374             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18375             "</li>"];
18376
18377         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18378             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18379                                 n.nextSibling.ui.getEl(), buf.join(""));
18380         }else{
18381             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18382         }
18383
18384         this.elNode = this.wrap.childNodes[0];
18385         this.ctNode = this.wrap.childNodes[1];
18386         var cs = this.elNode.childNodes;
18387         this.indentNode = cs[0];
18388         this.ecNode = cs[1];
18389         this.iconNode = cs[2];
18390         var index = 3;
18391         if(cb){
18392             this.checkbox = cs[3];
18393             index++;
18394         }
18395         this.anchor = cs[index];
18396         this.textNode = cs[index].firstChild;
18397     },
18398
18399     getAnchor : function(){
18400         return this.anchor;
18401     },
18402
18403     getTextEl : function(){
18404         return this.textNode;
18405     },
18406
18407     getIconEl : function(){
18408         return this.iconNode;
18409     },
18410
18411     isChecked : function(){
18412         return this.checkbox ? this.checkbox.checked : false;
18413     },
18414
18415     updateExpandIcon : function(){
18416         if(this.rendered){
18417             var n = this.node, c1, c2;
18418             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18419             var hasChild = n.hasChildNodes();
18420             if(hasChild){
18421                 if(n.expanded){
18422                     cls += "-minus";
18423                     c1 = "x-tree-node-collapsed";
18424                     c2 = "x-tree-node-expanded";
18425                 }else{
18426                     cls += "-plus";
18427                     c1 = "x-tree-node-expanded";
18428                     c2 = "x-tree-node-collapsed";
18429                 }
18430                 if(this.wasLeaf){
18431                     this.removeClass("x-tree-node-leaf");
18432                     this.wasLeaf = false;
18433                 }
18434                 if(this.c1 != c1 || this.c2 != c2){
18435                     Roo.fly(this.elNode).replaceClass(c1, c2);
18436                     this.c1 = c1; this.c2 = c2;
18437                 }
18438             }else{
18439                 // this changes non-leafs into leafs if they have no children.
18440                 // it's not very rational behaviour..
18441                 
18442                 if(!this.wasLeaf && this.node.leaf){
18443                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18444                     delete this.c1;
18445                     delete this.c2;
18446                     this.wasLeaf = true;
18447                 }
18448             }
18449             var ecc = "x-tree-ec-icon "+cls;
18450             if(this.ecc != ecc){
18451                 this.ecNode.className = ecc;
18452                 this.ecc = ecc;
18453             }
18454         }
18455     },
18456
18457     getChildIndent : function(){
18458         if(!this.childIndent){
18459             var buf = [];
18460             var p = this.node;
18461             while(p){
18462                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18463                     if(!p.isLast()) {
18464                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18465                     } else {
18466                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18467                     }
18468                 }
18469                 p = p.parentNode;
18470             }
18471             this.childIndent = buf.join("");
18472         }
18473         return this.childIndent;
18474     },
18475
18476     renderIndent : function(){
18477         if(this.rendered){
18478             var indent = "";
18479             var p = this.node.parentNode;
18480             if(p){
18481                 indent = p.ui.getChildIndent();
18482             }
18483             if(this.indentMarkup != indent){ // don't rerender if not required
18484                 this.indentNode.innerHTML = indent;
18485                 this.indentMarkup = indent;
18486             }
18487             this.updateExpandIcon();
18488         }
18489     }
18490 };
18491
18492 Roo.tree.RootTreeNodeUI = function(){
18493     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18494 };
18495 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18496     render : function(){
18497         if(!this.rendered){
18498             var targetNode = this.node.ownerTree.innerCt.dom;
18499             this.node.expanded = true;
18500             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18501             this.wrap = this.ctNode = targetNode.firstChild;
18502         }
18503     },
18504     collapse : function(){
18505     },
18506     expand : function(){
18507     }
18508 });/*
18509  * Based on:
18510  * Ext JS Library 1.1.1
18511  * Copyright(c) 2006-2007, Ext JS, LLC.
18512  *
18513  * Originally Released Under LGPL - original licence link has changed is not relivant.
18514  *
18515  * Fork - LGPL
18516  * <script type="text/javascript">
18517  */
18518 /**
18519  * @class Roo.tree.TreeLoader
18520  * @extends Roo.util.Observable
18521  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18522  * nodes from a specified URL. The response must be a javascript Array definition
18523  * who's elements are node definition objects. eg:
18524  * <pre><code>
18525 {  success : true,
18526    data :      [
18527    
18528     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18529     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18530     ]
18531 }
18532
18533
18534 </code></pre>
18535  * <br><br>
18536  * The old style respose with just an array is still supported, but not recommended.
18537  * <br><br>
18538  *
18539  * A server request is sent, and child nodes are loaded only when a node is expanded.
18540  * The loading node's id is passed to the server under the parameter name "node" to
18541  * enable the server to produce the correct child nodes.
18542  * <br><br>
18543  * To pass extra parameters, an event handler may be attached to the "beforeload"
18544  * event, and the parameters specified in the TreeLoader's baseParams property:
18545  * <pre><code>
18546     myTreeLoader.on("beforeload", function(treeLoader, node) {
18547         this.baseParams.category = node.attributes.category;
18548     }, this);
18549 </code></pre><
18550  * This would pass an HTTP parameter called "category" to the server containing
18551  * the value of the Node's "category" attribute.
18552  * @constructor
18553  * Creates a new Treeloader.
18554  * @param {Object} config A config object containing config properties.
18555  */
18556 Roo.tree.TreeLoader = function(config){
18557     this.baseParams = {};
18558     this.requestMethod = "POST";
18559     Roo.apply(this, config);
18560
18561     this.addEvents({
18562     
18563         /**
18564          * @event beforeload
18565          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18566          * @param {Object} This TreeLoader object.
18567          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18568          * @param {Object} callback The callback function specified in the {@link #load} call.
18569          */
18570         beforeload : true,
18571         /**
18572          * @event load
18573          * Fires when the node has been successfuly loaded.
18574          * @param {Object} This TreeLoader object.
18575          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18576          * @param {Object} response The response object containing the data from the server.
18577          */
18578         load : true,
18579         /**
18580          * @event loadexception
18581          * Fires if the network request failed.
18582          * @param {Object} This TreeLoader object.
18583          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18584          * @param {Object} response The response object containing the data from the server.
18585          */
18586         loadexception : true,
18587         /**
18588          * @event create
18589          * Fires before a node is created, enabling you to return custom Node types 
18590          * @param {Object} This TreeLoader object.
18591          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18592          */
18593         create : true
18594     });
18595
18596     Roo.tree.TreeLoader.superclass.constructor.call(this);
18597 };
18598
18599 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18600     /**
18601     * @cfg {String} dataUrl The URL from which to request a Json string which
18602     * specifies an array of node definition object representing the child nodes
18603     * to be loaded.
18604     */
18605     /**
18606     * @cfg {String} requestMethod either GET or POST
18607     * defaults to POST (due to BC)
18608     * to be loaded.
18609     */
18610     /**
18611     * @cfg {Object} baseParams (optional) An object containing properties which
18612     * specify HTTP parameters to be passed to each request for child nodes.
18613     */
18614     /**
18615     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18616     * created by this loader. If the attributes sent by the server have an attribute in this object,
18617     * they take priority.
18618     */
18619     /**
18620     * @cfg {Object} uiProviders (optional) An object containing properties which
18621     * 
18622     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18623     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18624     * <i>uiProvider</i> attribute of a returned child node is a string rather
18625     * than a reference to a TreeNodeUI implementation, this that string value
18626     * is used as a property name in the uiProviders object. You can define the provider named
18627     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18628     */
18629     uiProviders : {},
18630
18631     /**
18632     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18633     * child nodes before loading.
18634     */
18635     clearOnLoad : true,
18636
18637     /**
18638     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18639     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18640     * Grid query { data : [ .....] }
18641     */
18642     
18643     root : false,
18644      /**
18645     * @cfg {String} queryParam (optional) 
18646     * Name of the query as it will be passed on the querystring (defaults to 'node')
18647     * eg. the request will be ?node=[id]
18648     */
18649     
18650     
18651     queryParam: false,
18652     
18653     /**
18654      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18655      * This is called automatically when a node is expanded, but may be used to reload
18656      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18657      * @param {Roo.tree.TreeNode} node
18658      * @param {Function} callback
18659      */
18660     load : function(node, callback){
18661         if(this.clearOnLoad){
18662             while(node.firstChild){
18663                 node.removeChild(node.firstChild);
18664             }
18665         }
18666         if(node.attributes.children){ // preloaded json children
18667             var cs = node.attributes.children;
18668             for(var i = 0, len = cs.length; i < len; i++){
18669                 node.appendChild(this.createNode(cs[i]));
18670             }
18671             if(typeof callback == "function"){
18672                 callback();
18673             }
18674         }else if(this.dataUrl){
18675             this.requestData(node, callback);
18676         }
18677     },
18678
18679     getParams: function(node){
18680         var buf = [], bp = this.baseParams;
18681         for(var key in bp){
18682             if(typeof bp[key] != "function"){
18683                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18684             }
18685         }
18686         var n = this.queryParam === false ? 'node' : this.queryParam;
18687         buf.push(n + "=", encodeURIComponent(node.id));
18688         return buf.join("");
18689     },
18690
18691     requestData : function(node, callback){
18692         if(this.fireEvent("beforeload", this, node, callback) !== false){
18693             this.transId = Roo.Ajax.request({
18694                 method:this.requestMethod,
18695                 url: this.dataUrl||this.url,
18696                 success: this.handleResponse,
18697                 failure: this.handleFailure,
18698                 scope: this,
18699                 argument: {callback: callback, node: node},
18700                 params: this.getParams(node)
18701             });
18702         }else{
18703             // if the load is cancelled, make sure we notify
18704             // the node that we are done
18705             if(typeof callback == "function"){
18706                 callback();
18707             }
18708         }
18709     },
18710
18711     isLoading : function(){
18712         return this.transId ? true : false;
18713     },
18714
18715     abort : function(){
18716         if(this.isLoading()){
18717             Roo.Ajax.abort(this.transId);
18718         }
18719     },
18720
18721     // private
18722     createNode : function(attr)
18723     {
18724         // apply baseAttrs, nice idea Corey!
18725         if(this.baseAttrs){
18726             Roo.applyIf(attr, this.baseAttrs);
18727         }
18728         if(this.applyLoader !== false){
18729             attr.loader = this;
18730         }
18731         // uiProvider = depreciated..
18732         
18733         if(typeof(attr.uiProvider) == 'string'){
18734            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18735                 /**  eval:var:attr */ eval(attr.uiProvider);
18736         }
18737         if(typeof(this.uiProviders['default']) != 'undefined') {
18738             attr.uiProvider = this.uiProviders['default'];
18739         }
18740         
18741         this.fireEvent('create', this, attr);
18742         
18743         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18744         return(attr.leaf ?
18745                         new Roo.tree.TreeNode(attr) :
18746                         new Roo.tree.AsyncTreeNode(attr));
18747     },
18748
18749     processResponse : function(response, node, callback)
18750     {
18751         var json = response.responseText;
18752         try {
18753             
18754             var o = Roo.decode(json);
18755             
18756             if (this.root === false && typeof(o.success) != undefined) {
18757                 this.root = 'data'; // the default behaviour for list like data..
18758                 }
18759                 
18760             if (this.root !== false &&  !o.success) {
18761                 // it's a failure condition.
18762                 var a = response.argument;
18763                 this.fireEvent("loadexception", this, a.node, response);
18764                 Roo.log("Load failed - should have a handler really");
18765                 return;
18766             }
18767             
18768             
18769             
18770             if (this.root !== false) {
18771                  o = o[this.root];
18772             }
18773             
18774             for(var i = 0, len = o.length; i < len; i++){
18775                 var n = this.createNode(o[i]);
18776                 if(n){
18777                     node.appendChild(n);
18778                 }
18779             }
18780             if(typeof callback == "function"){
18781                 callback(this, node);
18782             }
18783         }catch(e){
18784             this.handleFailure(response);
18785         }
18786     },
18787
18788     handleResponse : function(response){
18789         this.transId = false;
18790         var a = response.argument;
18791         this.processResponse(response, a.node, a.callback);
18792         this.fireEvent("load", this, a.node, response);
18793     },
18794
18795     handleFailure : function(response)
18796     {
18797         // should handle failure better..
18798         this.transId = false;
18799         var a = response.argument;
18800         this.fireEvent("loadexception", this, a.node, response);
18801         if(typeof a.callback == "function"){
18802             a.callback(this, a.node);
18803         }
18804     }
18805 });/*
18806  * Based on:
18807  * Ext JS Library 1.1.1
18808  * Copyright(c) 2006-2007, Ext JS, LLC.
18809  *
18810  * Originally Released Under LGPL - original licence link has changed is not relivant.
18811  *
18812  * Fork - LGPL
18813  * <script type="text/javascript">
18814  */
18815
18816 /**
18817 * @class Roo.tree.TreeFilter
18818 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18819 * @param {TreePanel} tree
18820 * @param {Object} config (optional)
18821  */
18822 Roo.tree.TreeFilter = function(tree, config){
18823     this.tree = tree;
18824     this.filtered = {};
18825     Roo.apply(this, config);
18826 };
18827
18828 Roo.tree.TreeFilter.prototype = {
18829     clearBlank:false,
18830     reverse:false,
18831     autoClear:false,
18832     remove:false,
18833
18834      /**
18835      * Filter the data by a specific attribute.
18836      * @param {String/RegExp} value Either string that the attribute value
18837      * should start with or a RegExp to test against the attribute
18838      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18839      * @param {TreeNode} startNode (optional) The node to start the filter at.
18840      */
18841     filter : function(value, attr, startNode){
18842         attr = attr || "text";
18843         var f;
18844         if(typeof value == "string"){
18845             var vlen = value.length;
18846             // auto clear empty filter
18847             if(vlen == 0 && this.clearBlank){
18848                 this.clear();
18849                 return;
18850             }
18851             value = value.toLowerCase();
18852             f = function(n){
18853                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18854             };
18855         }else if(value.exec){ // regex?
18856             f = function(n){
18857                 return value.test(n.attributes[attr]);
18858             };
18859         }else{
18860             throw 'Illegal filter type, must be string or regex';
18861         }
18862         this.filterBy(f, null, startNode);
18863         },
18864
18865     /**
18866      * Filter by a function. The passed function will be called with each
18867      * node in the tree (or from the startNode). If the function returns true, the node is kept
18868      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18869      * @param {Function} fn The filter function
18870      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18871      */
18872     filterBy : function(fn, scope, startNode){
18873         startNode = startNode || this.tree.root;
18874         if(this.autoClear){
18875             this.clear();
18876         }
18877         var af = this.filtered, rv = this.reverse;
18878         var f = function(n){
18879             if(n == startNode){
18880                 return true;
18881             }
18882             if(af[n.id]){
18883                 return false;
18884             }
18885             var m = fn.call(scope || n, n);
18886             if(!m || rv){
18887                 af[n.id] = n;
18888                 n.ui.hide();
18889                 return false;
18890             }
18891             return true;
18892         };
18893         startNode.cascade(f);
18894         if(this.remove){
18895            for(var id in af){
18896                if(typeof id != "function"){
18897                    var n = af[id];
18898                    if(n && n.parentNode){
18899                        n.parentNode.removeChild(n);
18900                    }
18901                }
18902            }
18903         }
18904     },
18905
18906     /**
18907      * Clears the current filter. Note: with the "remove" option
18908      * set a filter cannot be cleared.
18909      */
18910     clear : function(){
18911         var t = this.tree;
18912         var af = this.filtered;
18913         for(var id in af){
18914             if(typeof id != "function"){
18915                 var n = af[id];
18916                 if(n){
18917                     n.ui.show();
18918                 }
18919             }
18920         }
18921         this.filtered = {};
18922     }
18923 };
18924 /*
18925  * Based on:
18926  * Ext JS Library 1.1.1
18927  * Copyright(c) 2006-2007, Ext JS, LLC.
18928  *
18929  * Originally Released Under LGPL - original licence link has changed is not relivant.
18930  *
18931  * Fork - LGPL
18932  * <script type="text/javascript">
18933  */
18934  
18935
18936 /**
18937  * @class Roo.tree.TreeSorter
18938  * Provides sorting of nodes in a TreePanel
18939  * 
18940  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18941  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18942  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18943  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18944  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18945  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18946  * @constructor
18947  * @param {TreePanel} tree
18948  * @param {Object} config
18949  */
18950 Roo.tree.TreeSorter = function(tree, config){
18951     Roo.apply(this, config);
18952     tree.on("beforechildrenrendered", this.doSort, this);
18953     tree.on("append", this.updateSort, this);
18954     tree.on("insert", this.updateSort, this);
18955     
18956     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18957     var p = this.property || "text";
18958     var sortType = this.sortType;
18959     var fs = this.folderSort;
18960     var cs = this.caseSensitive === true;
18961     var leafAttr = this.leafAttr || 'leaf';
18962
18963     this.sortFn = function(n1, n2){
18964         if(fs){
18965             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18966                 return 1;
18967             }
18968             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18969                 return -1;
18970             }
18971         }
18972         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18973         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18974         if(v1 < v2){
18975                         return dsc ? +1 : -1;
18976                 }else if(v1 > v2){
18977                         return dsc ? -1 : +1;
18978         }else{
18979                 return 0;
18980         }
18981     };
18982 };
18983
18984 Roo.tree.TreeSorter.prototype = {
18985     doSort : function(node){
18986         node.sort(this.sortFn);
18987     },
18988     
18989     compareNodes : function(n1, n2){
18990         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18991     },
18992     
18993     updateSort : function(tree, node){
18994         if(node.childrenRendered){
18995             this.doSort.defer(1, this, [node]);
18996         }
18997     }
18998 };/*
18999  * Based on:
19000  * Ext JS Library 1.1.1
19001  * Copyright(c) 2006-2007, Ext JS, LLC.
19002  *
19003  * Originally Released Under LGPL - original licence link has changed is not relivant.
19004  *
19005  * Fork - LGPL
19006  * <script type="text/javascript">
19007  */
19008
19009 if(Roo.dd.DropZone){
19010     
19011 Roo.tree.TreeDropZone = function(tree, config){
19012     this.allowParentInsert = false;
19013     this.allowContainerDrop = false;
19014     this.appendOnly = false;
19015     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19016     this.tree = tree;
19017     this.lastInsertClass = "x-tree-no-status";
19018     this.dragOverData = {};
19019 };
19020
19021 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19022     ddGroup : "TreeDD",
19023     scroll:  true,
19024     
19025     expandDelay : 1000,
19026     
19027     expandNode : function(node){
19028         if(node.hasChildNodes() && !node.isExpanded()){
19029             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19030         }
19031     },
19032     
19033     queueExpand : function(node){
19034         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19035     },
19036     
19037     cancelExpand : function(){
19038         if(this.expandProcId){
19039             clearTimeout(this.expandProcId);
19040             this.expandProcId = false;
19041         }
19042     },
19043     
19044     isValidDropPoint : function(n, pt, dd, e, data){
19045         if(!n || !data){ return false; }
19046         var targetNode = n.node;
19047         var dropNode = data.node;
19048         // default drop rules
19049         if(!(targetNode && targetNode.isTarget && pt)){
19050             return false;
19051         }
19052         if(pt == "append" && targetNode.allowChildren === false){
19053             return false;
19054         }
19055         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19056             return false;
19057         }
19058         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19059             return false;
19060         }
19061         // reuse the object
19062         var overEvent = this.dragOverData;
19063         overEvent.tree = this.tree;
19064         overEvent.target = targetNode;
19065         overEvent.data = data;
19066         overEvent.point = pt;
19067         overEvent.source = dd;
19068         overEvent.rawEvent = e;
19069         overEvent.dropNode = dropNode;
19070         overEvent.cancel = false;  
19071         var result = this.tree.fireEvent("nodedragover", overEvent);
19072         return overEvent.cancel === false && result !== false;
19073     },
19074     
19075     getDropPoint : function(e, n, dd)
19076     {
19077         var tn = n.node;
19078         if(tn.isRoot){
19079             return tn.allowChildren !== false ? "append" : false; // always append for root
19080         }
19081         var dragEl = n.ddel;
19082         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19083         var y = Roo.lib.Event.getPageY(e);
19084         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19085         
19086         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19087         var noAppend = tn.allowChildren === false;
19088         if(this.appendOnly || tn.parentNode.allowChildren === false){
19089             return noAppend ? false : "append";
19090         }
19091         var noBelow = false;
19092         if(!this.allowParentInsert){
19093             noBelow = tn.hasChildNodes() && tn.isExpanded();
19094         }
19095         var q = (b - t) / (noAppend ? 2 : 3);
19096         if(y >= t && y < (t + q)){
19097             return "above";
19098         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19099             return "below";
19100         }else{
19101             return "append";
19102         }
19103     },
19104     
19105     onNodeEnter : function(n, dd, e, data)
19106     {
19107         this.cancelExpand();
19108     },
19109     
19110     onNodeOver : function(n, dd, e, data)
19111     {
19112        
19113         var pt = this.getDropPoint(e, n, dd);
19114         var node = n.node;
19115         
19116         // auto node expand check
19117         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19118             this.queueExpand(node);
19119         }else if(pt != "append"){
19120             this.cancelExpand();
19121         }
19122         
19123         // set the insert point style on the target node
19124         var returnCls = this.dropNotAllowed;
19125         if(this.isValidDropPoint(n, pt, dd, e, data)){
19126            if(pt){
19127                var el = n.ddel;
19128                var cls;
19129                if(pt == "above"){
19130                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19131                    cls = "x-tree-drag-insert-above";
19132                }else if(pt == "below"){
19133                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19134                    cls = "x-tree-drag-insert-below";
19135                }else{
19136                    returnCls = "x-tree-drop-ok-append";
19137                    cls = "x-tree-drag-append";
19138                }
19139                if(this.lastInsertClass != cls){
19140                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19141                    this.lastInsertClass = cls;
19142                }
19143            }
19144        }
19145        return returnCls;
19146     },
19147     
19148     onNodeOut : function(n, dd, e, data){
19149         
19150         this.cancelExpand();
19151         this.removeDropIndicators(n);
19152     },
19153     
19154     onNodeDrop : function(n, dd, e, data){
19155         var point = this.getDropPoint(e, n, dd);
19156         var targetNode = n.node;
19157         targetNode.ui.startDrop();
19158         if(!this.isValidDropPoint(n, point, dd, e, data)){
19159             targetNode.ui.endDrop();
19160             return false;
19161         }
19162         // first try to find the drop node
19163         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19164         var dropEvent = {
19165             tree : this.tree,
19166             target: targetNode,
19167             data: data,
19168             point: point,
19169             source: dd,
19170             rawEvent: e,
19171             dropNode: dropNode,
19172             cancel: !dropNode   
19173         };
19174         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19175         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19176             targetNode.ui.endDrop();
19177             return false;
19178         }
19179         // allow target changing
19180         targetNode = dropEvent.target;
19181         if(point == "append" && !targetNode.isExpanded()){
19182             targetNode.expand(false, null, function(){
19183                 this.completeDrop(dropEvent);
19184             }.createDelegate(this));
19185         }else{
19186             this.completeDrop(dropEvent);
19187         }
19188         return true;
19189     },
19190     
19191     completeDrop : function(de){
19192         var ns = de.dropNode, p = de.point, t = de.target;
19193         if(!(ns instanceof Array)){
19194             ns = [ns];
19195         }
19196         var n;
19197         for(var i = 0, len = ns.length; i < len; i++){
19198             n = ns[i];
19199             if(p == "above"){
19200                 t.parentNode.insertBefore(n, t);
19201             }else if(p == "below"){
19202                 t.parentNode.insertBefore(n, t.nextSibling);
19203             }else{
19204                 t.appendChild(n);
19205             }
19206         }
19207         n.ui.focus();
19208         if(this.tree.hlDrop){
19209             n.ui.highlight();
19210         }
19211         t.ui.endDrop();
19212         this.tree.fireEvent("nodedrop", de);
19213     },
19214     
19215     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19216         if(this.tree.hlDrop){
19217             dropNode.ui.focus();
19218             dropNode.ui.highlight();
19219         }
19220         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19221     },
19222     
19223     getTree : function(){
19224         return this.tree;
19225     },
19226     
19227     removeDropIndicators : function(n){
19228         if(n && n.ddel){
19229             var el = n.ddel;
19230             Roo.fly(el).removeClass([
19231                     "x-tree-drag-insert-above",
19232                     "x-tree-drag-insert-below",
19233                     "x-tree-drag-append"]);
19234             this.lastInsertClass = "_noclass";
19235         }
19236     },
19237     
19238     beforeDragDrop : function(target, e, id){
19239         this.cancelExpand();
19240         return true;
19241     },
19242     
19243     afterRepair : function(data){
19244         if(data && Roo.enableFx){
19245             data.node.ui.highlight();
19246         }
19247         this.hideProxy();
19248     } 
19249     
19250 });
19251
19252 }
19253 /*
19254  * Based on:
19255  * Ext JS Library 1.1.1
19256  * Copyright(c) 2006-2007, Ext JS, LLC.
19257  *
19258  * Originally Released Under LGPL - original licence link has changed is not relivant.
19259  *
19260  * Fork - LGPL
19261  * <script type="text/javascript">
19262  */
19263  
19264
19265 if(Roo.dd.DragZone){
19266 Roo.tree.TreeDragZone = function(tree, config){
19267     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19268     this.tree = tree;
19269 };
19270
19271 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19272     ddGroup : "TreeDD",
19273    
19274     onBeforeDrag : function(data, e){
19275         var n = data.node;
19276         return n && n.draggable && !n.disabled;
19277     },
19278      
19279     
19280     onInitDrag : function(e){
19281         var data = this.dragData;
19282         this.tree.getSelectionModel().select(data.node);
19283         this.proxy.update("");
19284         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19285         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19286     },
19287     
19288     getRepairXY : function(e, data){
19289         return data.node.ui.getDDRepairXY();
19290     },
19291     
19292     onEndDrag : function(data, e){
19293         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19294         
19295         
19296     },
19297     
19298     onValidDrop : function(dd, e, id){
19299         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19300         this.hideProxy();
19301     },
19302     
19303     beforeInvalidDrop : function(e, id){
19304         // this scrolls the original position back into view
19305         var sm = this.tree.getSelectionModel();
19306         sm.clearSelections();
19307         sm.select(this.dragData.node);
19308     }
19309 });
19310 }/*
19311  * Based on:
19312  * Ext JS Library 1.1.1
19313  * Copyright(c) 2006-2007, Ext JS, LLC.
19314  *
19315  * Originally Released Under LGPL - original licence link has changed is not relivant.
19316  *
19317  * Fork - LGPL
19318  * <script type="text/javascript">
19319  */
19320 /**
19321  * @class Roo.tree.TreeEditor
19322  * @extends Roo.Editor
19323  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19324  * as the editor field.
19325  * @constructor
19326  * @param {Object} config (used to be the tree panel.)
19327  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19328  * 
19329  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19330  * @cfg {Roo.form.TextField|Object} field The field configuration
19331  *
19332  * 
19333  */
19334 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19335     var tree = config;
19336     var field;
19337     if (oldconfig) { // old style..
19338         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19339     } else {
19340         // new style..
19341         tree = config.tree;
19342         config.field = config.field  || {};
19343         config.field.xtype = 'TextField';
19344         field = Roo.factory(config.field, Roo.form);
19345     }
19346     config = config || {};
19347     
19348     
19349     this.addEvents({
19350         /**
19351          * @event beforenodeedit
19352          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19353          * false from the handler of this event.
19354          * @param {Editor} this
19355          * @param {Roo.tree.Node} node 
19356          */
19357         "beforenodeedit" : true
19358     });
19359     
19360     //Roo.log(config);
19361     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19362
19363     this.tree = tree;
19364
19365     tree.on('beforeclick', this.beforeNodeClick, this);
19366     tree.getTreeEl().on('mousedown', this.hide, this);
19367     this.on('complete', this.updateNode, this);
19368     this.on('beforestartedit', this.fitToTree, this);
19369     this.on('startedit', this.bindScroll, this, {delay:10});
19370     this.on('specialkey', this.onSpecialKey, this);
19371 };
19372
19373 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19374     /**
19375      * @cfg {String} alignment
19376      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19377      */
19378     alignment: "l-l",
19379     // inherit
19380     autoSize: false,
19381     /**
19382      * @cfg {Boolean} hideEl
19383      * True to hide the bound element while the editor is displayed (defaults to false)
19384      */
19385     hideEl : false,
19386     /**
19387      * @cfg {String} cls
19388      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19389      */
19390     cls: "x-small-editor x-tree-editor",
19391     /**
19392      * @cfg {Boolean} shim
19393      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19394      */
19395     shim:false,
19396     // inherit
19397     shadow:"frame",
19398     /**
19399      * @cfg {Number} maxWidth
19400      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19401      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19402      * scroll and client offsets into account prior to each edit.
19403      */
19404     maxWidth: 250,
19405
19406     editDelay : 350,
19407
19408     // private
19409     fitToTree : function(ed, el){
19410         var td = this.tree.getTreeEl().dom, nd = el.dom;
19411         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19412             td.scrollLeft = nd.offsetLeft;
19413         }
19414         var w = Math.min(
19415                 this.maxWidth,
19416                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19417         this.setSize(w, '');
19418         
19419         return this.fireEvent('beforenodeedit', this, this.editNode);
19420         
19421     },
19422
19423     // private
19424     triggerEdit : function(node){
19425         this.completeEdit();
19426         this.editNode = node;
19427         this.startEdit(node.ui.textNode, node.text);
19428     },
19429
19430     // private
19431     bindScroll : function(){
19432         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19433     },
19434
19435     // private
19436     beforeNodeClick : function(node, e){
19437         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19438         this.lastClick = new Date();
19439         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19440             e.stopEvent();
19441             this.triggerEdit(node);
19442             return false;
19443         }
19444         return true;
19445     },
19446
19447     // private
19448     updateNode : function(ed, value){
19449         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19450         this.editNode.setText(value);
19451     },
19452
19453     // private
19454     onHide : function(){
19455         Roo.tree.TreeEditor.superclass.onHide.call(this);
19456         if(this.editNode){
19457             this.editNode.ui.focus();
19458         }
19459     },
19460
19461     // private
19462     onSpecialKey : function(field, e){
19463         var k = e.getKey();
19464         if(k == e.ESC){
19465             e.stopEvent();
19466             this.cancelEdit();
19467         }else if(k == e.ENTER && !e.hasModifier()){
19468             e.stopEvent();
19469             this.completeEdit();
19470         }
19471     }
19472 });//<Script type="text/javascript">
19473 /*
19474  * Based on:
19475  * Ext JS Library 1.1.1
19476  * Copyright(c) 2006-2007, Ext JS, LLC.
19477  *
19478  * Originally Released Under LGPL - original licence link has changed is not relivant.
19479  *
19480  * Fork - LGPL
19481  * <script type="text/javascript">
19482  */
19483  
19484 /**
19485  * Not documented??? - probably should be...
19486  */
19487
19488 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19489     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19490     
19491     renderElements : function(n, a, targetNode, bulkRender){
19492         //consel.log("renderElements?");
19493         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19494
19495         var t = n.getOwnerTree();
19496         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19497         
19498         var cols = t.columns;
19499         var bw = t.borderWidth;
19500         var c = cols[0];
19501         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19502          var cb = typeof a.checked == "boolean";
19503         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19504         var colcls = 'x-t-' + tid + '-c0';
19505         var buf = [
19506             '<li class="x-tree-node">',
19507             
19508                 
19509                 '<div class="x-tree-node-el ', a.cls,'">',
19510                     // extran...
19511                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19512                 
19513                 
19514                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19515                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19516                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19517                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19518                            (a.iconCls ? ' '+a.iconCls : ''),
19519                            '" unselectable="on" />',
19520                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19521                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19522                              
19523                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19524                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19525                             '<span unselectable="on" qtip="' + tx + '">',
19526                              tx,
19527                              '</span></a>' ,
19528                     '</div>',
19529                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19530                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19531                  ];
19532         for(var i = 1, len = cols.length; i < len; i++){
19533             c = cols[i];
19534             colcls = 'x-t-' + tid + '-c' +i;
19535             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19536             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19537                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19538                       "</div>");
19539          }
19540          
19541          buf.push(
19542             '</a>',
19543             '<div class="x-clear"></div></div>',
19544             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19545             "</li>");
19546         
19547         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19548             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19549                                 n.nextSibling.ui.getEl(), buf.join(""));
19550         }else{
19551             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19552         }
19553         var el = this.wrap.firstChild;
19554         this.elRow = el;
19555         this.elNode = el.firstChild;
19556         this.ranchor = el.childNodes[1];
19557         this.ctNode = this.wrap.childNodes[1];
19558         var cs = el.firstChild.childNodes;
19559         this.indentNode = cs[0];
19560         this.ecNode = cs[1];
19561         this.iconNode = cs[2];
19562         var index = 3;
19563         if(cb){
19564             this.checkbox = cs[3];
19565             index++;
19566         }
19567         this.anchor = cs[index];
19568         
19569         this.textNode = cs[index].firstChild;
19570         
19571         //el.on("click", this.onClick, this);
19572         //el.on("dblclick", this.onDblClick, this);
19573         
19574         
19575        // console.log(this);
19576     },
19577     initEvents : function(){
19578         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19579         
19580             
19581         var a = this.ranchor;
19582
19583         var el = Roo.get(a);
19584
19585         if(Roo.isOpera){ // opera render bug ignores the CSS
19586             el.setStyle("text-decoration", "none");
19587         }
19588
19589         el.on("click", this.onClick, this);
19590         el.on("dblclick", this.onDblClick, this);
19591         el.on("contextmenu", this.onContextMenu, this);
19592         
19593     },
19594     
19595     /*onSelectedChange : function(state){
19596         if(state){
19597             this.focus();
19598             this.addClass("x-tree-selected");
19599         }else{
19600             //this.blur();
19601             this.removeClass("x-tree-selected");
19602         }
19603     },*/
19604     addClass : function(cls){
19605         if(this.elRow){
19606             Roo.fly(this.elRow).addClass(cls);
19607         }
19608         
19609     },
19610     
19611     
19612     removeClass : function(cls){
19613         if(this.elRow){
19614             Roo.fly(this.elRow).removeClass(cls);
19615         }
19616     }
19617
19618     
19619     
19620 });//<Script type="text/javascript">
19621
19622 /*
19623  * Based on:
19624  * Ext JS Library 1.1.1
19625  * Copyright(c) 2006-2007, Ext JS, LLC.
19626  *
19627  * Originally Released Under LGPL - original licence link has changed is not relivant.
19628  *
19629  * Fork - LGPL
19630  * <script type="text/javascript">
19631  */
19632  
19633
19634 /**
19635  * @class Roo.tree.ColumnTree
19636  * @extends Roo.data.TreePanel
19637  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19638  * @cfg {int} borderWidth  compined right/left border allowance
19639  * @constructor
19640  * @param {String/HTMLElement/Element} el The container element
19641  * @param {Object} config
19642  */
19643 Roo.tree.ColumnTree =  function(el, config)
19644 {
19645    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19646    this.addEvents({
19647         /**
19648         * @event resize
19649         * Fire this event on a container when it resizes
19650         * @param {int} w Width
19651         * @param {int} h Height
19652         */
19653        "resize" : true
19654     });
19655     this.on('resize', this.onResize, this);
19656 };
19657
19658 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19659     //lines:false,
19660     
19661     
19662     borderWidth: Roo.isBorderBox ? 0 : 2, 
19663     headEls : false,
19664     
19665     render : function(){
19666         // add the header.....
19667        
19668         Roo.tree.ColumnTree.superclass.render.apply(this);
19669         
19670         this.el.addClass('x-column-tree');
19671         
19672         this.headers = this.el.createChild(
19673             {cls:'x-tree-headers'},this.innerCt.dom);
19674    
19675         var cols = this.columns, c;
19676         var totalWidth = 0;
19677         this.headEls = [];
19678         var  len = cols.length;
19679         for(var i = 0; i < len; i++){
19680              c = cols[i];
19681              totalWidth += c.width;
19682             this.headEls.push(this.headers.createChild({
19683                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19684                  cn: {
19685                      cls:'x-tree-hd-text',
19686                      html: c.header
19687                  },
19688                  style:'width:'+(c.width-this.borderWidth)+'px;'
19689              }));
19690         }
19691         this.headers.createChild({cls:'x-clear'});
19692         // prevent floats from wrapping when clipped
19693         this.headers.setWidth(totalWidth);
19694         //this.innerCt.setWidth(totalWidth);
19695         this.innerCt.setStyle({ overflow: 'auto' });
19696         this.onResize(this.width, this.height);
19697              
19698         
19699     },
19700     onResize : function(w,h)
19701     {
19702         this.height = h;
19703         this.width = w;
19704         // resize cols..
19705         this.innerCt.setWidth(this.width);
19706         this.innerCt.setHeight(this.height-20);
19707         
19708         // headers...
19709         var cols = this.columns, c;
19710         var totalWidth = 0;
19711         var expEl = false;
19712         var len = cols.length;
19713         for(var i = 0; i < len; i++){
19714             c = cols[i];
19715             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19716                 // it's the expander..
19717                 expEl  = this.headEls[i];
19718                 continue;
19719             }
19720             totalWidth += c.width;
19721             
19722         }
19723         if (expEl) {
19724             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19725         }
19726         this.headers.setWidth(w-20);
19727
19728         
19729         
19730         
19731     }
19732 });
19733 /*
19734  * Based on:
19735  * Ext JS Library 1.1.1
19736  * Copyright(c) 2006-2007, Ext JS, LLC.
19737  *
19738  * Originally Released Under LGPL - original licence link has changed is not relivant.
19739  *
19740  * Fork - LGPL
19741  * <script type="text/javascript">
19742  */
19743  
19744 /**
19745  * @class Roo.menu.Menu
19746  * @extends Roo.util.Observable
19747  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19748  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19749  * @constructor
19750  * Creates a new Menu
19751  * @param {Object} config Configuration options
19752  */
19753 Roo.menu.Menu = function(config){
19754     Roo.apply(this, config);
19755     this.id = this.id || Roo.id();
19756     this.addEvents({
19757         /**
19758          * @event beforeshow
19759          * Fires before this menu is displayed
19760          * @param {Roo.menu.Menu} this
19761          */
19762         beforeshow : true,
19763         /**
19764          * @event beforehide
19765          * Fires before this menu is hidden
19766          * @param {Roo.menu.Menu} this
19767          */
19768         beforehide : true,
19769         /**
19770          * @event show
19771          * Fires after this menu is displayed
19772          * @param {Roo.menu.Menu} this
19773          */
19774         show : true,
19775         /**
19776          * @event hide
19777          * Fires after this menu is hidden
19778          * @param {Roo.menu.Menu} this
19779          */
19780         hide : true,
19781         /**
19782          * @event click
19783          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19784          * @param {Roo.menu.Menu} this
19785          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19786          * @param {Roo.EventObject} e
19787          */
19788         click : true,
19789         /**
19790          * @event mouseover
19791          * Fires when the mouse is hovering over this menu
19792          * @param {Roo.menu.Menu} this
19793          * @param {Roo.EventObject} e
19794          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19795          */
19796         mouseover : true,
19797         /**
19798          * @event mouseout
19799          * Fires when the mouse exits this menu
19800          * @param {Roo.menu.Menu} this
19801          * @param {Roo.EventObject} e
19802          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19803          */
19804         mouseout : true,
19805         /**
19806          * @event itemclick
19807          * Fires when a menu item contained in this menu is clicked
19808          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19809          * @param {Roo.EventObject} e
19810          */
19811         itemclick: true
19812     });
19813     if (this.registerMenu) {
19814         Roo.menu.MenuMgr.register(this);
19815     }
19816     
19817     var mis = this.items;
19818     this.items = new Roo.util.MixedCollection();
19819     if(mis){
19820         this.add.apply(this, mis);
19821     }
19822 };
19823
19824 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19825     /**
19826      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19827      */
19828     minWidth : 120,
19829     /**
19830      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19831      * for bottom-right shadow (defaults to "sides")
19832      */
19833     shadow : "sides",
19834     /**
19835      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19836      * this menu (defaults to "tl-tr?")
19837      */
19838     subMenuAlign : "tl-tr?",
19839     /**
19840      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19841      * relative to its element of origin (defaults to "tl-bl?")
19842      */
19843     defaultAlign : "tl-bl?",
19844     /**
19845      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19846      */
19847     allowOtherMenus : false,
19848     /**
19849      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19850      */
19851     registerMenu : true,
19852
19853     hidden:true,
19854
19855     // private
19856     render : function(){
19857         if(this.el){
19858             return;
19859         }
19860         var el = this.el = new Roo.Layer({
19861             cls: "x-menu",
19862             shadow:this.shadow,
19863             constrain: false,
19864             parentEl: this.parentEl || document.body,
19865             zindex:15000
19866         });
19867
19868         this.keyNav = new Roo.menu.MenuNav(this);
19869
19870         if(this.plain){
19871             el.addClass("x-menu-plain");
19872         }
19873         if(this.cls){
19874             el.addClass(this.cls);
19875         }
19876         // generic focus element
19877         this.focusEl = el.createChild({
19878             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19879         });
19880         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19881         ul.on("click", this.onClick, this);
19882         ul.on("mouseover", this.onMouseOver, this);
19883         ul.on("mouseout", this.onMouseOut, this);
19884         this.items.each(function(item){
19885             if (item.hidden) {
19886                 return;
19887             }
19888             
19889             var li = document.createElement("li");
19890             li.className = "x-menu-list-item";
19891             ul.dom.appendChild(li);
19892             item.render(li, this);
19893         }, this);
19894         this.ul = ul;
19895         this.autoWidth();
19896     },
19897
19898     // private
19899     autoWidth : function(){
19900         var el = this.el, ul = this.ul;
19901         if(!el){
19902             return;
19903         }
19904         var w = this.width;
19905         if(w){
19906             el.setWidth(w);
19907         }else if(Roo.isIE){
19908             el.setWidth(this.minWidth);
19909             var t = el.dom.offsetWidth; // force recalc
19910             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19911         }
19912     },
19913
19914     // private
19915     delayAutoWidth : function(){
19916         if(this.rendered){
19917             if(!this.awTask){
19918                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19919             }
19920             this.awTask.delay(20);
19921         }
19922     },
19923
19924     // private
19925     findTargetItem : function(e){
19926         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19927         if(t && t.menuItemId){
19928             return this.items.get(t.menuItemId);
19929         }
19930     },
19931
19932     // private
19933     onClick : function(e){
19934         var t;
19935         if(t = this.findTargetItem(e)){
19936             t.onClick(e);
19937             this.fireEvent("click", this, t, e);
19938         }
19939     },
19940
19941     // private
19942     setActiveItem : function(item, autoExpand){
19943         if(item != this.activeItem){
19944             if(this.activeItem){
19945                 this.activeItem.deactivate();
19946             }
19947             this.activeItem = item;
19948             item.activate(autoExpand);
19949         }else if(autoExpand){
19950             item.expandMenu();
19951         }
19952     },
19953
19954     // private
19955     tryActivate : function(start, step){
19956         var items = this.items;
19957         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19958             var item = items.get(i);
19959             if(!item.disabled && item.canActivate){
19960                 this.setActiveItem(item, false);
19961                 return item;
19962             }
19963         }
19964         return false;
19965     },
19966
19967     // private
19968     onMouseOver : function(e){
19969         var t;
19970         if(t = this.findTargetItem(e)){
19971             if(t.canActivate && !t.disabled){
19972                 this.setActiveItem(t, true);
19973             }
19974         }
19975         this.fireEvent("mouseover", this, e, t);
19976     },
19977
19978     // private
19979     onMouseOut : function(e){
19980         var t;
19981         if(t = this.findTargetItem(e)){
19982             if(t == this.activeItem && t.shouldDeactivate(e)){
19983                 this.activeItem.deactivate();
19984                 delete this.activeItem;
19985             }
19986         }
19987         this.fireEvent("mouseout", this, e, t);
19988     },
19989
19990     /**
19991      * Read-only.  Returns true if the menu is currently displayed, else false.
19992      * @type Boolean
19993      */
19994     isVisible : function(){
19995         return this.el && !this.hidden;
19996     },
19997
19998     /**
19999      * Displays this menu relative to another element
20000      * @param {String/HTMLElement/Roo.Element} element The element to align to
20001      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20002      * the element (defaults to this.defaultAlign)
20003      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20004      */
20005     show : function(el, pos, parentMenu){
20006         this.parentMenu = parentMenu;
20007         if(!this.el){
20008             this.render();
20009         }
20010         this.fireEvent("beforeshow", this);
20011         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20012     },
20013
20014     /**
20015      * Displays this menu at a specific xy position
20016      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20017      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20018      */
20019     showAt : function(xy, parentMenu, /* private: */_e){
20020         this.parentMenu = parentMenu;
20021         if(!this.el){
20022             this.render();
20023         }
20024         if(_e !== false){
20025             this.fireEvent("beforeshow", this);
20026             xy = this.el.adjustForConstraints(xy);
20027         }
20028         this.el.setXY(xy);
20029         this.el.show();
20030         this.hidden = false;
20031         this.focus();
20032         this.fireEvent("show", this);
20033     },
20034
20035     focus : function(){
20036         if(!this.hidden){
20037             this.doFocus.defer(50, this);
20038         }
20039     },
20040
20041     doFocus : function(){
20042         if(!this.hidden){
20043             this.focusEl.focus();
20044         }
20045     },
20046
20047     /**
20048      * Hides this menu and optionally all parent menus
20049      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20050      */
20051     hide : function(deep){
20052         if(this.el && this.isVisible()){
20053             this.fireEvent("beforehide", this);
20054             if(this.activeItem){
20055                 this.activeItem.deactivate();
20056                 this.activeItem = null;
20057             }
20058             this.el.hide();
20059             this.hidden = true;
20060             this.fireEvent("hide", this);
20061         }
20062         if(deep === true && this.parentMenu){
20063             this.parentMenu.hide(true);
20064         }
20065     },
20066
20067     /**
20068      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20069      * Any of the following are valid:
20070      * <ul>
20071      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20072      * <li>An HTMLElement object which will be converted to a menu item</li>
20073      * <li>A menu item config object that will be created as a new menu item</li>
20074      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20075      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20076      * </ul>
20077      * Usage:
20078      * <pre><code>
20079 // Create the menu
20080 var menu = new Roo.menu.Menu();
20081
20082 // Create a menu item to add by reference
20083 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20084
20085 // Add a bunch of items at once using different methods.
20086 // Only the last item added will be returned.
20087 var item = menu.add(
20088     menuItem,                // add existing item by ref
20089     'Dynamic Item',          // new TextItem
20090     '-',                     // new separator
20091     { text: 'Config Item' }  // new item by config
20092 );
20093 </code></pre>
20094      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20095      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20096      */
20097     add : function(){
20098         var a = arguments, l = a.length, item;
20099         for(var i = 0; i < l; i++){
20100             var el = a[i];
20101             if ((typeof(el) == "object") && el.xtype && el.xns) {
20102                 el = Roo.factory(el, Roo.menu);
20103             }
20104             
20105             if(el.render){ // some kind of Item
20106                 item = this.addItem(el);
20107             }else if(typeof el == "string"){ // string
20108                 if(el == "separator" || el == "-"){
20109                     item = this.addSeparator();
20110                 }else{
20111                     item = this.addText(el);
20112                 }
20113             }else if(el.tagName || el.el){ // element
20114                 item = this.addElement(el);
20115             }else if(typeof el == "object"){ // must be menu item config?
20116                 item = this.addMenuItem(el);
20117             }
20118         }
20119         return item;
20120     },
20121
20122     /**
20123      * Returns this menu's underlying {@link Roo.Element} object
20124      * @return {Roo.Element} The element
20125      */
20126     getEl : function(){
20127         if(!this.el){
20128             this.render();
20129         }
20130         return this.el;
20131     },
20132
20133     /**
20134      * Adds a separator bar to the menu
20135      * @return {Roo.menu.Item} The menu item that was added
20136      */
20137     addSeparator : function(){
20138         return this.addItem(new Roo.menu.Separator());
20139     },
20140
20141     /**
20142      * Adds an {@link Roo.Element} object to the menu
20143      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20144      * @return {Roo.menu.Item} The menu item that was added
20145      */
20146     addElement : function(el){
20147         return this.addItem(new Roo.menu.BaseItem(el));
20148     },
20149
20150     /**
20151      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20152      * @param {Roo.menu.Item} item The menu item to add
20153      * @return {Roo.menu.Item} The menu item that was added
20154      */
20155     addItem : function(item){
20156         this.items.add(item);
20157         if(this.ul){
20158             var li = document.createElement("li");
20159             li.className = "x-menu-list-item";
20160             this.ul.dom.appendChild(li);
20161             item.render(li, this);
20162             this.delayAutoWidth();
20163         }
20164         return item;
20165     },
20166
20167     /**
20168      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20169      * @param {Object} config A MenuItem config object
20170      * @return {Roo.menu.Item} The menu item that was added
20171      */
20172     addMenuItem : function(config){
20173         if(!(config instanceof Roo.menu.Item)){
20174             if(typeof config.checked == "boolean"){ // must be check menu item config?
20175                 config = new Roo.menu.CheckItem(config);
20176             }else{
20177                 config = new Roo.menu.Item(config);
20178             }
20179         }
20180         return this.addItem(config);
20181     },
20182
20183     /**
20184      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20185      * @param {String} text The text to display in the menu item
20186      * @return {Roo.menu.Item} The menu item that was added
20187      */
20188     addText : function(text){
20189         return this.addItem(new Roo.menu.TextItem({ text : text }));
20190     },
20191
20192     /**
20193      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20194      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20195      * @param {Roo.menu.Item} item The menu item to add
20196      * @return {Roo.menu.Item} The menu item that was added
20197      */
20198     insert : function(index, item){
20199         this.items.insert(index, item);
20200         if(this.ul){
20201             var li = document.createElement("li");
20202             li.className = "x-menu-list-item";
20203             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20204             item.render(li, this);
20205             this.delayAutoWidth();
20206         }
20207         return item;
20208     },
20209
20210     /**
20211      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20212      * @param {Roo.menu.Item} item The menu item to remove
20213      */
20214     remove : function(item){
20215         this.items.removeKey(item.id);
20216         item.destroy();
20217     },
20218
20219     /**
20220      * Removes and destroys all items in the menu
20221      */
20222     removeAll : function(){
20223         var f;
20224         while(f = this.items.first()){
20225             this.remove(f);
20226         }
20227     }
20228 });
20229
20230 // MenuNav is a private utility class used internally by the Menu
20231 Roo.menu.MenuNav = function(menu){
20232     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20233     this.scope = this.menu = menu;
20234 };
20235
20236 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20237     doRelay : function(e, h){
20238         var k = e.getKey();
20239         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20240             this.menu.tryActivate(0, 1);
20241             return false;
20242         }
20243         return h.call(this.scope || this, e, this.menu);
20244     },
20245
20246     up : function(e, m){
20247         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20248             m.tryActivate(m.items.length-1, -1);
20249         }
20250     },
20251
20252     down : function(e, m){
20253         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20254             m.tryActivate(0, 1);
20255         }
20256     },
20257
20258     right : function(e, m){
20259         if(m.activeItem){
20260             m.activeItem.expandMenu(true);
20261         }
20262     },
20263
20264     left : function(e, m){
20265         m.hide();
20266         if(m.parentMenu && m.parentMenu.activeItem){
20267             m.parentMenu.activeItem.activate();
20268         }
20269     },
20270
20271     enter : function(e, m){
20272         if(m.activeItem){
20273             e.stopPropagation();
20274             m.activeItem.onClick(e);
20275             m.fireEvent("click", this, m.activeItem);
20276             return true;
20277         }
20278     }
20279 });/*
20280  * Based on:
20281  * Ext JS Library 1.1.1
20282  * Copyright(c) 2006-2007, Ext JS, LLC.
20283  *
20284  * Originally Released Under LGPL - original licence link has changed is not relivant.
20285  *
20286  * Fork - LGPL
20287  * <script type="text/javascript">
20288  */
20289  
20290 /**
20291  * @class Roo.menu.MenuMgr
20292  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20293  * @singleton
20294  */
20295 Roo.menu.MenuMgr = function(){
20296    var menus, active, groups = {}, attached = false, lastShow = new Date();
20297
20298    // private - called when first menu is created
20299    function init(){
20300        menus = {};
20301        active = new Roo.util.MixedCollection();
20302        Roo.get(document).addKeyListener(27, function(){
20303            if(active.length > 0){
20304                hideAll();
20305            }
20306        });
20307    }
20308
20309    // private
20310    function hideAll(){
20311        if(active && active.length > 0){
20312            var c = active.clone();
20313            c.each(function(m){
20314                m.hide();
20315            });
20316        }
20317    }
20318
20319    // private
20320    function onHide(m){
20321        active.remove(m);
20322        if(active.length < 1){
20323            Roo.get(document).un("mousedown", onMouseDown);
20324            attached = false;
20325        }
20326    }
20327
20328    // private
20329    function onShow(m){
20330        var last = active.last();
20331        lastShow = new Date();
20332        active.add(m);
20333        if(!attached){
20334            Roo.get(document).on("mousedown", onMouseDown);
20335            attached = true;
20336        }
20337        if(m.parentMenu){
20338           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20339           m.parentMenu.activeChild = m;
20340        }else if(last && last.isVisible()){
20341           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20342        }
20343    }
20344
20345    // private
20346    function onBeforeHide(m){
20347        if(m.activeChild){
20348            m.activeChild.hide();
20349        }
20350        if(m.autoHideTimer){
20351            clearTimeout(m.autoHideTimer);
20352            delete m.autoHideTimer;
20353        }
20354    }
20355
20356    // private
20357    function onBeforeShow(m){
20358        var pm = m.parentMenu;
20359        if(!pm && !m.allowOtherMenus){
20360            hideAll();
20361        }else if(pm && pm.activeChild && active != m){
20362            pm.activeChild.hide();
20363        }
20364    }
20365
20366    // private
20367    function onMouseDown(e){
20368        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20369            hideAll();
20370        }
20371    }
20372
20373    // private
20374    function onBeforeCheck(mi, state){
20375        if(state){
20376            var g = groups[mi.group];
20377            for(var i = 0, l = g.length; i < l; i++){
20378                if(g[i] != mi){
20379                    g[i].setChecked(false);
20380                }
20381            }
20382        }
20383    }
20384
20385    return {
20386
20387        /**
20388         * Hides all menus that are currently visible
20389         */
20390        hideAll : function(){
20391             hideAll();  
20392        },
20393
20394        // private
20395        register : function(menu){
20396            if(!menus){
20397                init();
20398            }
20399            menus[menu.id] = menu;
20400            menu.on("beforehide", onBeforeHide);
20401            menu.on("hide", onHide);
20402            menu.on("beforeshow", onBeforeShow);
20403            menu.on("show", onShow);
20404            var g = menu.group;
20405            if(g && menu.events["checkchange"]){
20406                if(!groups[g]){
20407                    groups[g] = [];
20408                }
20409                groups[g].push(menu);
20410                menu.on("checkchange", onCheck);
20411            }
20412        },
20413
20414         /**
20415          * Returns a {@link Roo.menu.Menu} object
20416          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20417          * be used to generate and return a new Menu instance.
20418          */
20419        get : function(menu){
20420            if(typeof menu == "string"){ // menu id
20421                return menus[menu];
20422            }else if(menu.events){  // menu instance
20423                return menu;
20424            }else if(typeof menu.length == 'number'){ // array of menu items?
20425                return new Roo.menu.Menu({items:menu});
20426            }else{ // otherwise, must be a config
20427                return new Roo.menu.Menu(menu);
20428            }
20429        },
20430
20431        // private
20432        unregister : function(menu){
20433            delete menus[menu.id];
20434            menu.un("beforehide", onBeforeHide);
20435            menu.un("hide", onHide);
20436            menu.un("beforeshow", onBeforeShow);
20437            menu.un("show", onShow);
20438            var g = menu.group;
20439            if(g && menu.events["checkchange"]){
20440                groups[g].remove(menu);
20441                menu.un("checkchange", onCheck);
20442            }
20443        },
20444
20445        // private
20446        registerCheckable : function(menuItem){
20447            var g = menuItem.group;
20448            if(g){
20449                if(!groups[g]){
20450                    groups[g] = [];
20451                }
20452                groups[g].push(menuItem);
20453                menuItem.on("beforecheckchange", onBeforeCheck);
20454            }
20455        },
20456
20457        // private
20458        unregisterCheckable : function(menuItem){
20459            var g = menuItem.group;
20460            if(g){
20461                groups[g].remove(menuItem);
20462                menuItem.un("beforecheckchange", onBeforeCheck);
20463            }
20464        }
20465    };
20466 }();/*
20467  * Based on:
20468  * Ext JS Library 1.1.1
20469  * Copyright(c) 2006-2007, Ext JS, LLC.
20470  *
20471  * Originally Released Under LGPL - original licence link has changed is not relivant.
20472  *
20473  * Fork - LGPL
20474  * <script type="text/javascript">
20475  */
20476  
20477
20478 /**
20479  * @class Roo.menu.BaseItem
20480  * @extends Roo.Component
20481  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20482  * management and base configuration options shared by all menu components.
20483  * @constructor
20484  * Creates a new BaseItem
20485  * @param {Object} config Configuration options
20486  */
20487 Roo.menu.BaseItem = function(config){
20488     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20489
20490     this.addEvents({
20491         /**
20492          * @event click
20493          * Fires when this item is clicked
20494          * @param {Roo.menu.BaseItem} this
20495          * @param {Roo.EventObject} e
20496          */
20497         click: true,
20498         /**
20499          * @event activate
20500          * Fires when this item is activated
20501          * @param {Roo.menu.BaseItem} this
20502          */
20503         activate : true,
20504         /**
20505          * @event deactivate
20506          * Fires when this item is deactivated
20507          * @param {Roo.menu.BaseItem} this
20508          */
20509         deactivate : true
20510     });
20511
20512     if(this.handler){
20513         this.on("click", this.handler, this.scope, true);
20514     }
20515 };
20516
20517 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20518     /**
20519      * @cfg {Function} handler
20520      * A function that will handle the click event of this menu item (defaults to undefined)
20521      */
20522     /**
20523      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20524      */
20525     canActivate : false,
20526     
20527      /**
20528      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20529      */
20530     hidden: false,
20531     
20532     /**
20533      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20534      */
20535     activeClass : "x-menu-item-active",
20536     /**
20537      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20538      */
20539     hideOnClick : true,
20540     /**
20541      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20542      */
20543     hideDelay : 100,
20544
20545     // private
20546     ctype: "Roo.menu.BaseItem",
20547
20548     // private
20549     actionMode : "container",
20550
20551     // private
20552     render : function(container, parentMenu){
20553         this.parentMenu = parentMenu;
20554         Roo.menu.BaseItem.superclass.render.call(this, container);
20555         this.container.menuItemId = this.id;
20556     },
20557
20558     // private
20559     onRender : function(container, position){
20560         this.el = Roo.get(this.el);
20561         container.dom.appendChild(this.el.dom);
20562     },
20563
20564     // private
20565     onClick : function(e){
20566         if(!this.disabled && this.fireEvent("click", this, e) !== false
20567                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20568             this.handleClick(e);
20569         }else{
20570             e.stopEvent();
20571         }
20572     },
20573
20574     // private
20575     activate : function(){
20576         if(this.disabled){
20577             return false;
20578         }
20579         var li = this.container;
20580         li.addClass(this.activeClass);
20581         this.region = li.getRegion().adjust(2, 2, -2, -2);
20582         this.fireEvent("activate", this);
20583         return true;
20584     },
20585
20586     // private
20587     deactivate : function(){
20588         this.container.removeClass(this.activeClass);
20589         this.fireEvent("deactivate", this);
20590     },
20591
20592     // private
20593     shouldDeactivate : function(e){
20594         return !this.region || !this.region.contains(e.getPoint());
20595     },
20596
20597     // private
20598     handleClick : function(e){
20599         if(this.hideOnClick){
20600             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20601         }
20602     },
20603
20604     // private
20605     expandMenu : function(autoActivate){
20606         // do nothing
20607     },
20608
20609     // private
20610     hideMenu : function(){
20611         // do nothing
20612     }
20613 });/*
20614  * Based on:
20615  * Ext JS Library 1.1.1
20616  * Copyright(c) 2006-2007, Ext JS, LLC.
20617  *
20618  * Originally Released Under LGPL - original licence link has changed is not relivant.
20619  *
20620  * Fork - LGPL
20621  * <script type="text/javascript">
20622  */
20623  
20624 /**
20625  * @class Roo.menu.Adapter
20626  * @extends Roo.menu.BaseItem
20627  * 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.
20628  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20629  * @constructor
20630  * Creates a new Adapter
20631  * @param {Object} config Configuration options
20632  */
20633 Roo.menu.Adapter = function(component, config){
20634     Roo.menu.Adapter.superclass.constructor.call(this, config);
20635     this.component = component;
20636 };
20637 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20638     // private
20639     canActivate : true,
20640
20641     // private
20642     onRender : function(container, position){
20643         this.component.render(container);
20644         this.el = this.component.getEl();
20645     },
20646
20647     // private
20648     activate : function(){
20649         if(this.disabled){
20650             return false;
20651         }
20652         this.component.focus();
20653         this.fireEvent("activate", this);
20654         return true;
20655     },
20656
20657     // private
20658     deactivate : function(){
20659         this.fireEvent("deactivate", this);
20660     },
20661
20662     // private
20663     disable : function(){
20664         this.component.disable();
20665         Roo.menu.Adapter.superclass.disable.call(this);
20666     },
20667
20668     // private
20669     enable : function(){
20670         this.component.enable();
20671         Roo.menu.Adapter.superclass.enable.call(this);
20672     }
20673 });/*
20674  * Based on:
20675  * Ext JS Library 1.1.1
20676  * Copyright(c) 2006-2007, Ext JS, LLC.
20677  *
20678  * Originally Released Under LGPL - original licence link has changed is not relivant.
20679  *
20680  * Fork - LGPL
20681  * <script type="text/javascript">
20682  */
20683
20684 /**
20685  * @class Roo.menu.TextItem
20686  * @extends Roo.menu.BaseItem
20687  * Adds a static text string to a menu, usually used as either a heading or group separator.
20688  * Note: old style constructor with text is still supported.
20689  * 
20690  * @constructor
20691  * Creates a new TextItem
20692  * @param {Object} cfg Configuration
20693  */
20694 Roo.menu.TextItem = function(cfg){
20695     if (typeof(cfg) == 'string') {
20696         this.text = cfg;
20697     } else {
20698         Roo.apply(this,cfg);
20699     }
20700     
20701     Roo.menu.TextItem.superclass.constructor.call(this);
20702 };
20703
20704 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20705     /**
20706      * @cfg {Boolean} text Text to show on item.
20707      */
20708     text : '',
20709     
20710     /**
20711      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20712      */
20713     hideOnClick : false,
20714     /**
20715      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20716      */
20717     itemCls : "x-menu-text",
20718
20719     // private
20720     onRender : function(){
20721         var s = document.createElement("span");
20722         s.className = this.itemCls;
20723         s.innerHTML = this.text;
20724         this.el = s;
20725         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20726     }
20727 });/*
20728  * Based on:
20729  * Ext JS Library 1.1.1
20730  * Copyright(c) 2006-2007, Ext JS, LLC.
20731  *
20732  * Originally Released Under LGPL - original licence link has changed is not relivant.
20733  *
20734  * Fork - LGPL
20735  * <script type="text/javascript">
20736  */
20737
20738 /**
20739  * @class Roo.menu.Separator
20740  * @extends Roo.menu.BaseItem
20741  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20742  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20743  * @constructor
20744  * @param {Object} config Configuration options
20745  */
20746 Roo.menu.Separator = function(config){
20747     Roo.menu.Separator.superclass.constructor.call(this, config);
20748 };
20749
20750 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20751     /**
20752      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20753      */
20754     itemCls : "x-menu-sep",
20755     /**
20756      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20757      */
20758     hideOnClick : false,
20759
20760     // private
20761     onRender : function(li){
20762         var s = document.createElement("span");
20763         s.className = this.itemCls;
20764         s.innerHTML = "&#160;";
20765         this.el = s;
20766         li.addClass("x-menu-sep-li");
20767         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20768     }
20769 });/*
20770  * Based on:
20771  * Ext JS Library 1.1.1
20772  * Copyright(c) 2006-2007, Ext JS, LLC.
20773  *
20774  * Originally Released Under LGPL - original licence link has changed is not relivant.
20775  *
20776  * Fork - LGPL
20777  * <script type="text/javascript">
20778  */
20779 /**
20780  * @class Roo.menu.Item
20781  * @extends Roo.menu.BaseItem
20782  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20783  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20784  * activation and click handling.
20785  * @constructor
20786  * Creates a new Item
20787  * @param {Object} config Configuration options
20788  */
20789 Roo.menu.Item = function(config){
20790     Roo.menu.Item.superclass.constructor.call(this, config);
20791     if(this.menu){
20792         this.menu = Roo.menu.MenuMgr.get(this.menu);
20793     }
20794 };
20795 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20796     
20797     /**
20798      * @cfg {String} text
20799      * The text to show on the menu item.
20800      */
20801     text: '',
20802      /**
20803      * @cfg {String} HTML to render in menu
20804      * The text to show on the menu item (HTML version).
20805      */
20806     html: '',
20807     /**
20808      * @cfg {String} icon
20809      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20810      */
20811     icon: undefined,
20812     /**
20813      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20814      */
20815     itemCls : "x-menu-item",
20816     /**
20817      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20818      */
20819     canActivate : true,
20820     /**
20821      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20822      */
20823     showDelay: 200,
20824     // doc'd in BaseItem
20825     hideDelay: 200,
20826
20827     // private
20828     ctype: "Roo.menu.Item",
20829     
20830     // private
20831     onRender : function(container, position){
20832         var el = document.createElement("a");
20833         el.hideFocus = true;
20834         el.unselectable = "on";
20835         el.href = this.href || "#";
20836         if(this.hrefTarget){
20837             el.target = this.hrefTarget;
20838         }
20839         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20840         
20841         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20842         
20843         el.innerHTML = String.format(
20844                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20845                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20846         this.el = el;
20847         Roo.menu.Item.superclass.onRender.call(this, container, position);
20848     },
20849
20850     /**
20851      * Sets the text to display in this menu item
20852      * @param {String} text The text to display
20853      * @param {Boolean} isHTML true to indicate text is pure html.
20854      */
20855     setText : function(text, isHTML){
20856         if (isHTML) {
20857             this.html = text;
20858         } else {
20859             this.text = text;
20860             this.html = '';
20861         }
20862         if(this.rendered){
20863             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20864      
20865             this.el.update(String.format(
20866                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20867                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20868             this.parentMenu.autoWidth();
20869         }
20870     },
20871
20872     // private
20873     handleClick : function(e){
20874         if(!this.href){ // if no link defined, stop the event automatically
20875             e.stopEvent();
20876         }
20877         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20878     },
20879
20880     // private
20881     activate : function(autoExpand){
20882         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20883             this.focus();
20884             if(autoExpand){
20885                 this.expandMenu();
20886             }
20887         }
20888         return true;
20889     },
20890
20891     // private
20892     shouldDeactivate : function(e){
20893         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20894             if(this.menu && this.menu.isVisible()){
20895                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20896             }
20897             return true;
20898         }
20899         return false;
20900     },
20901
20902     // private
20903     deactivate : function(){
20904         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20905         this.hideMenu();
20906     },
20907
20908     // private
20909     expandMenu : function(autoActivate){
20910         if(!this.disabled && this.menu){
20911             clearTimeout(this.hideTimer);
20912             delete this.hideTimer;
20913             if(!this.menu.isVisible() && !this.showTimer){
20914                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20915             }else if (this.menu.isVisible() && autoActivate){
20916                 this.menu.tryActivate(0, 1);
20917             }
20918         }
20919     },
20920
20921     // private
20922     deferExpand : function(autoActivate){
20923         delete this.showTimer;
20924         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20925         if(autoActivate){
20926             this.menu.tryActivate(0, 1);
20927         }
20928     },
20929
20930     // private
20931     hideMenu : function(){
20932         clearTimeout(this.showTimer);
20933         delete this.showTimer;
20934         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20935             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20936         }
20937     },
20938
20939     // private
20940     deferHide : function(){
20941         delete this.hideTimer;
20942         this.menu.hide();
20943     }
20944 });/*
20945  * Based on:
20946  * Ext JS Library 1.1.1
20947  * Copyright(c) 2006-2007, Ext JS, LLC.
20948  *
20949  * Originally Released Under LGPL - original licence link has changed is not relivant.
20950  *
20951  * Fork - LGPL
20952  * <script type="text/javascript">
20953  */
20954  
20955 /**
20956  * @class Roo.menu.CheckItem
20957  * @extends Roo.menu.Item
20958  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20959  * @constructor
20960  * Creates a new CheckItem
20961  * @param {Object} config Configuration options
20962  */
20963 Roo.menu.CheckItem = function(config){
20964     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20965     this.addEvents({
20966         /**
20967          * @event beforecheckchange
20968          * Fires before the checked value is set, providing an opportunity to cancel if needed
20969          * @param {Roo.menu.CheckItem} this
20970          * @param {Boolean} checked The new checked value that will be set
20971          */
20972         "beforecheckchange" : true,
20973         /**
20974          * @event checkchange
20975          * Fires after the checked value has been set
20976          * @param {Roo.menu.CheckItem} this
20977          * @param {Boolean} checked The checked value that was set
20978          */
20979         "checkchange" : true
20980     });
20981     if(this.checkHandler){
20982         this.on('checkchange', this.checkHandler, this.scope);
20983     }
20984 };
20985 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20986     /**
20987      * @cfg {String} group
20988      * All check items with the same group name will automatically be grouped into a single-select
20989      * radio button group (defaults to '')
20990      */
20991     /**
20992      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20993      */
20994     itemCls : "x-menu-item x-menu-check-item",
20995     /**
20996      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20997      */
20998     groupClass : "x-menu-group-item",
20999
21000     /**
21001      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
21002      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21003      * initialized with checked = true will be rendered as checked.
21004      */
21005     checked: false,
21006
21007     // private
21008     ctype: "Roo.menu.CheckItem",
21009
21010     // private
21011     onRender : function(c){
21012         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21013         if(this.group){
21014             this.el.addClass(this.groupClass);
21015         }
21016         Roo.menu.MenuMgr.registerCheckable(this);
21017         if(this.checked){
21018             this.checked = false;
21019             this.setChecked(true, true);
21020         }
21021     },
21022
21023     // private
21024     destroy : function(){
21025         if(this.rendered){
21026             Roo.menu.MenuMgr.unregisterCheckable(this);
21027         }
21028         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21029     },
21030
21031     /**
21032      * Set the checked state of this item
21033      * @param {Boolean} checked The new checked value
21034      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21035      */
21036     setChecked : function(state, suppressEvent){
21037         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21038             if(this.container){
21039                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21040             }
21041             this.checked = state;
21042             if(suppressEvent !== true){
21043                 this.fireEvent("checkchange", this, state);
21044             }
21045         }
21046     },
21047
21048     // private
21049     handleClick : function(e){
21050        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21051            this.setChecked(!this.checked);
21052        }
21053        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21054     }
21055 });/*
21056  * Based on:
21057  * Ext JS Library 1.1.1
21058  * Copyright(c) 2006-2007, Ext JS, LLC.
21059  *
21060  * Originally Released Under LGPL - original licence link has changed is not relivant.
21061  *
21062  * Fork - LGPL
21063  * <script type="text/javascript">
21064  */
21065  
21066 /**
21067  * @class Roo.menu.DateItem
21068  * @extends Roo.menu.Adapter
21069  * A menu item that wraps the {@link Roo.DatPicker} component.
21070  * @constructor
21071  * Creates a new DateItem
21072  * @param {Object} config Configuration options
21073  */
21074 Roo.menu.DateItem = function(config){
21075     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21076     /** The Roo.DatePicker object @type Roo.DatePicker */
21077     this.picker = this.component;
21078     this.addEvents({select: true});
21079     
21080     this.picker.on("render", function(picker){
21081         picker.getEl().swallowEvent("click");
21082         picker.container.addClass("x-menu-date-item");
21083     });
21084
21085     this.picker.on("select", this.onSelect, this);
21086 };
21087
21088 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21089     // private
21090     onSelect : function(picker, date){
21091         this.fireEvent("select", this, date, picker);
21092         Roo.menu.DateItem.superclass.handleClick.call(this);
21093     }
21094 });/*
21095  * Based on:
21096  * Ext JS Library 1.1.1
21097  * Copyright(c) 2006-2007, Ext JS, LLC.
21098  *
21099  * Originally Released Under LGPL - original licence link has changed is not relivant.
21100  *
21101  * Fork - LGPL
21102  * <script type="text/javascript">
21103  */
21104  
21105 /**
21106  * @class Roo.menu.ColorItem
21107  * @extends Roo.menu.Adapter
21108  * A menu item that wraps the {@link Roo.ColorPalette} component.
21109  * @constructor
21110  * Creates a new ColorItem
21111  * @param {Object} config Configuration options
21112  */
21113 Roo.menu.ColorItem = function(config){
21114     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21115     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21116     this.palette = this.component;
21117     this.relayEvents(this.palette, ["select"]);
21118     if(this.selectHandler){
21119         this.on('select', this.selectHandler, this.scope);
21120     }
21121 };
21122 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21123  * Based on:
21124  * Ext JS Library 1.1.1
21125  * Copyright(c) 2006-2007, Ext JS, LLC.
21126  *
21127  * Originally Released Under LGPL - original licence link has changed is not relivant.
21128  *
21129  * Fork - LGPL
21130  * <script type="text/javascript">
21131  */
21132  
21133
21134 /**
21135  * @class Roo.menu.DateMenu
21136  * @extends Roo.menu.Menu
21137  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21138  * @constructor
21139  * Creates a new DateMenu
21140  * @param {Object} config Configuration options
21141  */
21142 Roo.menu.DateMenu = function(config){
21143     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21144     this.plain = true;
21145     var di = new Roo.menu.DateItem(config);
21146     this.add(di);
21147     /**
21148      * The {@link Roo.DatePicker} instance for this DateMenu
21149      * @type DatePicker
21150      */
21151     this.picker = di.picker;
21152     /**
21153      * @event select
21154      * @param {DatePicker} picker
21155      * @param {Date} date
21156      */
21157     this.relayEvents(di, ["select"]);
21158     this.on('beforeshow', function(){
21159         if(this.picker){
21160             this.picker.hideMonthPicker(false);
21161         }
21162     }, this);
21163 };
21164 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21165     cls:'x-date-menu'
21166 });/*
21167  * Based on:
21168  * Ext JS Library 1.1.1
21169  * Copyright(c) 2006-2007, Ext JS, LLC.
21170  *
21171  * Originally Released Under LGPL - original licence link has changed is not relivant.
21172  *
21173  * Fork - LGPL
21174  * <script type="text/javascript">
21175  */
21176  
21177
21178 /**
21179  * @class Roo.menu.ColorMenu
21180  * @extends Roo.menu.Menu
21181  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21182  * @constructor
21183  * Creates a new ColorMenu
21184  * @param {Object} config Configuration options
21185  */
21186 Roo.menu.ColorMenu = function(config){
21187     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21188     this.plain = true;
21189     var ci = new Roo.menu.ColorItem(config);
21190     this.add(ci);
21191     /**
21192      * The {@link Roo.ColorPalette} instance for this ColorMenu
21193      * @type ColorPalette
21194      */
21195     this.palette = ci.palette;
21196     /**
21197      * @event select
21198      * @param {ColorPalette} palette
21199      * @param {String} color
21200      */
21201     this.relayEvents(ci, ["select"]);
21202 };
21203 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21204  * Based on:
21205  * Ext JS Library 1.1.1
21206  * Copyright(c) 2006-2007, Ext JS, LLC.
21207  *
21208  * Originally Released Under LGPL - original licence link has changed is not relivant.
21209  *
21210  * Fork - LGPL
21211  * <script type="text/javascript">
21212  */
21213  
21214 /**
21215  * @class Roo.form.Field
21216  * @extends Roo.BoxComponent
21217  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21218  * @constructor
21219  * Creates a new Field
21220  * @param {Object} config Configuration options
21221  */
21222 Roo.form.Field = function(config){
21223     Roo.form.Field.superclass.constructor.call(this, config);
21224 };
21225
21226 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21227     /**
21228      * @cfg {String} fieldLabel Label to use when rendering a form.
21229      */
21230        /**
21231      * @cfg {String} qtip Mouse over tip
21232      */
21233      
21234     /**
21235      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21236      */
21237     invalidClass : "x-form-invalid",
21238     /**
21239      * @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")
21240      */
21241     invalidText : "The value in this field is invalid",
21242     /**
21243      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21244      */
21245     focusClass : "x-form-focus",
21246     /**
21247      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21248       automatic validation (defaults to "keyup").
21249      */
21250     validationEvent : "keyup",
21251     /**
21252      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21253      */
21254     validateOnBlur : true,
21255     /**
21256      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21257      */
21258     validationDelay : 250,
21259     /**
21260      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21261      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21262      */
21263     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21264     /**
21265      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21266      */
21267     fieldClass : "x-form-field",
21268     /**
21269      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21270      *<pre>
21271 Value         Description
21272 -----------   ----------------------------------------------------------------------
21273 qtip          Display a quick tip when the user hovers over the field
21274 title         Display a default browser title attribute popup
21275 under         Add a block div beneath the field containing the error text
21276 side          Add an error icon to the right of the field with a popup on hover
21277 [element id]  Add the error text directly to the innerHTML of the specified element
21278 </pre>
21279      */
21280     msgTarget : 'qtip',
21281     /**
21282      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21283      */
21284     msgFx : 'normal',
21285
21286     /**
21287      * @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.
21288      */
21289     readOnly : false,
21290
21291     /**
21292      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21293      */
21294     disabled : false,
21295
21296     /**
21297      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21298      */
21299     inputType : undefined,
21300     
21301     /**
21302      * @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).
21303          */
21304         tabIndex : undefined,
21305         
21306     // private
21307     isFormField : true,
21308
21309     // private
21310     hasFocus : false,
21311     /**
21312      * @property {Roo.Element} fieldEl
21313      * Element Containing the rendered Field (with label etc.)
21314      */
21315     /**
21316      * @cfg {Mixed} value A value to initialize this field with.
21317      */
21318     value : undefined,
21319
21320     /**
21321      * @cfg {String} name The field's HTML name attribute.
21322      */
21323     /**
21324      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21325      */
21326
21327         // private ??
21328         initComponent : function(){
21329         Roo.form.Field.superclass.initComponent.call(this);
21330         this.addEvents({
21331             /**
21332              * @event focus
21333              * Fires when this field receives input focus.
21334              * @param {Roo.form.Field} this
21335              */
21336             focus : true,
21337             /**
21338              * @event blur
21339              * Fires when this field loses input focus.
21340              * @param {Roo.form.Field} this
21341              */
21342             blur : true,
21343             /**
21344              * @event specialkey
21345              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21346              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21347              * @param {Roo.form.Field} this
21348              * @param {Roo.EventObject} e The event object
21349              */
21350             specialkey : true,
21351             /**
21352              * @event change
21353              * Fires just before the field blurs if the field value has changed.
21354              * @param {Roo.form.Field} this
21355              * @param {Mixed} newValue The new value
21356              * @param {Mixed} oldValue The original value
21357              */
21358             change : true,
21359             /**
21360              * @event invalid
21361              * Fires after the field has been marked as invalid.
21362              * @param {Roo.form.Field} this
21363              * @param {String} msg The validation message
21364              */
21365             invalid : true,
21366             /**
21367              * @event valid
21368              * Fires after the field has been validated with no errors.
21369              * @param {Roo.form.Field} this
21370              */
21371             valid : true,
21372              /**
21373              * @event keyup
21374              * Fires after the key up
21375              * @param {Roo.form.Field} this
21376              * @param {Roo.EventObject}  e The event Object
21377              */
21378             keyup : true
21379         });
21380     },
21381
21382     /**
21383      * Returns the name attribute of the field if available
21384      * @return {String} name The field name
21385      */
21386     getName: function(){
21387          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21388     },
21389
21390     // private
21391     onRender : function(ct, position){
21392         Roo.form.Field.superclass.onRender.call(this, ct, position);
21393         if(!this.el){
21394             var cfg = this.getAutoCreate();
21395             if(!cfg.name){
21396                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21397             }
21398             if (!cfg.name.length) {
21399                 delete cfg.name;
21400             }
21401             if(this.inputType){
21402                 cfg.type = this.inputType;
21403             }
21404             this.el = ct.createChild(cfg, position);
21405         }
21406         var type = this.el.dom.type;
21407         if(type){
21408             if(type == 'password'){
21409                 type = 'text';
21410             }
21411             this.el.addClass('x-form-'+type);
21412         }
21413         if(this.readOnly){
21414             this.el.dom.readOnly = true;
21415         }
21416         if(this.tabIndex !== undefined){
21417             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21418         }
21419
21420         this.el.addClass([this.fieldClass, this.cls]);
21421         this.initValue();
21422     },
21423
21424     /**
21425      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21426      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21427      * @return {Roo.form.Field} this
21428      */
21429     applyTo : function(target){
21430         this.allowDomMove = false;
21431         this.el = Roo.get(target);
21432         this.render(this.el.dom.parentNode);
21433         return this;
21434     },
21435
21436     // private
21437     initValue : function(){
21438         if(this.value !== undefined){
21439             this.setValue(this.value);
21440         }else if(this.el.dom.value.length > 0){
21441             this.setValue(this.el.dom.value);
21442         }
21443     },
21444
21445     /**
21446      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21447      */
21448     isDirty : function() {
21449         if(this.disabled) {
21450             return false;
21451         }
21452         return String(this.getValue()) !== String(this.originalValue);
21453     },
21454
21455     // private
21456     afterRender : function(){
21457         Roo.form.Field.superclass.afterRender.call(this);
21458         this.initEvents();
21459     },
21460
21461     // private
21462     fireKey : function(e){
21463         //Roo.log('field ' + e.getKey());
21464         if(e.isNavKeyPress()){
21465             this.fireEvent("specialkey", this, e);
21466         }
21467     },
21468
21469     /**
21470      * Resets the current field value to the originally loaded value and clears any validation messages
21471      */
21472     reset : function(){
21473         this.setValue(this.originalValue);
21474         this.clearInvalid();
21475     },
21476
21477     // private
21478     initEvents : function(){
21479         // safari killled keypress - so keydown is now used..
21480         this.el.on("keydown" , this.fireKey,  this);
21481         this.el.on("focus", this.onFocus,  this);
21482         this.el.on("blur", this.onBlur,  this);
21483         this.el.relayEvent('keyup', this);
21484
21485         // reference to original value for reset
21486         this.originalValue = this.getValue();
21487     },
21488
21489     // private
21490     onFocus : function(){
21491         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21492             this.el.addClass(this.focusClass);
21493         }
21494         if(!this.hasFocus){
21495             this.hasFocus = true;
21496             this.startValue = this.getValue();
21497             this.fireEvent("focus", this);
21498         }
21499     },
21500
21501     beforeBlur : Roo.emptyFn,
21502
21503     // private
21504     onBlur : function(){
21505         this.beforeBlur();
21506         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21507             this.el.removeClass(this.focusClass);
21508         }
21509         this.hasFocus = false;
21510         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21511             this.validate();
21512         }
21513         var v = this.getValue();
21514         if(String(v) !== String(this.startValue)){
21515             this.fireEvent('change', this, v, this.startValue);
21516         }
21517         this.fireEvent("blur", this);
21518     },
21519
21520     /**
21521      * Returns whether or not the field value is currently valid
21522      * @param {Boolean} preventMark True to disable marking the field invalid
21523      * @return {Boolean} True if the value is valid, else false
21524      */
21525     isValid : function(preventMark){
21526         if(this.disabled){
21527             return true;
21528         }
21529         var restore = this.preventMark;
21530         this.preventMark = preventMark === true;
21531         var v = this.validateValue(this.processValue(this.getRawValue()));
21532         this.preventMark = restore;
21533         return v;
21534     },
21535
21536     /**
21537      * Validates the field value
21538      * @return {Boolean} True if the value is valid, else false
21539      */
21540     validate : function(){
21541         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21542             this.clearInvalid();
21543             return true;
21544         }
21545         return false;
21546     },
21547
21548     processValue : function(value){
21549         return value;
21550     },
21551
21552     // private
21553     // Subclasses should provide the validation implementation by overriding this
21554     validateValue : function(value){
21555         return true;
21556     },
21557
21558     /**
21559      * Mark this field as invalid
21560      * @param {String} msg The validation message
21561      */
21562     markInvalid : function(msg){
21563         if(!this.rendered || this.preventMark){ // not rendered
21564             return;
21565         }
21566         this.el.addClass(this.invalidClass);
21567         msg = msg || this.invalidText;
21568         switch(this.msgTarget){
21569             case 'qtip':
21570                 this.el.dom.qtip = msg;
21571                 this.el.dom.qclass = 'x-form-invalid-tip';
21572                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21573                     Roo.QuickTips.enable();
21574                 }
21575                 break;
21576             case 'title':
21577                 this.el.dom.title = msg;
21578                 break;
21579             case 'under':
21580                 if(!this.errorEl){
21581                     var elp = this.el.findParent('.x-form-element', 5, true);
21582                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21583                     this.errorEl.setWidth(elp.getWidth(true)-20);
21584                 }
21585                 this.errorEl.update(msg);
21586                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21587                 break;
21588             case 'side':
21589                 if(!this.errorIcon){
21590                     var elp = this.el.findParent('.x-form-element', 5, true);
21591                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21592                 }
21593                 this.alignErrorIcon();
21594                 this.errorIcon.dom.qtip = msg;
21595                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21596                 this.errorIcon.show();
21597                 this.on('resize', this.alignErrorIcon, this);
21598                 break;
21599             default:
21600                 var t = Roo.getDom(this.msgTarget);
21601                 t.innerHTML = msg;
21602                 t.style.display = this.msgDisplay;
21603                 break;
21604         }
21605         this.fireEvent('invalid', this, msg);
21606     },
21607
21608     // private
21609     alignErrorIcon : function(){
21610         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21611     },
21612
21613     /**
21614      * Clear any invalid styles/messages for this field
21615      */
21616     clearInvalid : function(){
21617         if(!this.rendered || this.preventMark){ // not rendered
21618             return;
21619         }
21620         this.el.removeClass(this.invalidClass);
21621         switch(this.msgTarget){
21622             case 'qtip':
21623                 this.el.dom.qtip = '';
21624                 break;
21625             case 'title':
21626                 this.el.dom.title = '';
21627                 break;
21628             case 'under':
21629                 if(this.errorEl){
21630                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21631                 }
21632                 break;
21633             case 'side':
21634                 if(this.errorIcon){
21635                     this.errorIcon.dom.qtip = '';
21636                     this.errorIcon.hide();
21637                     this.un('resize', this.alignErrorIcon, this);
21638                 }
21639                 break;
21640             default:
21641                 var t = Roo.getDom(this.msgTarget);
21642                 t.innerHTML = '';
21643                 t.style.display = 'none';
21644                 break;
21645         }
21646         this.fireEvent('valid', this);
21647     },
21648
21649     /**
21650      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21651      * @return {Mixed} value The field value
21652      */
21653     getRawValue : function(){
21654         var v = this.el.getValue();
21655         
21656         return v;
21657     },
21658
21659     /**
21660      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21661      * @return {Mixed} value The field value
21662      */
21663     getValue : function(){
21664         var v = this.el.getValue();
21665          
21666         return v;
21667     },
21668
21669     /**
21670      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21671      * @param {Mixed} value The value to set
21672      */
21673     setRawValue : function(v){
21674         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21675     },
21676
21677     /**
21678      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21679      * @param {Mixed} value The value to set
21680      */
21681     setValue : function(v){
21682         this.value = v;
21683         if(this.rendered){
21684             this.el.dom.value = (v === null || v === undefined ? '' : v);
21685              this.validate();
21686         }
21687     },
21688
21689     adjustSize : function(w, h){
21690         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21691         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21692         return s;
21693     },
21694
21695     adjustWidth : function(tag, w){
21696         tag = tag.toLowerCase();
21697         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21698             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21699                 if(tag == 'input'){
21700                     return w + 2;
21701                 }
21702                 if(tag == 'textarea'){
21703                     return w-2;
21704                 }
21705             }else if(Roo.isOpera){
21706                 if(tag == 'input'){
21707                     return w + 2;
21708                 }
21709                 if(tag == 'textarea'){
21710                     return w-2;
21711                 }
21712             }
21713         }
21714         return w;
21715     }
21716 });
21717
21718
21719 // anything other than normal should be considered experimental
21720 Roo.form.Field.msgFx = {
21721     normal : {
21722         show: function(msgEl, f){
21723             msgEl.setDisplayed('block');
21724         },
21725
21726         hide : function(msgEl, f){
21727             msgEl.setDisplayed(false).update('');
21728         }
21729     },
21730
21731     slide : {
21732         show: function(msgEl, f){
21733             msgEl.slideIn('t', {stopFx:true});
21734         },
21735
21736         hide : function(msgEl, f){
21737             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21738         }
21739     },
21740
21741     slideRight : {
21742         show: function(msgEl, f){
21743             msgEl.fixDisplay();
21744             msgEl.alignTo(f.el, 'tl-tr');
21745             msgEl.slideIn('l', {stopFx:true});
21746         },
21747
21748         hide : function(msgEl, f){
21749             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21750         }
21751     }
21752 };/*
21753  * Based on:
21754  * Ext JS Library 1.1.1
21755  * Copyright(c) 2006-2007, Ext JS, LLC.
21756  *
21757  * Originally Released Under LGPL - original licence link has changed is not relivant.
21758  *
21759  * Fork - LGPL
21760  * <script type="text/javascript">
21761  */
21762  
21763
21764 /**
21765  * @class Roo.form.TextField
21766  * @extends Roo.form.Field
21767  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21768  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21769  * @constructor
21770  * Creates a new TextField
21771  * @param {Object} config Configuration options
21772  */
21773 Roo.form.TextField = function(config){
21774     Roo.form.TextField.superclass.constructor.call(this, config);
21775     this.addEvents({
21776         /**
21777          * @event autosize
21778          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21779          * according to the default logic, but this event provides a hook for the developer to apply additional
21780          * logic at runtime to resize the field if needed.
21781              * @param {Roo.form.Field} this This text field
21782              * @param {Number} width The new field width
21783              */
21784         autosize : true
21785     });
21786 };
21787
21788 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21789     /**
21790      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21791      */
21792     grow : false,
21793     /**
21794      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21795      */
21796     growMin : 30,
21797     /**
21798      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21799      */
21800     growMax : 800,
21801     /**
21802      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21803      */
21804     vtype : null,
21805     /**
21806      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21807      */
21808     maskRe : null,
21809     /**
21810      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21811      */
21812     disableKeyFilter : false,
21813     /**
21814      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21815      */
21816     allowBlank : true,
21817     /**
21818      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21819      */
21820     minLength : 0,
21821     /**
21822      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21823      */
21824     maxLength : Number.MAX_VALUE,
21825     /**
21826      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21827      */
21828     minLengthText : "The minimum length for this field is {0}",
21829     /**
21830      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21831      */
21832     maxLengthText : "The maximum length for this field is {0}",
21833     /**
21834      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21835      */
21836     selectOnFocus : false,
21837     /**
21838      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21839      */
21840     blankText : "This field is required",
21841     /**
21842      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21843      * If available, this function will be called only after the basic validators all return true, and will be passed the
21844      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21845      */
21846     validator : null,
21847     /**
21848      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21849      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21850      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21851      */
21852     regex : null,
21853     /**
21854      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21855      */
21856     regexText : "",
21857     /**
21858      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21859      */
21860     emptyText : null,
21861    
21862
21863     // private
21864     initEvents : function()
21865     {
21866         if (this.emptyText) {
21867             this.el.attr('placeholder', this.emptyText);
21868         }
21869         
21870         Roo.form.TextField.superclass.initEvents.call(this);
21871         if(this.validationEvent == 'keyup'){
21872             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21873             this.el.on('keyup', this.filterValidation, this);
21874         }
21875         else if(this.validationEvent !== false){
21876             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21877         }
21878         
21879         if(this.selectOnFocus){
21880             this.on("focus", this.preFocus, this);
21881             
21882         }
21883         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21884             this.el.on("keypress", this.filterKeys, this);
21885         }
21886         if(this.grow){
21887             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21888             this.el.on("click", this.autoSize,  this);
21889         }
21890         if(this.el.is('input[type=password]') && Roo.isSafari){
21891             this.el.on('keydown', this.SafariOnKeyDown, this);
21892         }
21893     },
21894
21895     processValue : function(value){
21896         if(this.stripCharsRe){
21897             var newValue = value.replace(this.stripCharsRe, '');
21898             if(newValue !== value){
21899                 this.setRawValue(newValue);
21900                 return newValue;
21901             }
21902         }
21903         return value;
21904     },
21905
21906     filterValidation : function(e){
21907         if(!e.isNavKeyPress()){
21908             this.validationTask.delay(this.validationDelay);
21909         }
21910     },
21911
21912     // private
21913     onKeyUp : function(e){
21914         if(!e.isNavKeyPress()){
21915             this.autoSize();
21916         }
21917     },
21918
21919     /**
21920      * Resets the current field value to the originally-loaded value and clears any validation messages.
21921      *  
21922      */
21923     reset : function(){
21924         Roo.form.TextField.superclass.reset.call(this);
21925        
21926     },
21927
21928     
21929     // private
21930     preFocus : function(){
21931         
21932         if(this.selectOnFocus){
21933             this.el.dom.select();
21934         }
21935     },
21936
21937     
21938     // private
21939     filterKeys : function(e){
21940         var k = e.getKey();
21941         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21942             return;
21943         }
21944         var c = e.getCharCode(), cc = String.fromCharCode(c);
21945         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21946             return;
21947         }
21948         if(!this.maskRe.test(cc)){
21949             e.stopEvent();
21950         }
21951     },
21952
21953     setValue : function(v){
21954         
21955         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21956         
21957         this.autoSize();
21958     },
21959
21960     /**
21961      * Validates a value according to the field's validation rules and marks the field as invalid
21962      * if the validation fails
21963      * @param {Mixed} value The value to validate
21964      * @return {Boolean} True if the value is valid, else false
21965      */
21966     validateValue : function(value){
21967         if(value.length < 1)  { // if it's blank
21968              if(this.allowBlank){
21969                 this.clearInvalid();
21970                 return true;
21971              }else{
21972                 this.markInvalid(this.blankText);
21973                 return false;
21974              }
21975         }
21976         if(value.length < this.minLength){
21977             this.markInvalid(String.format(this.minLengthText, this.minLength));
21978             return false;
21979         }
21980         if(value.length > this.maxLength){
21981             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21982             return false;
21983         }
21984         if(this.vtype){
21985             var vt = Roo.form.VTypes;
21986             if(!vt[this.vtype](value, this)){
21987                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21988                 return false;
21989             }
21990         }
21991         if(typeof this.validator == "function"){
21992             var msg = this.validator(value);
21993             if(msg !== true){
21994                 this.markInvalid(msg);
21995                 return false;
21996             }
21997         }
21998         if(this.regex && !this.regex.test(value)){
21999             this.markInvalid(this.regexText);
22000             return false;
22001         }
22002         return true;
22003     },
22004
22005     /**
22006      * Selects text in this field
22007      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22008      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22009      */
22010     selectText : function(start, end){
22011         var v = this.getRawValue();
22012         if(v.length > 0){
22013             start = start === undefined ? 0 : start;
22014             end = end === undefined ? v.length : end;
22015             var d = this.el.dom;
22016             if(d.setSelectionRange){
22017                 d.setSelectionRange(start, end);
22018             }else if(d.createTextRange){
22019                 var range = d.createTextRange();
22020                 range.moveStart("character", start);
22021                 range.moveEnd("character", v.length-end);
22022                 range.select();
22023             }
22024         }
22025     },
22026
22027     /**
22028      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22029      * This only takes effect if grow = true, and fires the autosize event.
22030      */
22031     autoSize : function(){
22032         if(!this.grow || !this.rendered){
22033             return;
22034         }
22035         if(!this.metrics){
22036             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22037         }
22038         var el = this.el;
22039         var v = el.dom.value;
22040         var d = document.createElement('div');
22041         d.appendChild(document.createTextNode(v));
22042         v = d.innerHTML;
22043         d = null;
22044         v += "&#160;";
22045         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22046         this.el.setWidth(w);
22047         this.fireEvent("autosize", this, w);
22048     },
22049     
22050     // private
22051     SafariOnKeyDown : function(event)
22052     {
22053         // this is a workaround for a password hang bug on chrome/ webkit.
22054         
22055         var isSelectAll = false;
22056         
22057         if(this.el.dom.selectionEnd > 0){
22058             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22059         }
22060         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22061             event.preventDefault();
22062             this.setValue('');
22063             return;
22064         }
22065         
22066         if(isSelectAll){ // backspace and delete key
22067             
22068             event.preventDefault();
22069             // this is very hacky as keydown always get's upper case.
22070             //
22071             var cc = String.fromCharCode(event.getCharCode());
22072             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
22073             
22074         }
22075         
22076         
22077     }
22078 });/*
22079  * Based on:
22080  * Ext JS Library 1.1.1
22081  * Copyright(c) 2006-2007, Ext JS, LLC.
22082  *
22083  * Originally Released Under LGPL - original licence link has changed is not relivant.
22084  *
22085  * Fork - LGPL
22086  * <script type="text/javascript">
22087  */
22088  
22089 /**
22090  * @class Roo.form.Hidden
22091  * @extends Roo.form.TextField
22092  * Simple Hidden element used on forms 
22093  * 
22094  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22095  * 
22096  * @constructor
22097  * Creates a new Hidden form element.
22098  * @param {Object} config Configuration options
22099  */
22100
22101
22102
22103 // easy hidden field...
22104 Roo.form.Hidden = function(config){
22105     Roo.form.Hidden.superclass.constructor.call(this, config);
22106 };
22107   
22108 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22109     fieldLabel:      '',
22110     inputType:      'hidden',
22111     width:          50,
22112     allowBlank:     true,
22113     labelSeparator: '',
22114     hidden:         true,
22115     itemCls :       'x-form-item-display-none'
22116
22117
22118 });
22119
22120
22121 /*
22122  * Based on:
22123  * Ext JS Library 1.1.1
22124  * Copyright(c) 2006-2007, Ext JS, LLC.
22125  *
22126  * Originally Released Under LGPL - original licence link has changed is not relivant.
22127  *
22128  * Fork - LGPL
22129  * <script type="text/javascript">
22130  */
22131  
22132 /**
22133  * @class Roo.form.TriggerField
22134  * @extends Roo.form.TextField
22135  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22136  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22137  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22138  * for which you can provide a custom implementation.  For example:
22139  * <pre><code>
22140 var trigger = new Roo.form.TriggerField();
22141 trigger.onTriggerClick = myTriggerFn;
22142 trigger.applyTo('my-field');
22143 </code></pre>
22144  *
22145  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22146  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22147  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22148  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22149  * @constructor
22150  * Create a new TriggerField.
22151  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22152  * to the base TextField)
22153  */
22154 Roo.form.TriggerField = function(config){
22155     this.mimicing = false;
22156     Roo.form.TriggerField.superclass.constructor.call(this, config);
22157 };
22158
22159 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22160     /**
22161      * @cfg {String} triggerClass A CSS class to apply to the trigger
22162      */
22163     /**
22164      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22165      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22166      */
22167     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22168     /**
22169      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22170      */
22171     hideTrigger:false,
22172
22173     /** @cfg {Boolean} grow @hide */
22174     /** @cfg {Number} growMin @hide */
22175     /** @cfg {Number} growMax @hide */
22176
22177     /**
22178      * @hide 
22179      * @method
22180      */
22181     autoSize: Roo.emptyFn,
22182     // private
22183     monitorTab : true,
22184     // private
22185     deferHeight : true,
22186
22187     
22188     actionMode : 'wrap',
22189     // private
22190     onResize : function(w, h){
22191         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22192         if(typeof w == 'number'){
22193             var x = w - this.trigger.getWidth();
22194             this.el.setWidth(this.adjustWidth('input', x));
22195             this.trigger.setStyle('left', x+'px');
22196         }
22197     },
22198
22199     // private
22200     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22201
22202     // private
22203     getResizeEl : function(){
22204         return this.wrap;
22205     },
22206
22207     // private
22208     getPositionEl : function(){
22209         return this.wrap;
22210     },
22211
22212     // private
22213     alignErrorIcon : function(){
22214         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22215     },
22216
22217     // private
22218     onRender : function(ct, position){
22219         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22220         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22221         this.trigger = this.wrap.createChild(this.triggerConfig ||
22222                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22223         if(this.hideTrigger){
22224             this.trigger.setDisplayed(false);
22225         }
22226         this.initTrigger();
22227         if(!this.width){
22228             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22229         }
22230     },
22231
22232     // private
22233     initTrigger : function(){
22234         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22235         this.trigger.addClassOnOver('x-form-trigger-over');
22236         this.trigger.addClassOnClick('x-form-trigger-click');
22237     },
22238
22239     // private
22240     onDestroy : function(){
22241         if(this.trigger){
22242             this.trigger.removeAllListeners();
22243             this.trigger.remove();
22244         }
22245         if(this.wrap){
22246             this.wrap.remove();
22247         }
22248         Roo.form.TriggerField.superclass.onDestroy.call(this);
22249     },
22250
22251     // private
22252     onFocus : function(){
22253         Roo.form.TriggerField.superclass.onFocus.call(this);
22254         if(!this.mimicing){
22255             this.wrap.addClass('x-trigger-wrap-focus');
22256             this.mimicing = true;
22257             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22258             if(this.monitorTab){
22259                 this.el.on("keydown", this.checkTab, this);
22260             }
22261         }
22262     },
22263
22264     // private
22265     checkTab : function(e){
22266         if(e.getKey() == e.TAB){
22267             this.triggerBlur();
22268         }
22269     },
22270
22271     // private
22272     onBlur : function(){
22273         // do nothing
22274     },
22275
22276     // private
22277     mimicBlur : function(e, t){
22278         if(!this.wrap.contains(t) && this.validateBlur()){
22279             this.triggerBlur();
22280         }
22281     },
22282
22283     // private
22284     triggerBlur : function(){
22285         this.mimicing = false;
22286         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22287         if(this.monitorTab){
22288             this.el.un("keydown", this.checkTab, this);
22289         }
22290         this.wrap.removeClass('x-trigger-wrap-focus');
22291         Roo.form.TriggerField.superclass.onBlur.call(this);
22292     },
22293
22294     // private
22295     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22296     validateBlur : function(e, t){
22297         return true;
22298     },
22299
22300     // private
22301     onDisable : function(){
22302         Roo.form.TriggerField.superclass.onDisable.call(this);
22303         if(this.wrap){
22304             this.wrap.addClass('x-item-disabled');
22305         }
22306     },
22307
22308     // private
22309     onEnable : function(){
22310         Roo.form.TriggerField.superclass.onEnable.call(this);
22311         if(this.wrap){
22312             this.wrap.removeClass('x-item-disabled');
22313         }
22314     },
22315
22316     // private
22317     onShow : function(){
22318         var ae = this.getActionEl();
22319         
22320         if(ae){
22321             ae.dom.style.display = '';
22322             ae.dom.style.visibility = 'visible';
22323         }
22324     },
22325
22326     // private
22327     
22328     onHide : function(){
22329         var ae = this.getActionEl();
22330         ae.dom.style.display = 'none';
22331     },
22332
22333     /**
22334      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22335      * by an implementing function.
22336      * @method
22337      * @param {EventObject} e
22338      */
22339     onTriggerClick : Roo.emptyFn
22340 });
22341
22342 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22343 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22344 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22345 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22346     initComponent : function(){
22347         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22348
22349         this.triggerConfig = {
22350             tag:'span', cls:'x-form-twin-triggers', cn:[
22351             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22352             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22353         ]};
22354     },
22355
22356     getTrigger : function(index){
22357         return this.triggers[index];
22358     },
22359
22360     initTrigger : function(){
22361         var ts = this.trigger.select('.x-form-trigger', true);
22362         this.wrap.setStyle('overflow', 'hidden');
22363         var triggerField = this;
22364         ts.each(function(t, all, index){
22365             t.hide = function(){
22366                 var w = triggerField.wrap.getWidth();
22367                 this.dom.style.display = 'none';
22368                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22369             };
22370             t.show = function(){
22371                 var w = triggerField.wrap.getWidth();
22372                 this.dom.style.display = '';
22373                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22374             };
22375             var triggerIndex = 'Trigger'+(index+1);
22376
22377             if(this['hide'+triggerIndex]){
22378                 t.dom.style.display = 'none';
22379             }
22380             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22381             t.addClassOnOver('x-form-trigger-over');
22382             t.addClassOnClick('x-form-trigger-click');
22383         }, this);
22384         this.triggers = ts.elements;
22385     },
22386
22387     onTrigger1Click : Roo.emptyFn,
22388     onTrigger2Click : Roo.emptyFn
22389 });/*
22390  * Based on:
22391  * Ext JS Library 1.1.1
22392  * Copyright(c) 2006-2007, Ext JS, LLC.
22393  *
22394  * Originally Released Under LGPL - original licence link has changed is not relivant.
22395  *
22396  * Fork - LGPL
22397  * <script type="text/javascript">
22398  */
22399  
22400 /**
22401  * @class Roo.form.TextArea
22402  * @extends Roo.form.TextField
22403  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22404  * support for auto-sizing.
22405  * @constructor
22406  * Creates a new TextArea
22407  * @param {Object} config Configuration options
22408  */
22409 Roo.form.TextArea = function(config){
22410     Roo.form.TextArea.superclass.constructor.call(this, config);
22411     // these are provided exchanges for backwards compat
22412     // minHeight/maxHeight were replaced by growMin/growMax to be
22413     // compatible with TextField growing config values
22414     if(this.minHeight !== undefined){
22415         this.growMin = this.minHeight;
22416     }
22417     if(this.maxHeight !== undefined){
22418         this.growMax = this.maxHeight;
22419     }
22420 };
22421
22422 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22423     /**
22424      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22425      */
22426     growMin : 60,
22427     /**
22428      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22429      */
22430     growMax: 1000,
22431     /**
22432      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22433      * in the field (equivalent to setting overflow: hidden, defaults to false)
22434      */
22435     preventScrollbars: false,
22436     /**
22437      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22438      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22439      */
22440
22441     // private
22442     onRender : function(ct, position){
22443         if(!this.el){
22444             this.defaultAutoCreate = {
22445                 tag: "textarea",
22446                 style:"width:300px;height:60px;",
22447                 autocomplete: "off"
22448             };
22449         }
22450         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22451         if(this.grow){
22452             this.textSizeEl = Roo.DomHelper.append(document.body, {
22453                 tag: "pre", cls: "x-form-grow-sizer"
22454             });
22455             if(this.preventScrollbars){
22456                 this.el.setStyle("overflow", "hidden");
22457             }
22458             this.el.setHeight(this.growMin);
22459         }
22460     },
22461
22462     onDestroy : function(){
22463         if(this.textSizeEl){
22464             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22465         }
22466         Roo.form.TextArea.superclass.onDestroy.call(this);
22467     },
22468
22469     // private
22470     onKeyUp : function(e){
22471         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22472             this.autoSize();
22473         }
22474     },
22475
22476     /**
22477      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22478      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22479      */
22480     autoSize : function(){
22481         if(!this.grow || !this.textSizeEl){
22482             return;
22483         }
22484         var el = this.el;
22485         var v = el.dom.value;
22486         var ts = this.textSizeEl;
22487
22488         ts.innerHTML = '';
22489         ts.appendChild(document.createTextNode(v));
22490         v = ts.innerHTML;
22491
22492         Roo.fly(ts).setWidth(this.el.getWidth());
22493         if(v.length < 1){
22494             v = "&#160;&#160;";
22495         }else{
22496             if(Roo.isIE){
22497                 v = v.replace(/\n/g, '<p>&#160;</p>');
22498             }
22499             v += "&#160;\n&#160;";
22500         }
22501         ts.innerHTML = v;
22502         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22503         if(h != this.lastHeight){
22504             this.lastHeight = h;
22505             this.el.setHeight(h);
22506             this.fireEvent("autosize", this, h);
22507         }
22508     }
22509 });/*
22510  * Based on:
22511  * Ext JS Library 1.1.1
22512  * Copyright(c) 2006-2007, Ext JS, LLC.
22513  *
22514  * Originally Released Under LGPL - original licence link has changed is not relivant.
22515  *
22516  * Fork - LGPL
22517  * <script type="text/javascript">
22518  */
22519  
22520
22521 /**
22522  * @class Roo.form.NumberField
22523  * @extends Roo.form.TextField
22524  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22525  * @constructor
22526  * Creates a new NumberField
22527  * @param {Object} config Configuration options
22528  */
22529 Roo.form.NumberField = function(config){
22530     Roo.form.NumberField.superclass.constructor.call(this, config);
22531 };
22532
22533 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22534     /**
22535      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22536      */
22537     fieldClass: "x-form-field x-form-num-field",
22538     /**
22539      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22540      */
22541     allowDecimals : true,
22542     /**
22543      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22544      */
22545     decimalSeparator : ".",
22546     /**
22547      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22548      */
22549     decimalPrecision : 2,
22550     /**
22551      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22552      */
22553     allowNegative : true,
22554     /**
22555      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22556      */
22557     minValue : Number.NEGATIVE_INFINITY,
22558     /**
22559      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22560      */
22561     maxValue : Number.MAX_VALUE,
22562     /**
22563      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22564      */
22565     minText : "The minimum value for this field is {0}",
22566     /**
22567      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22568      */
22569     maxText : "The maximum value for this field is {0}",
22570     /**
22571      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22572      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22573      */
22574     nanText : "{0} is not a valid number",
22575
22576     // private
22577     initEvents : function(){
22578         Roo.form.NumberField.superclass.initEvents.call(this);
22579         var allowed = "0123456789";
22580         if(this.allowDecimals){
22581             allowed += this.decimalSeparator;
22582         }
22583         if(this.allowNegative){
22584             allowed += "-";
22585         }
22586         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22587         var keyPress = function(e){
22588             var k = e.getKey();
22589             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22590                 return;
22591             }
22592             var c = e.getCharCode();
22593             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22594                 e.stopEvent();
22595             }
22596         };
22597         this.el.on("keypress", keyPress, this);
22598     },
22599
22600     // private
22601     validateValue : function(value){
22602         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22603             return false;
22604         }
22605         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22606              return true;
22607         }
22608         var num = this.parseValue(value);
22609         if(isNaN(num)){
22610             this.markInvalid(String.format(this.nanText, value));
22611             return false;
22612         }
22613         if(num < this.minValue){
22614             this.markInvalid(String.format(this.minText, this.minValue));
22615             return false;
22616         }
22617         if(num > this.maxValue){
22618             this.markInvalid(String.format(this.maxText, this.maxValue));
22619             return false;
22620         }
22621         return true;
22622     },
22623
22624     getValue : function(){
22625         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22626     },
22627
22628     // private
22629     parseValue : function(value){
22630         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22631         return isNaN(value) ? '' : value;
22632     },
22633
22634     // private
22635     fixPrecision : function(value){
22636         var nan = isNaN(value);
22637         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22638             return nan ? '' : value;
22639         }
22640         return parseFloat(value).toFixed(this.decimalPrecision);
22641     },
22642
22643     setValue : function(v){
22644         v = this.fixPrecision(v);
22645         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22646     },
22647
22648     // private
22649     decimalPrecisionFcn : function(v){
22650         return Math.floor(v);
22651     },
22652
22653     beforeBlur : function(){
22654         var v = this.parseValue(this.getRawValue());
22655         if(v){
22656             this.setValue(v);
22657         }
22658     }
22659 });/*
22660  * Based on:
22661  * Ext JS Library 1.1.1
22662  * Copyright(c) 2006-2007, Ext JS, LLC.
22663  *
22664  * Originally Released Under LGPL - original licence link has changed is not relivant.
22665  *
22666  * Fork - LGPL
22667  * <script type="text/javascript">
22668  */
22669  
22670 /**
22671  * @class Roo.form.DateField
22672  * @extends Roo.form.TriggerField
22673  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22674 * @constructor
22675 * Create a new DateField
22676 * @param {Object} config
22677  */
22678 Roo.form.DateField = function(config){
22679     Roo.form.DateField.superclass.constructor.call(this, config);
22680     
22681       this.addEvents({
22682          
22683         /**
22684          * @event select
22685          * Fires when a date is selected
22686              * @param {Roo.form.DateField} combo This combo box
22687              * @param {Date} date The date selected
22688              */
22689         'select' : true
22690          
22691     });
22692     
22693     
22694     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22695     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22696     this.ddMatch = null;
22697     if(this.disabledDates){
22698         var dd = this.disabledDates;
22699         var re = "(?:";
22700         for(var i = 0; i < dd.length; i++){
22701             re += dd[i];
22702             if(i != dd.length-1) re += "|";
22703         }
22704         this.ddMatch = new RegExp(re + ")");
22705     }
22706 };
22707
22708 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22709     /**
22710      * @cfg {String} format
22711      * The default date format string which can be overriden for localization support.  The format must be
22712      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22713      */
22714     format : "m/d/y",
22715     /**
22716      * @cfg {String} altFormats
22717      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22718      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22719      */
22720     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22721     /**
22722      * @cfg {Array} disabledDays
22723      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22724      */
22725     disabledDays : null,
22726     /**
22727      * @cfg {String} disabledDaysText
22728      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22729      */
22730     disabledDaysText : "Disabled",
22731     /**
22732      * @cfg {Array} disabledDates
22733      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22734      * expression so they are very powerful. Some examples:
22735      * <ul>
22736      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22737      * <li>["03/08", "09/16"] would disable those days for every year</li>
22738      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22739      * <li>["03/../2006"] would disable every day in March 2006</li>
22740      * <li>["^03"] would disable every day in every March</li>
22741      * </ul>
22742      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22743      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22744      */
22745     disabledDates : null,
22746     /**
22747      * @cfg {String} disabledDatesText
22748      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22749      */
22750     disabledDatesText : "Disabled",
22751     /**
22752      * @cfg {Date/String} minValue
22753      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22754      * valid format (defaults to null).
22755      */
22756     minValue : null,
22757     /**
22758      * @cfg {Date/String} maxValue
22759      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22760      * valid format (defaults to null).
22761      */
22762     maxValue : null,
22763     /**
22764      * @cfg {String} minText
22765      * The error text to display when the date in the cell is before minValue (defaults to
22766      * 'The date in this field must be after {minValue}').
22767      */
22768     minText : "The date in this field must be equal to or after {0}",
22769     /**
22770      * @cfg {String} maxText
22771      * The error text to display when the date in the cell is after maxValue (defaults to
22772      * 'The date in this field must be before {maxValue}').
22773      */
22774     maxText : "The date in this field must be equal to or before {0}",
22775     /**
22776      * @cfg {String} invalidText
22777      * The error text to display when the date in the field is invalid (defaults to
22778      * '{value} is not a valid date - it must be in the format {format}').
22779      */
22780     invalidText : "{0} is not a valid date - it must be in the format {1}",
22781     /**
22782      * @cfg {String} triggerClass
22783      * An additional CSS class used to style the trigger button.  The trigger will always get the
22784      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22785      * which displays a calendar icon).
22786      */
22787     triggerClass : 'x-form-date-trigger',
22788     
22789
22790     /**
22791      * @cfg {Boolean} useIso
22792      * if enabled, then the date field will use a hidden field to store the 
22793      * real value as iso formated date. default (false)
22794      */ 
22795     useIso : false,
22796     /**
22797      * @cfg {String/Object} autoCreate
22798      * A DomHelper element spec, or true for a default element spec (defaults to
22799      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22800      */ 
22801     // private
22802     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22803     
22804     // private
22805     hiddenField: false,
22806     
22807     onRender : function(ct, position)
22808     {
22809         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22810         if (this.useIso) {
22811             //this.el.dom.removeAttribute('name'); 
22812             Roo.log("Changing name?");
22813             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22814             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22815                     'before', true);
22816             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22817             // prevent input submission
22818             this.hiddenName = this.name;
22819         }
22820             
22821             
22822     },
22823     
22824     // private
22825     validateValue : function(value)
22826     {
22827         value = this.formatDate(value);
22828         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22829             Roo.log('super failed');
22830             return false;
22831         }
22832         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22833              return true;
22834         }
22835         var svalue = value;
22836         value = this.parseDate(value);
22837         if(!value){
22838             Roo.log('parse date failed' + svalue);
22839             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22840             return false;
22841         }
22842         var time = value.getTime();
22843         if(this.minValue && time < this.minValue.getTime()){
22844             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22845             return false;
22846         }
22847         if(this.maxValue && time > this.maxValue.getTime()){
22848             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22849             return false;
22850         }
22851         if(this.disabledDays){
22852             var day = value.getDay();
22853             for(var i = 0; i < this.disabledDays.length; i++) {
22854                 if(day === this.disabledDays[i]){
22855                     this.markInvalid(this.disabledDaysText);
22856                     return false;
22857                 }
22858             }
22859         }
22860         var fvalue = this.formatDate(value);
22861         if(this.ddMatch && this.ddMatch.test(fvalue)){
22862             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22863             return false;
22864         }
22865         return true;
22866     },
22867
22868     // private
22869     // Provides logic to override the default TriggerField.validateBlur which just returns true
22870     validateBlur : function(){
22871         return !this.menu || !this.menu.isVisible();
22872     },
22873     
22874     getName: function()
22875     {
22876         // returns hidden if it's set..
22877         if (!this.rendered) {return ''};
22878         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22879         
22880     },
22881
22882     /**
22883      * Returns the current date value of the date field.
22884      * @return {Date} The date value
22885      */
22886     getValue : function(){
22887         
22888         return  this.hiddenField ?
22889                 this.hiddenField.value :
22890                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22891     },
22892
22893     /**
22894      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22895      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22896      * (the default format used is "m/d/y").
22897      * <br />Usage:
22898      * <pre><code>
22899 //All of these calls set the same date value (May 4, 2006)
22900
22901 //Pass a date object:
22902 var dt = new Date('5/4/06');
22903 dateField.setValue(dt);
22904
22905 //Pass a date string (default format):
22906 dateField.setValue('5/4/06');
22907
22908 //Pass a date string (custom format):
22909 dateField.format = 'Y-m-d';
22910 dateField.setValue('2006-5-4');
22911 </code></pre>
22912      * @param {String/Date} date The date or valid date string
22913      */
22914     setValue : function(date){
22915         if (this.hiddenField) {
22916             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22917         }
22918         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22919         // make sure the value field is always stored as a date..
22920         this.value = this.parseDate(date);
22921         
22922         
22923     },
22924
22925     // private
22926     parseDate : function(value){
22927         if(!value || value instanceof Date){
22928             return value;
22929         }
22930         var v = Date.parseDate(value, this.format);
22931          if (!v && this.useIso) {
22932             v = Date.parseDate(value, 'Y-m-d');
22933         }
22934         if(!v && this.altFormats){
22935             if(!this.altFormatsArray){
22936                 this.altFormatsArray = this.altFormats.split("|");
22937             }
22938             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22939                 v = Date.parseDate(value, this.altFormatsArray[i]);
22940             }
22941         }
22942         return v;
22943     },
22944
22945     // private
22946     formatDate : function(date, fmt){
22947         return (!date || !(date instanceof Date)) ?
22948                date : date.dateFormat(fmt || this.format);
22949     },
22950
22951     // private
22952     menuListeners : {
22953         select: function(m, d){
22954             
22955             this.setValue(d);
22956             this.fireEvent('select', this, d);
22957         },
22958         show : function(){ // retain focus styling
22959             this.onFocus();
22960         },
22961         hide : function(){
22962             this.focus.defer(10, this);
22963             var ml = this.menuListeners;
22964             this.menu.un("select", ml.select,  this);
22965             this.menu.un("show", ml.show,  this);
22966             this.menu.un("hide", ml.hide,  this);
22967         }
22968     },
22969
22970     // private
22971     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22972     onTriggerClick : function(){
22973         if(this.disabled){
22974             return;
22975         }
22976         if(this.menu == null){
22977             this.menu = new Roo.menu.DateMenu();
22978         }
22979         Roo.apply(this.menu.picker,  {
22980             showClear: this.allowBlank,
22981             minDate : this.minValue,
22982             maxDate : this.maxValue,
22983             disabledDatesRE : this.ddMatch,
22984             disabledDatesText : this.disabledDatesText,
22985             disabledDays : this.disabledDays,
22986             disabledDaysText : this.disabledDaysText,
22987             format : this.useIso ? 'Y-m-d' : this.format,
22988             minText : String.format(this.minText, this.formatDate(this.minValue)),
22989             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22990         });
22991         this.menu.on(Roo.apply({}, this.menuListeners, {
22992             scope:this
22993         }));
22994         this.menu.picker.setValue(this.getValue() || new Date());
22995         this.menu.show(this.el, "tl-bl?");
22996     },
22997
22998     beforeBlur : function(){
22999         var v = this.parseDate(this.getRawValue());
23000         if(v){
23001             this.setValue(v);
23002         }
23003     }
23004
23005     /** @cfg {Boolean} grow @hide */
23006     /** @cfg {Number} growMin @hide */
23007     /** @cfg {Number} growMax @hide */
23008     /**
23009      * @hide
23010      * @method autoSize
23011      */
23012 });/*
23013  * Based on:
23014  * Ext JS Library 1.1.1
23015  * Copyright(c) 2006-2007, Ext JS, LLC.
23016  *
23017  * Originally Released Under LGPL - original licence link has changed is not relivant.
23018  *
23019  * Fork - LGPL
23020  * <script type="text/javascript">
23021  */
23022  
23023 /**
23024  * @class Roo.form.MonthField
23025  * @extends Roo.form.TriggerField
23026  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23027 * @constructor
23028 * Create a new MonthField
23029 * @param {Object} config
23030  */
23031 Roo.form.MonthField = function(config){
23032     
23033     Roo.form.MonthField.superclass.constructor.call(this, config);
23034     
23035       this.addEvents({
23036          
23037         /**
23038          * @event select
23039          * Fires when a date is selected
23040              * @param {Roo.form.MonthFieeld} combo This combo box
23041              * @param {Date} date The date selected
23042              */
23043         'select' : true
23044          
23045     });
23046     
23047     
23048     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23049     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23050     this.ddMatch = null;
23051     if(this.disabledDates){
23052         var dd = this.disabledDates;
23053         var re = "(?:";
23054         for(var i = 0; i < dd.length; i++){
23055             re += dd[i];
23056             if(i != dd.length-1) re += "|";
23057         }
23058         this.ddMatch = new RegExp(re + ")");
23059     }
23060 };
23061
23062 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23063     /**
23064      * @cfg {String} format
23065      * The default date format string which can be overriden for localization support.  The format must be
23066      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23067      */
23068     format : "M Y",
23069     /**
23070      * @cfg {String} altFormats
23071      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23072      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23073      */
23074     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23075     /**
23076      * @cfg {Array} disabledDays
23077      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23078      */
23079     disabledDays : [0,1,2,3,4,5,6],
23080     /**
23081      * @cfg {String} disabledDaysText
23082      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23083      */
23084     disabledDaysText : "Disabled",
23085     /**
23086      * @cfg {Array} disabledDates
23087      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23088      * expression so they are very powerful. Some examples:
23089      * <ul>
23090      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23091      * <li>["03/08", "09/16"] would disable those days for every year</li>
23092      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23093      * <li>["03/../2006"] would disable every day in March 2006</li>
23094      * <li>["^03"] would disable every day in every March</li>
23095      * </ul>
23096      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23097      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23098      */
23099     disabledDates : null,
23100     /**
23101      * @cfg {String} disabledDatesText
23102      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23103      */
23104     disabledDatesText : "Disabled",
23105     /**
23106      * @cfg {Date/String} minValue
23107      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23108      * valid format (defaults to null).
23109      */
23110     minValue : null,
23111     /**
23112      * @cfg {Date/String} maxValue
23113      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23114      * valid format (defaults to null).
23115      */
23116     maxValue : null,
23117     /**
23118      * @cfg {String} minText
23119      * The error text to display when the date in the cell is before minValue (defaults to
23120      * 'The date in this field must be after {minValue}').
23121      */
23122     minText : "The date in this field must be equal to or after {0}",
23123     /**
23124      * @cfg {String} maxTextf
23125      * The error text to display when the date in the cell is after maxValue (defaults to
23126      * 'The date in this field must be before {maxValue}').
23127      */
23128     maxText : "The date in this field must be equal to or before {0}",
23129     /**
23130      * @cfg {String} invalidText
23131      * The error text to display when the date in the field is invalid (defaults to
23132      * '{value} is not a valid date - it must be in the format {format}').
23133      */
23134     invalidText : "{0} is not a valid date - it must be in the format {1}",
23135     /**
23136      * @cfg {String} triggerClass
23137      * An additional CSS class used to style the trigger button.  The trigger will always get the
23138      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23139      * which displays a calendar icon).
23140      */
23141     triggerClass : 'x-form-date-trigger',
23142     
23143
23144     /**
23145      * @cfg {Boolean} useIso
23146      * if enabled, then the date field will use a hidden field to store the 
23147      * real value as iso formated date. default (true)
23148      */ 
23149     useIso : true,
23150     /**
23151      * @cfg {String/Object} autoCreate
23152      * A DomHelper element spec, or true for a default element spec (defaults to
23153      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23154      */ 
23155     // private
23156     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23157     
23158     // private
23159     hiddenField: false,
23160     
23161     hideMonthPicker : false,
23162     
23163     onRender : function(ct, position)
23164     {
23165         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23166         if (this.useIso) {
23167             this.el.dom.removeAttribute('name'); 
23168             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23169                     'before', true);
23170             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23171             // prevent input submission
23172             this.hiddenName = this.name;
23173         }
23174             
23175             
23176     },
23177     
23178     // private
23179     validateValue : function(value)
23180     {
23181         value = this.formatDate(value);
23182         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23183             return false;
23184         }
23185         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23186              return true;
23187         }
23188         var svalue = value;
23189         value = this.parseDate(value);
23190         if(!value){
23191             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23192             return false;
23193         }
23194         var time = value.getTime();
23195         if(this.minValue && time < this.minValue.getTime()){
23196             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23197             return false;
23198         }
23199         if(this.maxValue && time > this.maxValue.getTime()){
23200             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23201             return false;
23202         }
23203         /*if(this.disabledDays){
23204             var day = value.getDay();
23205             for(var i = 0; i < this.disabledDays.length; i++) {
23206                 if(day === this.disabledDays[i]){
23207                     this.markInvalid(this.disabledDaysText);
23208                     return false;
23209                 }
23210             }
23211         }
23212         */
23213         var fvalue = this.formatDate(value);
23214         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23215             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23216             return false;
23217         }
23218         */
23219         return true;
23220     },
23221
23222     // private
23223     // Provides logic to override the default TriggerField.validateBlur which just returns true
23224     validateBlur : function(){
23225         return !this.menu || !this.menu.isVisible();
23226     },
23227
23228     /**
23229      * Returns the current date value of the date field.
23230      * @return {Date} The date value
23231      */
23232     getValue : function(){
23233         
23234         
23235         
23236         return  this.hiddenField ?
23237                 this.hiddenField.value :
23238                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23239     },
23240
23241     /**
23242      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23243      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23244      * (the default format used is "m/d/y").
23245      * <br />Usage:
23246      * <pre><code>
23247 //All of these calls set the same date value (May 4, 2006)
23248
23249 //Pass a date object:
23250 var dt = new Date('5/4/06');
23251 monthField.setValue(dt);
23252
23253 //Pass a date string (default format):
23254 monthField.setValue('5/4/06');
23255
23256 //Pass a date string (custom format):
23257 monthField.format = 'Y-m-d';
23258 monthField.setValue('2006-5-4');
23259 </code></pre>
23260      * @param {String/Date} date The date or valid date string
23261      */
23262     setValue : function(date){
23263         Roo.log('month setValue' + date);
23264         // can only be first of month..
23265         
23266         var val = this.parseDate(date);
23267         
23268         if (this.hiddenField) {
23269             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23270         }
23271         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23272         this.value = this.parseDate(date);
23273     },
23274
23275     // private
23276     parseDate : function(value){
23277         if(!value || value instanceof Date){
23278             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23279             return value;
23280         }
23281         var v = Date.parseDate(value, this.format);
23282         if (!v && this.useIso) {
23283             v = Date.parseDate(value, 'Y-m-d');
23284         }
23285         if (v) {
23286             // 
23287             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23288         }
23289         
23290         
23291         if(!v && this.altFormats){
23292             if(!this.altFormatsArray){
23293                 this.altFormatsArray = this.altFormats.split("|");
23294             }
23295             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23296                 v = Date.parseDate(value, this.altFormatsArray[i]);
23297             }
23298         }
23299         return v;
23300     },
23301
23302     // private
23303     formatDate : function(date, fmt){
23304         return (!date || !(date instanceof Date)) ?
23305                date : date.dateFormat(fmt || this.format);
23306     },
23307
23308     // private
23309     menuListeners : {
23310         select: function(m, d){
23311             this.setValue(d);
23312             this.fireEvent('select', this, d);
23313         },
23314         show : function(){ // retain focus styling
23315             this.onFocus();
23316         },
23317         hide : function(){
23318             this.focus.defer(10, this);
23319             var ml = this.menuListeners;
23320             this.menu.un("select", ml.select,  this);
23321             this.menu.un("show", ml.show,  this);
23322             this.menu.un("hide", ml.hide,  this);
23323         }
23324     },
23325     // private
23326     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23327     onTriggerClick : function(){
23328         if(this.disabled){
23329             return;
23330         }
23331         if(this.menu == null){
23332             this.menu = new Roo.menu.DateMenu();
23333            
23334         }
23335         
23336         Roo.apply(this.menu.picker,  {
23337             
23338             showClear: this.allowBlank,
23339             minDate : this.minValue,
23340             maxDate : this.maxValue,
23341             disabledDatesRE : this.ddMatch,
23342             disabledDatesText : this.disabledDatesText,
23343             
23344             format : this.useIso ? 'Y-m-d' : this.format,
23345             minText : String.format(this.minText, this.formatDate(this.minValue)),
23346             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23347             
23348         });
23349          this.menu.on(Roo.apply({}, this.menuListeners, {
23350             scope:this
23351         }));
23352        
23353         
23354         var m = this.menu;
23355         var p = m.picker;
23356         
23357         // hide month picker get's called when we called by 'before hide';
23358         
23359         var ignorehide = true;
23360         p.hideMonthPicker  = function(disableAnim){
23361             if (ignorehide) {
23362                 return;
23363             }
23364              if(this.monthPicker){
23365                 Roo.log("hideMonthPicker called");
23366                 if(disableAnim === true){
23367                     this.monthPicker.hide();
23368                 }else{
23369                     this.monthPicker.slideOut('t', {duration:.2});
23370                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23371                     p.fireEvent("select", this, this.value);
23372                     m.hide();
23373                 }
23374             }
23375         }
23376         
23377         Roo.log('picker set value');
23378         Roo.log(this.getValue());
23379         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23380         m.show(this.el, 'tl-bl?');
23381         ignorehide  = false;
23382         // this will trigger hideMonthPicker..
23383         
23384         
23385         // hidden the day picker
23386         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23387         
23388         
23389         
23390       
23391         
23392         p.showMonthPicker.defer(100, p);
23393     
23394         
23395        
23396     },
23397
23398     beforeBlur : function(){
23399         var v = this.parseDate(this.getRawValue());
23400         if(v){
23401             this.setValue(v);
23402         }
23403     }
23404
23405     /** @cfg {Boolean} grow @hide */
23406     /** @cfg {Number} growMin @hide */
23407     /** @cfg {Number} growMax @hide */
23408     /**
23409      * @hide
23410      * @method autoSize
23411      */
23412 });/*
23413  * Based on:
23414  * Ext JS Library 1.1.1
23415  * Copyright(c) 2006-2007, Ext JS, LLC.
23416  *
23417  * Originally Released Under LGPL - original licence link has changed is not relivant.
23418  *
23419  * Fork - LGPL
23420  * <script type="text/javascript">
23421  */
23422  
23423
23424 /**
23425  * @class Roo.form.ComboBox
23426  * @extends Roo.form.TriggerField
23427  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23428  * @constructor
23429  * Create a new ComboBox.
23430  * @param {Object} config Configuration options
23431  */
23432 Roo.form.ComboBox = function(config){
23433     Roo.form.ComboBox.superclass.constructor.call(this, config);
23434     this.addEvents({
23435         /**
23436          * @event expand
23437          * Fires when the dropdown list is expanded
23438              * @param {Roo.form.ComboBox} combo This combo box
23439              */
23440         'expand' : true,
23441         /**
23442          * @event collapse
23443          * Fires when the dropdown list is collapsed
23444              * @param {Roo.form.ComboBox} combo This combo box
23445              */
23446         'collapse' : true,
23447         /**
23448          * @event beforeselect
23449          * Fires before a list item is selected. Return false to cancel the selection.
23450              * @param {Roo.form.ComboBox} combo This combo box
23451              * @param {Roo.data.Record} record The data record returned from the underlying store
23452              * @param {Number} index The index of the selected item in the dropdown list
23453              */
23454         'beforeselect' : true,
23455         /**
23456          * @event select
23457          * Fires when a list item is selected
23458              * @param {Roo.form.ComboBox} combo This combo box
23459              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23460              * @param {Number} index The index of the selected item in the dropdown list
23461              */
23462         'select' : true,
23463         /**
23464          * @event beforequery
23465          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23466          * The event object passed has these properties:
23467              * @param {Roo.form.ComboBox} combo This combo box
23468              * @param {String} query The query
23469              * @param {Boolean} forceAll true to force "all" query
23470              * @param {Boolean} cancel true to cancel the query
23471              * @param {Object} e The query event object
23472              */
23473         'beforequery': true,
23474          /**
23475          * @event add
23476          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23477              * @param {Roo.form.ComboBox} combo This combo box
23478              */
23479         'add' : true,
23480         /**
23481          * @event edit
23482          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23483              * @param {Roo.form.ComboBox} combo This combo box
23484              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23485              */
23486         'edit' : true
23487         
23488         
23489     });
23490     if(this.transform){
23491         this.allowDomMove = false;
23492         var s = Roo.getDom(this.transform);
23493         if(!this.hiddenName){
23494             this.hiddenName = s.name;
23495         }
23496         if(!this.store){
23497             this.mode = 'local';
23498             var d = [], opts = s.options;
23499             for(var i = 0, len = opts.length;i < len; i++){
23500                 var o = opts[i];
23501                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23502                 if(o.selected) {
23503                     this.value = value;
23504                 }
23505                 d.push([value, o.text]);
23506             }
23507             this.store = new Roo.data.SimpleStore({
23508                 'id': 0,
23509                 fields: ['value', 'text'],
23510                 data : d
23511             });
23512             this.valueField = 'value';
23513             this.displayField = 'text';
23514         }
23515         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23516         if(!this.lazyRender){
23517             this.target = true;
23518             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23519             s.parentNode.removeChild(s); // remove it
23520             this.render(this.el.parentNode);
23521         }else{
23522             s.parentNode.removeChild(s); // remove it
23523         }
23524
23525     }
23526     if (this.store) {
23527         this.store = Roo.factory(this.store, Roo.data);
23528     }
23529     
23530     this.selectedIndex = -1;
23531     if(this.mode == 'local'){
23532         if(config.queryDelay === undefined){
23533             this.queryDelay = 10;
23534         }
23535         if(config.minChars === undefined){
23536             this.minChars = 0;
23537         }
23538     }
23539 };
23540
23541 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23542     /**
23543      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23544      */
23545     /**
23546      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23547      * rendering into an Roo.Editor, defaults to false)
23548      */
23549     /**
23550      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23551      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23552      */
23553     /**
23554      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23555      */
23556     /**
23557      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23558      * the dropdown list (defaults to undefined, with no header element)
23559      */
23560
23561      /**
23562      * @cfg {String/Roo.Template} tpl The template to use to render the output
23563      */
23564      
23565     // private
23566     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23567     /**
23568      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23569      */
23570     listWidth: undefined,
23571     /**
23572      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23573      * mode = 'remote' or 'text' if mode = 'local')
23574      */
23575     displayField: undefined,
23576     /**
23577      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23578      * mode = 'remote' or 'value' if mode = 'local'). 
23579      * Note: use of a valueField requires the user make a selection
23580      * in order for a value to be mapped.
23581      */
23582     valueField: undefined,
23583     
23584     
23585     /**
23586      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23587      * field's data value (defaults to the underlying DOM element's name)
23588      */
23589     hiddenName: undefined,
23590     /**
23591      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23592      */
23593     listClass: '',
23594     /**
23595      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23596      */
23597     selectedClass: 'x-combo-selected',
23598     /**
23599      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23600      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23601      * which displays a downward arrow icon).
23602      */
23603     triggerClass : 'x-form-arrow-trigger',
23604     /**
23605      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23606      */
23607     shadow:'sides',
23608     /**
23609      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23610      * anchor positions (defaults to 'tl-bl')
23611      */
23612     listAlign: 'tl-bl?',
23613     /**
23614      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23615      */
23616     maxHeight: 300,
23617     /**
23618      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23619      * query specified by the allQuery config option (defaults to 'query')
23620      */
23621     triggerAction: 'query',
23622     /**
23623      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23624      * (defaults to 4, does not apply if editable = false)
23625      */
23626     minChars : 4,
23627     /**
23628      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23629      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23630      */
23631     typeAhead: false,
23632     /**
23633      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23634      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23635      */
23636     queryDelay: 500,
23637     /**
23638      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23639      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23640      */
23641     pageSize: 0,
23642     /**
23643      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23644      * when editable = true (defaults to false)
23645      */
23646     selectOnFocus:false,
23647     /**
23648      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23649      */
23650     queryParam: 'query',
23651     /**
23652      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23653      * when mode = 'remote' (defaults to 'Loading...')
23654      */
23655     loadingText: 'Loading...',
23656     /**
23657      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23658      */
23659     resizable: false,
23660     /**
23661      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23662      */
23663     handleHeight : 8,
23664     /**
23665      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23666      * traditional select (defaults to true)
23667      */
23668     editable: true,
23669     /**
23670      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23671      */
23672     allQuery: '',
23673     /**
23674      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23675      */
23676     mode: 'remote',
23677     /**
23678      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23679      * listWidth has a higher value)
23680      */
23681     minListWidth : 70,
23682     /**
23683      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23684      * allow the user to set arbitrary text into the field (defaults to false)
23685      */
23686     forceSelection:false,
23687     /**
23688      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23689      * if typeAhead = true (defaults to 250)
23690      */
23691     typeAheadDelay : 250,
23692     /**
23693      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23694      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23695      */
23696     valueNotFoundText : undefined,
23697     /**
23698      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23699      */
23700     blockFocus : false,
23701     
23702     /**
23703      * @cfg {Boolean} disableClear Disable showing of clear button.
23704      */
23705     disableClear : false,
23706     /**
23707      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23708      */
23709     alwaysQuery : false,
23710     
23711     //private
23712     addicon : false,
23713     editicon: false,
23714     
23715     // element that contains real text value.. (when hidden is used..)
23716      
23717     // private
23718     onRender : function(ct, position){
23719         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23720         if(this.hiddenName){
23721             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23722                     'before', true);
23723             this.hiddenField.value =
23724                 this.hiddenValue !== undefined ? this.hiddenValue :
23725                 this.value !== undefined ? this.value : '';
23726
23727             // prevent input submission
23728             this.el.dom.removeAttribute('name');
23729              
23730              
23731         }
23732         if(Roo.isGecko){
23733             this.el.dom.setAttribute('autocomplete', 'off');
23734         }
23735
23736         var cls = 'x-combo-list';
23737
23738         this.list = new Roo.Layer({
23739             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23740         });
23741
23742         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23743         this.list.setWidth(lw);
23744         this.list.swallowEvent('mousewheel');
23745         this.assetHeight = 0;
23746
23747         if(this.title){
23748             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23749             this.assetHeight += this.header.getHeight();
23750         }
23751
23752         this.innerList = this.list.createChild({cls:cls+'-inner'});
23753         this.innerList.on('mouseover', this.onViewOver, this);
23754         this.innerList.on('mousemove', this.onViewMove, this);
23755         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23756         
23757         if(this.allowBlank && !this.pageSize && !this.disableClear){
23758             this.footer = this.list.createChild({cls:cls+'-ft'});
23759             this.pageTb = new Roo.Toolbar(this.footer);
23760            
23761         }
23762         if(this.pageSize){
23763             this.footer = this.list.createChild({cls:cls+'-ft'});
23764             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23765                     {pageSize: this.pageSize});
23766             
23767         }
23768         
23769         if (this.pageTb && this.allowBlank && !this.disableClear) {
23770             var _this = this;
23771             this.pageTb.add(new Roo.Toolbar.Fill(), {
23772                 cls: 'x-btn-icon x-btn-clear',
23773                 text: '&#160;',
23774                 handler: function()
23775                 {
23776                     _this.collapse();
23777                     _this.clearValue();
23778                     _this.onSelect(false, -1);
23779                 }
23780             });
23781         }
23782         if (this.footer) {
23783             this.assetHeight += this.footer.getHeight();
23784         }
23785         
23786
23787         if(!this.tpl){
23788             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23789         }
23790
23791         this.view = new Roo.View(this.innerList, this.tpl, {
23792             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23793         });
23794
23795         this.view.on('click', this.onViewClick, this);
23796
23797         this.store.on('beforeload', this.onBeforeLoad, this);
23798         this.store.on('load', this.onLoad, this);
23799         this.store.on('loadexception', this.onLoadException, this);
23800
23801         if(this.resizable){
23802             this.resizer = new Roo.Resizable(this.list,  {
23803                pinned:true, handles:'se'
23804             });
23805             this.resizer.on('resize', function(r, w, h){
23806                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23807                 this.listWidth = w;
23808                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23809                 this.restrictHeight();
23810             }, this);
23811             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23812         }
23813         if(!this.editable){
23814             this.editable = true;
23815             this.setEditable(false);
23816         }  
23817         
23818         
23819         if (typeof(this.events.add.listeners) != 'undefined') {
23820             
23821             this.addicon = this.wrap.createChild(
23822                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23823        
23824             this.addicon.on('click', function(e) {
23825                 this.fireEvent('add', this);
23826             }, this);
23827         }
23828         if (typeof(this.events.edit.listeners) != 'undefined') {
23829             
23830             this.editicon = this.wrap.createChild(
23831                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23832             if (this.addicon) {
23833                 this.editicon.setStyle('margin-left', '40px');
23834             }
23835             this.editicon.on('click', function(e) {
23836                 
23837                 // we fire even  if inothing is selected..
23838                 this.fireEvent('edit', this, this.lastData );
23839                 
23840             }, this);
23841         }
23842         
23843         
23844         
23845     },
23846
23847     // private
23848     initEvents : function(){
23849         Roo.form.ComboBox.superclass.initEvents.call(this);
23850
23851         this.keyNav = new Roo.KeyNav(this.el, {
23852             "up" : function(e){
23853                 this.inKeyMode = true;
23854                 this.selectPrev();
23855             },
23856
23857             "down" : function(e){
23858                 if(!this.isExpanded()){
23859                     this.onTriggerClick();
23860                 }else{
23861                     this.inKeyMode = true;
23862                     this.selectNext();
23863                 }
23864             },
23865
23866             "enter" : function(e){
23867                 this.onViewClick();
23868                 //return true;
23869             },
23870
23871             "esc" : function(e){
23872                 this.collapse();
23873             },
23874
23875             "tab" : function(e){
23876                 this.onViewClick(false);
23877                 this.fireEvent("specialkey", this, e);
23878                 return true;
23879             },
23880
23881             scope : this,
23882
23883             doRelay : function(foo, bar, hname){
23884                 if(hname == 'down' || this.scope.isExpanded()){
23885                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23886                 }
23887                 return true;
23888             },
23889
23890             forceKeyDown: true
23891         });
23892         this.queryDelay = Math.max(this.queryDelay || 10,
23893                 this.mode == 'local' ? 10 : 250);
23894         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23895         if(this.typeAhead){
23896             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23897         }
23898         if(this.editable !== false){
23899             this.el.on("keyup", this.onKeyUp, this);
23900         }
23901         if(this.forceSelection){
23902             this.on('blur', this.doForce, this);
23903         }
23904     },
23905
23906     onDestroy : function(){
23907         if(this.view){
23908             this.view.setStore(null);
23909             this.view.el.removeAllListeners();
23910             this.view.el.remove();
23911             this.view.purgeListeners();
23912         }
23913         if(this.list){
23914             this.list.destroy();
23915         }
23916         if(this.store){
23917             this.store.un('beforeload', this.onBeforeLoad, this);
23918             this.store.un('load', this.onLoad, this);
23919             this.store.un('loadexception', this.onLoadException, this);
23920         }
23921         Roo.form.ComboBox.superclass.onDestroy.call(this);
23922     },
23923
23924     // private
23925     fireKey : function(e){
23926         if(e.isNavKeyPress() && !this.list.isVisible()){
23927             this.fireEvent("specialkey", this, e);
23928         }
23929     },
23930
23931     // private
23932     onResize: function(w, h){
23933         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23934         
23935         if(typeof w != 'number'){
23936             // we do not handle it!?!?
23937             return;
23938         }
23939         var tw = this.trigger.getWidth();
23940         tw += this.addicon ? this.addicon.getWidth() : 0;
23941         tw += this.editicon ? this.editicon.getWidth() : 0;
23942         var x = w - tw;
23943         this.el.setWidth( this.adjustWidth('input', x));
23944             
23945         this.trigger.setStyle('left', x+'px');
23946         
23947         if(this.list && this.listWidth === undefined){
23948             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23949             this.list.setWidth(lw);
23950             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23951         }
23952         
23953     
23954         
23955     },
23956
23957     /**
23958      * Allow or prevent the user from directly editing the field text.  If false is passed,
23959      * the user will only be able to select from the items defined in the dropdown list.  This method
23960      * is the runtime equivalent of setting the 'editable' config option at config time.
23961      * @param {Boolean} value True to allow the user to directly edit the field text
23962      */
23963     setEditable : function(value){
23964         if(value == this.editable){
23965             return;
23966         }
23967         this.editable = value;
23968         if(!value){
23969             this.el.dom.setAttribute('readOnly', true);
23970             this.el.on('mousedown', this.onTriggerClick,  this);
23971             this.el.addClass('x-combo-noedit');
23972         }else{
23973             this.el.dom.setAttribute('readOnly', false);
23974             this.el.un('mousedown', this.onTriggerClick,  this);
23975             this.el.removeClass('x-combo-noedit');
23976         }
23977     },
23978
23979     // private
23980     onBeforeLoad : function(){
23981         if(!this.hasFocus){
23982             return;
23983         }
23984         this.innerList.update(this.loadingText ?
23985                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23986         this.restrictHeight();
23987         this.selectedIndex = -1;
23988     },
23989
23990     // private
23991     onLoad : function(){
23992         if(!this.hasFocus){
23993             return;
23994         }
23995         if(this.store.getCount() > 0){
23996             this.expand();
23997             this.restrictHeight();
23998             if(this.lastQuery == this.allQuery){
23999                 if(this.editable){
24000                     this.el.dom.select();
24001                 }
24002                 if(!this.selectByValue(this.value, true)){
24003                     this.select(0, true);
24004                 }
24005             }else{
24006                 this.selectNext();
24007                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24008                     this.taTask.delay(this.typeAheadDelay);
24009                 }
24010             }
24011         }else{
24012             this.onEmptyResults();
24013         }
24014         //this.el.focus();
24015     },
24016     // private
24017     onLoadException : function()
24018     {
24019         this.collapse();
24020         Roo.log(this.store.reader.jsonData);
24021         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24022             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24023         }
24024         
24025         
24026     },
24027     // private
24028     onTypeAhead : function(){
24029         if(this.store.getCount() > 0){
24030             var r = this.store.getAt(0);
24031             var newValue = r.data[this.displayField];
24032             var len = newValue.length;
24033             var selStart = this.getRawValue().length;
24034             if(selStart != len){
24035                 this.setRawValue(newValue);
24036                 this.selectText(selStart, newValue.length);
24037             }
24038         }
24039     },
24040
24041     // private
24042     onSelect : function(record, index){
24043         if(this.fireEvent('beforeselect', this, record, index) !== false){
24044             this.setFromData(index > -1 ? record.data : false);
24045             this.collapse();
24046             this.fireEvent('select', this, record, index);
24047         }
24048     },
24049
24050     /**
24051      * Returns the currently selected field value or empty string if no value is set.
24052      * @return {String} value The selected value
24053      */
24054     getValue : function(){
24055         if(this.valueField){
24056             return typeof this.value != 'undefined' ? this.value : '';
24057         }else{
24058             return Roo.form.ComboBox.superclass.getValue.call(this);
24059         }
24060     },
24061
24062     /**
24063      * Clears any text/value currently set in the field
24064      */
24065     clearValue : function(){
24066         if(this.hiddenField){
24067             this.hiddenField.value = '';
24068         }
24069         this.value = '';
24070         this.setRawValue('');
24071         this.lastSelectionText = '';
24072         
24073     },
24074
24075     /**
24076      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24077      * will be displayed in the field.  If the value does not match the data value of an existing item,
24078      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24079      * Otherwise the field will be blank (although the value will still be set).
24080      * @param {String} value The value to match
24081      */
24082     setValue : function(v){
24083         var text = v;
24084         if(this.valueField){
24085             var r = this.findRecord(this.valueField, v);
24086             if(r){
24087                 text = r.data[this.displayField];
24088             }else if(this.valueNotFoundText !== undefined){
24089                 text = this.valueNotFoundText;
24090             }
24091         }
24092         this.lastSelectionText = text;
24093         if(this.hiddenField){
24094             this.hiddenField.value = v;
24095         }
24096         Roo.form.ComboBox.superclass.setValue.call(this, text);
24097         this.value = v;
24098     },
24099     /**
24100      * @property {Object} the last set data for the element
24101      */
24102     
24103     lastData : false,
24104     /**
24105      * Sets the value of the field based on a object which is related to the record format for the store.
24106      * @param {Object} value the value to set as. or false on reset?
24107      */
24108     setFromData : function(o){
24109         var dv = ''; // display value
24110         var vv = ''; // value value..
24111         this.lastData = o;
24112         if (this.displayField) {
24113             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24114         } else {
24115             // this is an error condition!!!
24116             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24117         }
24118         
24119         if(this.valueField){
24120             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24121         }
24122         if(this.hiddenField){
24123             this.hiddenField.value = vv;
24124             
24125             this.lastSelectionText = dv;
24126             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24127             this.value = vv;
24128             return;
24129         }
24130         // no hidden field.. - we store the value in 'value', but still display
24131         // display field!!!!
24132         this.lastSelectionText = dv;
24133         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24134         this.value = vv;
24135         
24136         
24137     },
24138     // private
24139     reset : function(){
24140         // overridden so that last data is reset..
24141         this.setValue(this.originalValue);
24142         this.clearInvalid();
24143         this.lastData = false;
24144         if (this.view) {
24145             this.view.clearSelections();
24146         }
24147     },
24148     // private
24149     findRecord : function(prop, value){
24150         var record;
24151         if(this.store.getCount() > 0){
24152             this.store.each(function(r){
24153                 if(r.data[prop] == value){
24154                     record = r;
24155                     return false;
24156                 }
24157                 return true;
24158             });
24159         }
24160         return record;
24161     },
24162     
24163     getName: function()
24164     {
24165         // returns hidden if it's set..
24166         if (!this.rendered) {return ''};
24167         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24168         
24169     },
24170     // private
24171     onViewMove : function(e, t){
24172         this.inKeyMode = false;
24173     },
24174
24175     // private
24176     onViewOver : function(e, t){
24177         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24178             return;
24179         }
24180         var item = this.view.findItemFromChild(t);
24181         if(item){
24182             var index = this.view.indexOf(item);
24183             this.select(index, false);
24184         }
24185     },
24186
24187     // private
24188     onViewClick : function(doFocus)
24189     {
24190         var index = this.view.getSelectedIndexes()[0];
24191         var r = this.store.getAt(index);
24192         if(r){
24193             this.onSelect(r, index);
24194         }
24195         if(doFocus !== false && !this.blockFocus){
24196             this.el.focus();
24197         }
24198     },
24199
24200     // private
24201     restrictHeight : function(){
24202         this.innerList.dom.style.height = '';
24203         var inner = this.innerList.dom;
24204         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24205         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24206         this.list.beginUpdate();
24207         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24208         this.list.alignTo(this.el, this.listAlign);
24209         this.list.endUpdate();
24210     },
24211
24212     // private
24213     onEmptyResults : function(){
24214         this.collapse();
24215     },
24216
24217     /**
24218      * Returns true if the dropdown list is expanded, else false.
24219      */
24220     isExpanded : function(){
24221         return this.list.isVisible();
24222     },
24223
24224     /**
24225      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24226      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24227      * @param {String} value The data value of the item to select
24228      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24229      * selected item if it is not currently in view (defaults to true)
24230      * @return {Boolean} True if the value matched an item in the list, else false
24231      */
24232     selectByValue : function(v, scrollIntoView){
24233         if(v !== undefined && v !== null){
24234             var r = this.findRecord(this.valueField || this.displayField, v);
24235             if(r){
24236                 this.select(this.store.indexOf(r), scrollIntoView);
24237                 return true;
24238             }
24239         }
24240         return false;
24241     },
24242
24243     /**
24244      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24245      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24246      * @param {Number} index The zero-based index of the list item to select
24247      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24248      * selected item if it is not currently in view (defaults to true)
24249      */
24250     select : function(index, scrollIntoView){
24251         this.selectedIndex = index;
24252         this.view.select(index);
24253         if(scrollIntoView !== false){
24254             var el = this.view.getNode(index);
24255             if(el){
24256                 this.innerList.scrollChildIntoView(el, false);
24257             }
24258         }
24259     },
24260
24261     // private
24262     selectNext : function(){
24263         var ct = this.store.getCount();
24264         if(ct > 0){
24265             if(this.selectedIndex == -1){
24266                 this.select(0);
24267             }else if(this.selectedIndex < ct-1){
24268                 this.select(this.selectedIndex+1);
24269             }
24270         }
24271     },
24272
24273     // private
24274     selectPrev : function(){
24275         var ct = this.store.getCount();
24276         if(ct > 0){
24277             if(this.selectedIndex == -1){
24278                 this.select(0);
24279             }else if(this.selectedIndex != 0){
24280                 this.select(this.selectedIndex-1);
24281             }
24282         }
24283     },
24284
24285     // private
24286     onKeyUp : function(e){
24287         if(this.editable !== false && !e.isSpecialKey()){
24288             this.lastKey = e.getKey();
24289             this.dqTask.delay(this.queryDelay);
24290         }
24291     },
24292
24293     // private
24294     validateBlur : function(){
24295         return !this.list || !this.list.isVisible();   
24296     },
24297
24298     // private
24299     initQuery : function(){
24300         this.doQuery(this.getRawValue());
24301     },
24302
24303     // private
24304     doForce : function(){
24305         if(this.el.dom.value.length > 0){
24306             this.el.dom.value =
24307                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24308              
24309         }
24310     },
24311
24312     /**
24313      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24314      * query allowing the query action to be canceled if needed.
24315      * @param {String} query The SQL query to execute
24316      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24317      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24318      * saved in the current store (defaults to false)
24319      */
24320     doQuery : function(q, forceAll){
24321         if(q === undefined || q === null){
24322             q = '';
24323         }
24324         var qe = {
24325             query: q,
24326             forceAll: forceAll,
24327             combo: this,
24328             cancel:false
24329         };
24330         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24331             return false;
24332         }
24333         q = qe.query;
24334         forceAll = qe.forceAll;
24335         if(forceAll === true || (q.length >= this.minChars)){
24336             if(this.lastQuery != q || this.alwaysQuery){
24337                 this.lastQuery = q;
24338                 if(this.mode == 'local'){
24339                     this.selectedIndex = -1;
24340                     if(forceAll){
24341                         this.store.clearFilter();
24342                     }else{
24343                         this.store.filter(this.displayField, q);
24344                     }
24345                     this.onLoad();
24346                 }else{
24347                     this.store.baseParams[this.queryParam] = q;
24348                     this.store.load({
24349                         params: this.getParams(q)
24350                     });
24351                     this.expand();
24352                 }
24353             }else{
24354                 this.selectedIndex = -1;
24355                 this.onLoad();   
24356             }
24357         }
24358     },
24359
24360     // private
24361     getParams : function(q){
24362         var p = {};
24363         //p[this.queryParam] = q;
24364         if(this.pageSize){
24365             p.start = 0;
24366             p.limit = this.pageSize;
24367         }
24368         return p;
24369     },
24370
24371     /**
24372      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24373      */
24374     collapse : function(){
24375         if(!this.isExpanded()){
24376             return;
24377         }
24378         this.list.hide();
24379         Roo.get(document).un('mousedown', this.collapseIf, this);
24380         Roo.get(document).un('mousewheel', this.collapseIf, this);
24381         if (!this.editable) {
24382             Roo.get(document).un('keydown', this.listKeyPress, this);
24383         }
24384         this.fireEvent('collapse', this);
24385     },
24386
24387     // private
24388     collapseIf : function(e){
24389         if(!e.within(this.wrap) && !e.within(this.list)){
24390             this.collapse();
24391         }
24392     },
24393
24394     /**
24395      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24396      */
24397     expand : function(){
24398         if(this.isExpanded() || !this.hasFocus){
24399             return;
24400         }
24401         this.list.alignTo(this.el, this.listAlign);
24402         this.list.show();
24403         Roo.get(document).on('mousedown', this.collapseIf, this);
24404         Roo.get(document).on('mousewheel', this.collapseIf, this);
24405         if (!this.editable) {
24406             Roo.get(document).on('keydown', this.listKeyPress, this);
24407         }
24408         
24409         this.fireEvent('expand', this);
24410     },
24411
24412     // private
24413     // Implements the default empty TriggerField.onTriggerClick function
24414     onTriggerClick : function(){
24415         if(this.disabled){
24416             return;
24417         }
24418         if(this.isExpanded()){
24419             this.collapse();
24420             if (!this.blockFocus) {
24421                 this.el.focus();
24422             }
24423             
24424         }else {
24425             this.hasFocus = true;
24426             if(this.triggerAction == 'all') {
24427                 this.doQuery(this.allQuery, true);
24428             } else {
24429                 this.doQuery(this.getRawValue());
24430             }
24431             if (!this.blockFocus) {
24432                 this.el.focus();
24433             }
24434         }
24435     },
24436     listKeyPress : function(e)
24437     {
24438         //Roo.log('listkeypress');
24439         // scroll to first matching element based on key pres..
24440         if (e.isSpecialKey()) {
24441             return false;
24442         }
24443         var k = String.fromCharCode(e.getKey()).toUpperCase();
24444         //Roo.log(k);
24445         var match  = false;
24446         var csel = this.view.getSelectedNodes();
24447         var cselitem = false;
24448         if (csel.length) {
24449             var ix = this.view.indexOf(csel[0]);
24450             cselitem  = this.store.getAt(ix);
24451             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24452                 cselitem = false;
24453             }
24454             
24455         }
24456         
24457         this.store.each(function(v) { 
24458             if (cselitem) {
24459                 // start at existing selection.
24460                 if (cselitem.id == v.id) {
24461                     cselitem = false;
24462                 }
24463                 return;
24464             }
24465                 
24466             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24467                 match = this.store.indexOf(v);
24468                 return false;
24469             }
24470         }, this);
24471         
24472         if (match === false) {
24473             return true; // no more action?
24474         }
24475         // scroll to?
24476         this.view.select(match);
24477         var sn = Roo.get(this.view.getSelectedNodes()[0])
24478         sn.scrollIntoView(sn.dom.parentNode, false);
24479     }
24480
24481     /** 
24482     * @cfg {Boolean} grow 
24483     * @hide 
24484     */
24485     /** 
24486     * @cfg {Number} growMin 
24487     * @hide 
24488     */
24489     /** 
24490     * @cfg {Number} growMax 
24491     * @hide 
24492     */
24493     /**
24494      * @hide
24495      * @method autoSize
24496      */
24497 });/*
24498  * Copyright(c) 2010-2012, Roo J Solutions Limited
24499  *
24500  * Licence LGPL
24501  *
24502  */
24503
24504 /**
24505  * @class Roo.form.ComboBoxArray
24506  * @extends Roo.form.TextField
24507  * A facebook style adder... for lists of email / people / countries  etc...
24508  * pick multiple items from a combo box, and shows each one.
24509  *
24510  *  Fred [x]  Brian [x]  [Pick another |v]
24511  *
24512  *
24513  *  For this to work: it needs various extra information
24514  *    - normal combo problay has
24515  *      name, hiddenName
24516  *    + displayField, valueField
24517  *
24518  *    For our purpose...
24519  *
24520  *
24521  *   If we change from 'extends' to wrapping...
24522  *   
24523  *  
24524  *
24525  
24526  
24527  * @constructor
24528  * Create a new ComboBoxArray.
24529  * @param {Object} config Configuration options
24530  */
24531  
24532
24533 Roo.form.ComboBoxArray = function(config)
24534 {
24535     
24536     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24537     
24538     this.items = new Roo.util.MixedCollection(false);
24539     
24540     // construct the child combo...
24541     
24542     
24543     
24544     
24545    
24546     
24547 }
24548
24549  
24550 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24551
24552     /**
24553      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24554      */
24555     
24556     lastData : false,
24557     
24558     // behavies liek a hiddne field
24559     inputType:      'hidden',
24560     /**
24561      * @cfg {Number} width The width of the box that displays the selected element
24562      */ 
24563     width:          300,
24564
24565     
24566     
24567     /**
24568      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24569      */
24570     name : false,
24571     /**
24572      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24573      */
24574     hiddenName : false,
24575     
24576     
24577     // private the array of items that are displayed..
24578     items  : false,
24579     // private - the hidden field el.
24580     hiddenEl : false,
24581     // private - the filed el..
24582     el : false,
24583     
24584     //validateValue : function() { return true; }, // all values are ok!
24585     //onAddClick: function() { },
24586     
24587     onRender : function(ct, position) 
24588     {
24589         
24590         // create the standard hidden element
24591         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24592         
24593         
24594         // give fake names to child combo;
24595         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24596         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24597         
24598         this.combo = Roo.factory(this.combo, Roo.form);
24599         this.combo.onRender(ct, position);
24600         if (typeof(this.combo.width) != 'undefined') {
24601             this.combo.onResize(this.combo.width,0);
24602         }
24603         
24604         this.combo.initEvents();
24605         
24606         // assigned so form know we need to do this..
24607         this.store          = this.combo.store;
24608         this.valueField     = this.combo.valueField;
24609         this.displayField   = this.combo.displayField ;
24610         
24611         
24612         this.combo.wrap.addClass('x-cbarray-grp');
24613         
24614         var cbwrap = this.combo.wrap.createChild(
24615             {tag: 'div', cls: 'x-cbarray-cb'},
24616             this.combo.el.dom
24617         );
24618         
24619              
24620         this.hiddenEl = this.combo.wrap.createChild({
24621             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24622         });
24623         this.el = this.combo.wrap.createChild({
24624             tag: 'input',  type:'hidden' , name: this.name, value : ''
24625         });
24626          //   this.el.dom.removeAttribute("name");
24627         
24628         
24629         this.outerWrap = this.combo.wrap;
24630         this.wrap = cbwrap;
24631         
24632         this.outerWrap.setWidth(this.width);
24633         this.outerWrap.dom.removeChild(this.el.dom);
24634         
24635         this.wrap.dom.appendChild(this.el.dom);
24636         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24637         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24638         
24639         this.combo.trigger.setStyle('position','relative');
24640         this.combo.trigger.setStyle('left', '0px');
24641         this.combo.trigger.setStyle('top', '2px');
24642         
24643         this.combo.el.setStyle('vertical-align', 'text-bottom');
24644         
24645         //this.trigger.setStyle('vertical-align', 'top');
24646         
24647         // this should use the code from combo really... on('add' ....)
24648         if (this.adder) {
24649             
24650         
24651             this.adder = this.outerWrap.createChild(
24652                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24653             var _t = this;
24654             this.adder.on('click', function(e) {
24655                 _t.fireEvent('adderclick', this, e);
24656             }, _t);
24657         }
24658         //var _t = this;
24659         //this.adder.on('click', this.onAddClick, _t);
24660         
24661         
24662         this.combo.on('select', function(cb, rec, ix) {
24663             this.addItem(rec.data);
24664             
24665             cb.setValue('');
24666             cb.el.dom.value = '';
24667             //cb.lastData = rec.data;
24668             // add to list
24669             
24670         }, this);
24671         
24672         
24673     },
24674     
24675     
24676     getName: function()
24677     {
24678         // returns hidden if it's set..
24679         if (!this.rendered) {return ''};
24680         return  this.hiddenName ? this.hiddenName : this.name;
24681         
24682     },
24683     
24684     
24685     onResize: function(w, h){
24686         
24687         return;
24688         // not sure if this is needed..
24689         //this.combo.onResize(w,h);
24690         
24691         if(typeof w != 'number'){
24692             // we do not handle it!?!?
24693             return;
24694         }
24695         var tw = this.combo.trigger.getWidth();
24696         tw += this.addicon ? this.addicon.getWidth() : 0;
24697         tw += this.editicon ? this.editicon.getWidth() : 0;
24698         var x = w - tw;
24699         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24700             
24701         this.combo.trigger.setStyle('left', '0px');
24702         
24703         if(this.list && this.listWidth === undefined){
24704             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24705             this.list.setWidth(lw);
24706             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24707         }
24708         
24709     
24710         
24711     },
24712     
24713     addItem: function(rec)
24714     {
24715         var valueField = this.combo.valueField;
24716         var displayField = this.combo.displayField;
24717         if (this.items.indexOfKey(rec[valueField]) > -1) {
24718             //console.log("GOT " + rec.data.id);
24719             return;
24720         }
24721         
24722         var x = new Roo.form.ComboBoxArray.Item({
24723             //id : rec[this.idField],
24724             data : rec,
24725             displayField : displayField ,
24726             tipField : displayField ,
24727             cb : this
24728         });
24729         // use the 
24730         this.items.add(rec[valueField],x);
24731         // add it before the element..
24732         this.updateHiddenEl();
24733         x.render(this.outerWrap, this.wrap.dom);
24734         // add the image handler..
24735     },
24736     
24737     updateHiddenEl : function()
24738     {
24739         this.validate();
24740         if (!this.hiddenEl) {
24741             return;
24742         }
24743         var ar = [];
24744         var idField = this.combo.valueField;
24745         
24746         this.items.each(function(f) {
24747             ar.push(f.data[idField]);
24748            
24749         });
24750         this.hiddenEl.dom.value = ar.join(',');
24751         this.validate();
24752     },
24753     
24754     reset : function()
24755     {
24756         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24757         this.items.each(function(f) {
24758            f.remove(); 
24759         });
24760         this.el.dom.value = '';
24761         if (this.hiddenEl) {
24762             this.hiddenEl.dom.value = '';
24763         }
24764         
24765     },
24766     getValue: function()
24767     {
24768         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24769     },
24770     setValue: function(v) // not a valid action - must use addItems..
24771     {
24772          
24773         this.reset();
24774         
24775         
24776         
24777         if (this.store.isLocal && (typeof(v) == 'string')) {
24778             // then we can use the store to find the values..
24779             // comma seperated at present.. this needs to allow JSON based encoding..
24780             this.hiddenEl.value  = v;
24781             var v_ar = [];
24782             Roo.each(v.split(','), function(k) {
24783                 Roo.log("CHECK " + this.valueField + ',' + k);
24784                 var li = this.store.query(this.valueField, k);
24785                 if (!li.length) {
24786                     return;
24787                 }
24788                 var add = {};
24789                 add[this.valueField] = k;
24790                 add[this.displayField] = li.item(0).data[this.displayField];
24791                 
24792                 this.addItem(add);
24793             }, this) 
24794              
24795         }
24796         if (typeof(v) == 'object') {
24797             // then let's assume it's an array of objects..
24798             Roo.each(v, function(l) {
24799                 this.addItem(l);
24800             }, this);
24801              
24802         }
24803         
24804         
24805     },
24806     setFromData: function(v)
24807     {
24808         // this recieves an object, if setValues is called.
24809         this.reset();
24810         this.el.dom.value = v[this.displayField];
24811         this.hiddenEl.dom.value = v[this.valueField];
24812         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24813             return;
24814         }
24815         var kv = v[this.valueField];
24816         var dv = v[this.displayField];
24817         kv = typeof(kv) != 'string' ? '' : kv;
24818         dv = typeof(dv) != 'string' ? '' : dv;
24819         
24820         
24821         var keys = kv.split(',');
24822         var display = dv.split(',');
24823         for (var i = 0 ; i < keys.length; i++) {
24824             
24825             add = {};
24826             add[this.valueField] = keys[i];
24827             add[this.displayField] = display[i];
24828             this.addItem(add);
24829         }
24830       
24831         
24832     },
24833     
24834     
24835     validateValue : function(value){
24836         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24837         
24838     }
24839     
24840 });
24841
24842
24843
24844 /**
24845  * @class Roo.form.ComboBoxArray.Item
24846  * @extends Roo.BoxComponent
24847  * A selected item in the list
24848  *  Fred [x]  Brian [x]  [Pick another |v]
24849  * 
24850  * @constructor
24851  * Create a new item.
24852  * @param {Object} config Configuration options
24853  */
24854  
24855 Roo.form.ComboBoxArray.Item = function(config) {
24856     config.id = Roo.id();
24857     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24858 }
24859
24860 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24861     data : {},
24862     cb: false,
24863     displayField : false,
24864     tipField : false,
24865     
24866     
24867     defaultAutoCreate : {
24868         tag: 'div',
24869         cls: 'x-cbarray-item',
24870         cn : [ 
24871             { tag: 'div' },
24872             {
24873                 tag: 'img',
24874                 width:16,
24875                 height : 16,
24876                 src : Roo.BLANK_IMAGE_URL ,
24877                 align: 'center'
24878             }
24879         ]
24880         
24881     },
24882     
24883  
24884     onRender : function(ct, position)
24885     {
24886         Roo.form.Field.superclass.onRender.call(this, ct, position);
24887         
24888         if(!this.el){
24889             var cfg = this.getAutoCreate();
24890             this.el = ct.createChild(cfg, position);
24891         }
24892         
24893         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24894         
24895         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24896             this.cb.renderer(this.data) :
24897             String.format('{0}',this.data[this.displayField]);
24898         
24899             
24900         this.el.child('div').dom.setAttribute('qtip',
24901                         String.format('{0}',this.data[this.tipField])
24902         );
24903         
24904         this.el.child('img').on('click', this.remove, this);
24905         
24906     },
24907    
24908     remove : function()
24909     {
24910         
24911         this.cb.items.remove(this);
24912         this.el.child('img').un('click', this.remove, this);
24913         this.el.remove();
24914         this.cb.updateHiddenEl();
24915     }
24916     
24917     
24918 });/*
24919  * Based on:
24920  * Ext JS Library 1.1.1
24921  * Copyright(c) 2006-2007, Ext JS, LLC.
24922  *
24923  * Originally Released Under LGPL - original licence link has changed is not relivant.
24924  *
24925  * Fork - LGPL
24926  * <script type="text/javascript">
24927  */
24928 /**
24929  * @class Roo.form.Checkbox
24930  * @extends Roo.form.Field
24931  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24932  * @constructor
24933  * Creates a new Checkbox
24934  * @param {Object} config Configuration options
24935  */
24936 Roo.form.Checkbox = function(config){
24937     Roo.form.Checkbox.superclass.constructor.call(this, config);
24938     this.addEvents({
24939         /**
24940          * @event check
24941          * Fires when the checkbox is checked or unchecked.
24942              * @param {Roo.form.Checkbox} this This checkbox
24943              * @param {Boolean} checked The new checked value
24944              */
24945         check : true
24946     });
24947 };
24948
24949 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24950     /**
24951      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24952      */
24953     focusClass : undefined,
24954     /**
24955      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24956      */
24957     fieldClass: "x-form-field",
24958     /**
24959      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24960      */
24961     checked: false,
24962     /**
24963      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24964      * {tag: "input", type: "checkbox", autocomplete: "off"})
24965      */
24966     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24967     /**
24968      * @cfg {String} boxLabel The text that appears beside the checkbox
24969      */
24970     boxLabel : "",
24971     /**
24972      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24973      */  
24974     inputValue : '1',
24975     /**
24976      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24977      */
24978      valueOff: '0', // value when not checked..
24979
24980     actionMode : 'viewEl', 
24981     //
24982     // private
24983     itemCls : 'x-menu-check-item x-form-item',
24984     groupClass : 'x-menu-group-item',
24985     inputType : 'hidden',
24986     
24987     
24988     inSetChecked: false, // check that we are not calling self...
24989     
24990     inputElement: false, // real input element?
24991     basedOn: false, // ????
24992     
24993     isFormField: true, // not sure where this is needed!!!!
24994
24995     onResize : function(){
24996         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24997         if(!this.boxLabel){
24998             this.el.alignTo(this.wrap, 'c-c');
24999         }
25000     },
25001
25002     initEvents : function(){
25003         Roo.form.Checkbox.superclass.initEvents.call(this);
25004         this.el.on("click", this.onClick,  this);
25005         this.el.on("change", this.onClick,  this);
25006     },
25007
25008
25009     getResizeEl : function(){
25010         return this.wrap;
25011     },
25012
25013     getPositionEl : function(){
25014         return this.wrap;
25015     },
25016
25017     // private
25018     onRender : function(ct, position){
25019         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25020         /*
25021         if(this.inputValue !== undefined){
25022             this.el.dom.value = this.inputValue;
25023         }
25024         */
25025         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25026         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25027         var viewEl = this.wrap.createChild({ 
25028             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25029         this.viewEl = viewEl;   
25030         this.wrap.on('click', this.onClick,  this); 
25031         
25032         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25033         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25034         
25035         
25036         
25037         if(this.boxLabel){
25038             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25039         //    viewEl.on('click', this.onClick,  this); 
25040         }
25041         //if(this.checked){
25042             this.setChecked(this.checked);
25043         //}else{
25044             //this.checked = this.el.dom;
25045         //}
25046
25047     },
25048
25049     // private
25050     initValue : Roo.emptyFn,
25051
25052     /**
25053      * Returns the checked state of the checkbox.
25054      * @return {Boolean} True if checked, else false
25055      */
25056     getValue : function(){
25057         if(this.el){
25058             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25059         }
25060         return this.valueOff;
25061         
25062     },
25063
25064         // private
25065     onClick : function(){ 
25066         this.setChecked(!this.checked);
25067
25068         //if(this.el.dom.checked != this.checked){
25069         //    this.setValue(this.el.dom.checked);
25070        // }
25071     },
25072
25073     /**
25074      * Sets the checked state of the checkbox.
25075      * On is always based on a string comparison between inputValue and the param.
25076      * @param {Boolean/String} value - the value to set 
25077      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25078      */
25079     setValue : function(v,suppressEvent){
25080         
25081         
25082         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25083         //if(this.el && this.el.dom){
25084         //    this.el.dom.checked = this.checked;
25085         //    this.el.dom.defaultChecked = this.checked;
25086         //}
25087         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25088         //this.fireEvent("check", this, this.checked);
25089     },
25090     // private..
25091     setChecked : function(state,suppressEvent)
25092     {
25093         if (this.inSetChecked) {
25094             this.checked = state;
25095             return;
25096         }
25097         
25098     
25099         if(this.wrap){
25100             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25101         }
25102         this.checked = state;
25103         if(suppressEvent !== true){
25104             this.fireEvent('check', this, state);
25105         }
25106         this.inSetChecked = true;
25107         this.el.dom.value = state ? this.inputValue : this.valueOff;
25108         this.inSetChecked = false;
25109         
25110     },
25111     // handle setting of hidden value by some other method!!?!?
25112     setFromHidden: function()
25113     {
25114         if(!this.el){
25115             return;
25116         }
25117         //console.log("SET FROM HIDDEN");
25118         //alert('setFrom hidden');
25119         this.setValue(this.el.dom.value);
25120     },
25121     
25122     onDestroy : function()
25123     {
25124         if(this.viewEl){
25125             Roo.get(this.viewEl).remove();
25126         }
25127          
25128         Roo.form.Checkbox.superclass.onDestroy.call(this);
25129     }
25130
25131 });/*
25132  * Based on:
25133  * Ext JS Library 1.1.1
25134  * Copyright(c) 2006-2007, Ext JS, LLC.
25135  *
25136  * Originally Released Under LGPL - original licence link has changed is not relivant.
25137  *
25138  * Fork - LGPL
25139  * <script type="text/javascript">
25140  */
25141  
25142 /**
25143  * @class Roo.form.Radio
25144  * @extends Roo.form.Checkbox
25145  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25146  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25147  * @constructor
25148  * Creates a new Radio
25149  * @param {Object} config Configuration options
25150  */
25151 Roo.form.Radio = function(){
25152     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25153 };
25154 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25155     inputType: 'radio',
25156
25157     /**
25158      * If this radio is part of a group, it will return the selected value
25159      * @return {String}
25160      */
25161     getGroupValue : function(){
25162         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25163     },
25164     
25165     
25166     onRender : function(ct, position){
25167         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25168         
25169         if(this.inputValue !== undefined){
25170             this.el.dom.value = this.inputValue;
25171         }
25172          
25173         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25174         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25175         //var viewEl = this.wrap.createChild({ 
25176         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25177         //this.viewEl = viewEl;   
25178         //this.wrap.on('click', this.onClick,  this); 
25179         
25180         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25181         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
25182         
25183         
25184         
25185         if(this.boxLabel){
25186             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25187         //    viewEl.on('click', this.onClick,  this); 
25188         }
25189          if(this.checked){
25190             this.el.dom.checked =   'checked' ;
25191         }
25192          
25193     } 
25194     
25195     
25196 });//<script type="text/javascript">
25197
25198 /*
25199  * Ext JS Library 1.1.1
25200  * Copyright(c) 2006-2007, Ext JS, LLC.
25201  * licensing@extjs.com
25202  * 
25203  * http://www.extjs.com/license
25204  */
25205  
25206  /*
25207   * 
25208   * Known bugs:
25209   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25210   * - IE ? - no idea how much works there.
25211   * 
25212   * 
25213   * 
25214   */
25215  
25216
25217 /**
25218  * @class Ext.form.HtmlEditor
25219  * @extends Ext.form.Field
25220  * Provides a lightweight HTML Editor component.
25221  *
25222  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25223  * 
25224  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25225  * supported by this editor.</b><br/><br/>
25226  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25227  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25228  */
25229 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25230       /**
25231      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25232      */
25233     toolbars : false,
25234     /**
25235      * @cfg {String} createLinkText The default text for the create link prompt
25236      */
25237     createLinkText : 'Please enter the URL for the link:',
25238     /**
25239      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25240      */
25241     defaultLinkValue : 'http:/'+'/',
25242    
25243      /**
25244      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25245      *                        Roo.resizable.
25246      */
25247     resizable : false,
25248      /**
25249      * @cfg {Number} height (in pixels)
25250      */   
25251     height: 300,
25252    /**
25253      * @cfg {Number} width (in pixels)
25254      */   
25255     width: 500,
25256     
25257     /**
25258      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25259      * 
25260      */
25261     stylesheets: false,
25262     
25263     // id of frame..
25264     frameId: false,
25265     
25266     // private properties
25267     validationEvent : false,
25268     deferHeight: true,
25269     initialized : false,
25270     activated : false,
25271     sourceEditMode : false,
25272     onFocus : Roo.emptyFn,
25273     iframePad:3,
25274     hideMode:'offsets',
25275     
25276     defaultAutoCreate : { // modified by initCompnoent..
25277         tag: "textarea",
25278         style:"width:500px;height:300px;",
25279         autocomplete: "off"
25280     },
25281
25282     // private
25283     initComponent : function(){
25284         this.addEvents({
25285             /**
25286              * @event initialize
25287              * Fires when the editor is fully initialized (including the iframe)
25288              * @param {HtmlEditor} this
25289              */
25290             initialize: true,
25291             /**
25292              * @event activate
25293              * Fires when the editor is first receives the focus. Any insertion must wait
25294              * until after this event.
25295              * @param {HtmlEditor} this
25296              */
25297             activate: true,
25298              /**
25299              * @event beforesync
25300              * Fires before the textarea is updated with content from the editor iframe. Return false
25301              * to cancel the sync.
25302              * @param {HtmlEditor} this
25303              * @param {String} html
25304              */
25305             beforesync: true,
25306              /**
25307              * @event beforepush
25308              * Fires before the iframe editor is updated with content from the textarea. Return false
25309              * to cancel the push.
25310              * @param {HtmlEditor} this
25311              * @param {String} html
25312              */
25313             beforepush: true,
25314              /**
25315              * @event sync
25316              * Fires when the textarea is updated with content from the editor iframe.
25317              * @param {HtmlEditor} this
25318              * @param {String} html
25319              */
25320             sync: true,
25321              /**
25322              * @event push
25323              * Fires when the iframe editor is updated with content from the textarea.
25324              * @param {HtmlEditor} this
25325              * @param {String} html
25326              */
25327             push: true,
25328              /**
25329              * @event editmodechange
25330              * Fires when the editor switches edit modes
25331              * @param {HtmlEditor} this
25332              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25333              */
25334             editmodechange: true,
25335             /**
25336              * @event editorevent
25337              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25338              * @param {HtmlEditor} this
25339              */
25340             editorevent: true
25341         });
25342         this.defaultAutoCreate =  {
25343             tag: "textarea",
25344             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25345             autocomplete: "off"
25346         };
25347     },
25348
25349     /**
25350      * Protected method that will not generally be called directly. It
25351      * is called when the editor creates its toolbar. Override this method if you need to
25352      * add custom toolbar buttons.
25353      * @param {HtmlEditor} editor
25354      */
25355     createToolbar : function(editor){
25356         if (!editor.toolbars || !editor.toolbars.length) {
25357             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25358         }
25359         
25360         for (var i =0 ; i < editor.toolbars.length;i++) {
25361             editor.toolbars[i] = Roo.factory(
25362                     typeof(editor.toolbars[i]) == 'string' ?
25363                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25364                 Roo.form.HtmlEditor);
25365             editor.toolbars[i].init(editor);
25366         }
25367          
25368         
25369     },
25370
25371     /**
25372      * Protected method that will not generally be called directly. It
25373      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25374      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25375      */
25376     getDocMarkup : function(){
25377         // body styles..
25378         var st = '';
25379         if (this.stylesheets === false) {
25380             
25381             Roo.get(document.head).select('style').each(function(node) {
25382                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25383             });
25384             
25385             Roo.get(document.head).select('link').each(function(node) { 
25386                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25387             });
25388             
25389         } else if (!this.stylesheets.length) {
25390                 // simple..
25391                 st = '<style type="text/css">' +
25392                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25393                    '</style>';
25394         } else {
25395             Roo.each(this.stylesheets, function(s) {
25396                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25397             });
25398             
25399         }
25400         
25401         st +=  '<style type="text/css">' +
25402             'IMG { cursor: pointer } ' +
25403         '</style>';
25404
25405         
25406         return '<html><head>' + st  +
25407             //<style type="text/css">' +
25408             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25409             //'</style>' +
25410             ' </head><body class="roo-htmleditor-body"></body></html>';
25411     },
25412
25413     // private
25414     onRender : function(ct, position)
25415     {
25416         var _t = this;
25417         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25418         this.el.dom.style.border = '0 none';
25419         this.el.dom.setAttribute('tabIndex', -1);
25420         this.el.addClass('x-hidden');
25421         if(Roo.isIE){ // fix IE 1px bogus margin
25422             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25423         }
25424         this.wrap = this.el.wrap({
25425             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25426         });
25427         
25428         if (this.resizable) {
25429             this.resizeEl = new Roo.Resizable(this.wrap, {
25430                 pinned : true,
25431                 wrap: true,
25432                 dynamic : true,
25433                 minHeight : this.height,
25434                 height: this.height,
25435                 handles : this.resizable,
25436                 width: this.width,
25437                 listeners : {
25438                     resize : function(r, w, h) {
25439                         _t.onResize(w,h); // -something
25440                     }
25441                 }
25442             });
25443             
25444         }
25445
25446         this.frameId = Roo.id();
25447         
25448         this.createToolbar(this);
25449         
25450       
25451         
25452         var iframe = this.wrap.createChild({
25453             tag: 'iframe',
25454             id: this.frameId,
25455             name: this.frameId,
25456             frameBorder : 'no',
25457             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25458         }, this.el
25459         );
25460         
25461        // console.log(iframe);
25462         //this.wrap.dom.appendChild(iframe);
25463
25464         this.iframe = iframe.dom;
25465
25466          this.assignDocWin();
25467         
25468         this.doc.designMode = 'on';
25469        
25470         this.doc.open();
25471         this.doc.write(this.getDocMarkup());
25472         this.doc.close();
25473
25474         
25475         var task = { // must defer to wait for browser to be ready
25476             run : function(){
25477                 //console.log("run task?" + this.doc.readyState);
25478                 this.assignDocWin();
25479                 if(this.doc.body || this.doc.readyState == 'complete'){
25480                     try {
25481                         this.doc.designMode="on";
25482                     } catch (e) {
25483                         return;
25484                     }
25485                     Roo.TaskMgr.stop(task);
25486                     this.initEditor.defer(10, this);
25487                 }
25488             },
25489             interval : 10,
25490             duration:10000,
25491             scope: this
25492         };
25493         Roo.TaskMgr.start(task);
25494
25495         if(!this.width){
25496             this.setSize(this.wrap.getSize());
25497         }
25498         if (this.resizeEl) {
25499             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25500             // should trigger onReize..
25501         }
25502     },
25503
25504     // private
25505     onResize : function(w, h)
25506     {
25507         //Roo.log('resize: ' +w + ',' + h );
25508         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25509         if(this.el && this.iframe){
25510             if(typeof w == 'number'){
25511                 var aw = w - this.wrap.getFrameWidth('lr');
25512                 this.el.setWidth(this.adjustWidth('textarea', aw));
25513                 this.iframe.style.width = aw + 'px';
25514             }
25515             if(typeof h == 'number'){
25516                 var tbh = 0;
25517                 for (var i =0; i < this.toolbars.length;i++) {
25518                     // fixme - ask toolbars for heights?
25519                     tbh += this.toolbars[i].tb.el.getHeight();
25520                     if (this.toolbars[i].footer) {
25521                         tbh += this.toolbars[i].footer.el.getHeight();
25522                     }
25523                 }
25524                 
25525                 
25526                 
25527                 
25528                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25529                 ah -= 5; // knock a few pixes off for look..
25530                 this.el.setHeight(this.adjustWidth('textarea', ah));
25531                 this.iframe.style.height = ah + 'px';
25532                 if(this.doc){
25533                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25534                 }
25535             }
25536         }
25537     },
25538
25539     /**
25540      * Toggles the editor between standard and source edit mode.
25541      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25542      */
25543     toggleSourceEdit : function(sourceEditMode){
25544         
25545         this.sourceEditMode = sourceEditMode === true;
25546         
25547         if(this.sourceEditMode){
25548 //            Roo.log('in');
25549 //            Roo.log(this.syncValue());
25550             this.syncValue();
25551             this.iframe.className = 'x-hidden';
25552             this.el.removeClass('x-hidden');
25553             this.el.dom.removeAttribute('tabIndex');
25554             this.el.focus();
25555         }else{
25556 //            Roo.log('out')
25557 //            Roo.log(this.pushValue()); 
25558             this.pushValue();
25559             this.iframe.className = '';
25560             this.el.addClass('x-hidden');
25561             this.el.dom.setAttribute('tabIndex', -1);
25562             this.deferFocus();
25563         }
25564         this.setSize(this.wrap.getSize());
25565         this.fireEvent('editmodechange', this, this.sourceEditMode);
25566     },
25567
25568     // private used internally
25569     createLink : function(){
25570         var url = prompt(this.createLinkText, this.defaultLinkValue);
25571         if(url && url != 'http:/'+'/'){
25572             this.relayCmd('createlink', url);
25573         }
25574     },
25575
25576     // private (for BoxComponent)
25577     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25578
25579     // private (for BoxComponent)
25580     getResizeEl : function(){
25581         return this.wrap;
25582     },
25583
25584     // private (for BoxComponent)
25585     getPositionEl : function(){
25586         return this.wrap;
25587     },
25588
25589     // private
25590     initEvents : function(){
25591         this.originalValue = this.getValue();
25592     },
25593
25594     /**
25595      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25596      * @method
25597      */
25598     markInvalid : Roo.emptyFn,
25599     /**
25600      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25601      * @method
25602      */
25603     clearInvalid : Roo.emptyFn,
25604
25605     setValue : function(v){
25606         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25607         this.pushValue();
25608     },
25609
25610     /**
25611      * Protected method that will not generally be called directly. If you need/want
25612      * custom HTML cleanup, this is the method you should override.
25613      * @param {String} html The HTML to be cleaned
25614      * return {String} The cleaned HTML
25615      */
25616     cleanHtml : function(html){
25617         html = String(html);
25618         if(html.length > 5){
25619             if(Roo.isSafari){ // strip safari nonsense
25620                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25621             }
25622         }
25623         if(html == '&nbsp;'){
25624             html = '';
25625         }
25626         return html;
25627     },
25628
25629     /**
25630      * Protected method that will not generally be called directly. Syncs the contents
25631      * of the editor iframe with the textarea.
25632      */
25633     syncValue : function(){
25634         if(this.initialized){
25635             var bd = (this.doc.body || this.doc.documentElement);
25636             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25637             var html = bd.innerHTML;
25638             if(Roo.isSafari){
25639                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25640                 var m = bs.match(/text-align:(.*?);/i);
25641                 if(m && m[1]){
25642                     html = '<div style="'+m[0]+'">' + html + '</div>';
25643                 }
25644             }
25645             html = this.cleanHtml(html);
25646             // fix up the special chars.. normaly like back quotes in word...
25647             // however we do not want to do this with chinese..
25648             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25649                 var cc = b.charCodeAt();
25650                 if (
25651                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25652                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25653                     (cc >= 0xf900 && cc < 0xfb00 )
25654                 ) {
25655                         return b;
25656                 }
25657                 return "&#"+cc+";" 
25658             });
25659             if(this.fireEvent('beforesync', this, html) !== false){
25660                 this.el.dom.value = html;
25661                 this.fireEvent('sync', this, html);
25662             }
25663         }
25664     },
25665
25666     /**
25667      * Protected method that will not generally be called directly. Pushes the value of the textarea
25668      * into the iframe editor.
25669      */
25670     pushValue : function(){
25671         if(this.initialized){
25672             var v = this.el.dom.value;
25673             
25674             if(v.length < 1){
25675                 v = '&#160;';
25676             }
25677             
25678             if(this.fireEvent('beforepush', this, v) !== false){
25679                 var d = (this.doc.body || this.doc.documentElement);
25680                 d.innerHTML = v;
25681                 this.cleanUpPaste();
25682                 this.el.dom.value = d.innerHTML;
25683                 this.fireEvent('push', this, v);
25684             }
25685         }
25686     },
25687
25688     // private
25689     deferFocus : function(){
25690         this.focus.defer(10, this);
25691     },
25692
25693     // doc'ed in Field
25694     focus : function(){
25695         if(this.win && !this.sourceEditMode){
25696             this.win.focus();
25697         }else{
25698             this.el.focus();
25699         }
25700     },
25701     
25702     assignDocWin: function()
25703     {
25704         var iframe = this.iframe;
25705         
25706          if(Roo.isIE){
25707             this.doc = iframe.contentWindow.document;
25708             this.win = iframe.contentWindow;
25709         } else {
25710             if (!Roo.get(this.frameId)) {
25711                 return;
25712             }
25713             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25714             this.win = Roo.get(this.frameId).dom.contentWindow;
25715         }
25716     },
25717     
25718     // private
25719     initEditor : function(){
25720         //console.log("INIT EDITOR");
25721         this.assignDocWin();
25722         
25723         
25724         
25725         this.doc.designMode="on";
25726         this.doc.open();
25727         this.doc.write(this.getDocMarkup());
25728         this.doc.close();
25729         
25730         var dbody = (this.doc.body || this.doc.documentElement);
25731         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25732         // this copies styles from the containing element into thsi one..
25733         // not sure why we need all of this..
25734         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25735         ss['background-attachment'] = 'fixed'; // w3c
25736         dbody.bgProperties = 'fixed'; // ie
25737         Roo.DomHelper.applyStyles(dbody, ss);
25738         Roo.EventManager.on(this.doc, {
25739             //'mousedown': this.onEditorEvent,
25740             'mouseup': this.onEditorEvent,
25741             'dblclick': this.onEditorEvent,
25742             'click': this.onEditorEvent,
25743             'keyup': this.onEditorEvent,
25744             buffer:100,
25745             scope: this
25746         });
25747         if(Roo.isGecko){
25748             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25749         }
25750         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25751             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25752         }
25753         this.initialized = true;
25754
25755         this.fireEvent('initialize', this);
25756         this.pushValue();
25757     },
25758
25759     // private
25760     onDestroy : function(){
25761         
25762         
25763         
25764         if(this.rendered){
25765             
25766             for (var i =0; i < this.toolbars.length;i++) {
25767                 // fixme - ask toolbars for heights?
25768                 this.toolbars[i].onDestroy();
25769             }
25770             
25771             this.wrap.dom.innerHTML = '';
25772             this.wrap.remove();
25773         }
25774     },
25775
25776     // private
25777     onFirstFocus : function(){
25778         
25779         this.assignDocWin();
25780         
25781         
25782         this.activated = true;
25783         for (var i =0; i < this.toolbars.length;i++) {
25784             this.toolbars[i].onFirstFocus();
25785         }
25786        
25787         if(Roo.isGecko){ // prevent silly gecko errors
25788             this.win.focus();
25789             var s = this.win.getSelection();
25790             if(!s.focusNode || s.focusNode.nodeType != 3){
25791                 var r = s.getRangeAt(0);
25792                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25793                 r.collapse(true);
25794                 this.deferFocus();
25795             }
25796             try{
25797                 this.execCmd('useCSS', true);
25798                 this.execCmd('styleWithCSS', false);
25799             }catch(e){}
25800         }
25801         this.fireEvent('activate', this);
25802     },
25803
25804     // private
25805     adjustFont: function(btn){
25806         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25807         //if(Roo.isSafari){ // safari
25808         //    adjust *= 2;
25809        // }
25810         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25811         if(Roo.isSafari){ // safari
25812             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25813             v =  (v < 10) ? 10 : v;
25814             v =  (v > 48) ? 48 : v;
25815             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25816             
25817         }
25818         
25819         
25820         v = Math.max(1, v+adjust);
25821         
25822         this.execCmd('FontSize', v  );
25823     },
25824
25825     onEditorEvent : function(e){
25826         this.fireEvent('editorevent', this, e);
25827       //  this.updateToolbar();
25828         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25829     },
25830
25831     insertTag : function(tg)
25832     {
25833         // could be a bit smarter... -> wrap the current selected tRoo..
25834         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25835             
25836             range = this.createRange(this.getSelection());
25837             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25838             wrappingNode.appendChild(range.extractContents());
25839             range.insertNode(wrappingNode);
25840
25841             return;
25842             
25843             
25844             
25845         }
25846         this.execCmd("formatblock",   tg);
25847         
25848     },
25849     
25850     insertText : function(txt)
25851     {
25852         
25853         
25854         var range = this.createRange();
25855         range.deleteContents();
25856                //alert(Sender.getAttribute('label'));
25857                
25858         range.insertNode(this.doc.createTextNode(txt));
25859     } ,
25860     
25861     // private
25862     relayBtnCmd : function(btn){
25863         this.relayCmd(btn.cmd);
25864     },
25865
25866     /**
25867      * Executes a Midas editor command on the editor document and performs necessary focus and
25868      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25869      * @param {String} cmd The Midas command
25870      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25871      */
25872     relayCmd : function(cmd, value){
25873         this.win.focus();
25874         this.execCmd(cmd, value);
25875         this.fireEvent('editorevent', this);
25876         //this.updateToolbar();
25877         this.deferFocus();
25878     },
25879
25880     /**
25881      * Executes a Midas editor command directly on the editor document.
25882      * For visual commands, you should use {@link #relayCmd} instead.
25883      * <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     execCmd : function(cmd, value){
25888         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25889         this.syncValue();
25890     },
25891  
25892  
25893    
25894     /**
25895      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25896      * to insert tRoo.
25897      * @param {String} text | dom node.. 
25898      */
25899     insertAtCursor : function(text)
25900     {
25901         
25902         
25903         
25904         if(!this.activated){
25905             return;
25906         }
25907         /*
25908         if(Roo.isIE){
25909             this.win.focus();
25910             var r = this.doc.selection.createRange();
25911             if(r){
25912                 r.collapse(true);
25913                 r.pasteHTML(text);
25914                 this.syncValue();
25915                 this.deferFocus();
25916             
25917             }
25918             return;
25919         }
25920         */
25921         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25922             this.win.focus();
25923             
25924             
25925             // from jquery ui (MIT licenced)
25926             var range, node;
25927             var win = this.win;
25928             
25929             if (win.getSelection && win.getSelection().getRangeAt) {
25930                 range = win.getSelection().getRangeAt(0);
25931                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25932                 range.insertNode(node);
25933             } else if (win.document.selection && win.document.selection.createRange) {
25934                 // no firefox support
25935                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25936                 win.document.selection.createRange().pasteHTML(txt);
25937             } else {
25938                 // no firefox support
25939                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25940                 this.execCmd('InsertHTML', txt);
25941             } 
25942             
25943             this.syncValue();
25944             
25945             this.deferFocus();
25946         }
25947     },
25948  // private
25949     mozKeyPress : function(e){
25950         if(e.ctrlKey){
25951             var c = e.getCharCode(), cmd;
25952           
25953             if(c > 0){
25954                 c = String.fromCharCode(c).toLowerCase();
25955                 switch(c){
25956                     case 'b':
25957                         cmd = 'bold';
25958                         break;
25959                     case 'i':
25960                         cmd = 'italic';
25961                         break;
25962                     
25963                     case 'u':
25964                         cmd = 'underline';
25965                         break;
25966                     
25967                     case 'v':
25968                         this.cleanUpPaste.defer(100, this);
25969                         return;
25970                         
25971                 }
25972                 if(cmd){
25973                     this.win.focus();
25974                     this.execCmd(cmd);
25975                     this.deferFocus();
25976                     e.preventDefault();
25977                 }
25978                 
25979             }
25980         }
25981     },
25982
25983     // private
25984     fixKeys : function(){ // load time branching for fastest keydown performance
25985         if(Roo.isIE){
25986             return function(e){
25987                 var k = e.getKey(), r;
25988                 if(k == e.TAB){
25989                     e.stopEvent();
25990                     r = this.doc.selection.createRange();
25991                     if(r){
25992                         r.collapse(true);
25993                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25994                         this.deferFocus();
25995                     }
25996                     return;
25997                 }
25998                 
25999                 if(k == e.ENTER){
26000                     r = this.doc.selection.createRange();
26001                     if(r){
26002                         var target = r.parentElement();
26003                         if(!target || target.tagName.toLowerCase() != 'li'){
26004                             e.stopEvent();
26005                             r.pasteHTML('<br />');
26006                             r.collapse(false);
26007                             r.select();
26008                         }
26009                     }
26010                 }
26011                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26012                     this.cleanUpPaste.defer(100, this);
26013                     return;
26014                 }
26015                 
26016                 
26017             };
26018         }else if(Roo.isOpera){
26019             return function(e){
26020                 var k = e.getKey();
26021                 if(k == e.TAB){
26022                     e.stopEvent();
26023                     this.win.focus();
26024                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26025                     this.deferFocus();
26026                 }
26027                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26028                     this.cleanUpPaste.defer(100, this);
26029                     return;
26030                 }
26031                 
26032             };
26033         }else if(Roo.isSafari){
26034             return function(e){
26035                 var k = e.getKey();
26036                 
26037                 if(k == e.TAB){
26038                     e.stopEvent();
26039                     this.execCmd('InsertText','\t');
26040                     this.deferFocus();
26041                     return;
26042                 }
26043                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26044                     this.cleanUpPaste.defer(100, this);
26045                     return;
26046                 }
26047                 
26048              };
26049         }
26050     }(),
26051     
26052     getAllAncestors: function()
26053     {
26054         var p = this.getSelectedNode();
26055         var a = [];
26056         if (!p) {
26057             a.push(p); // push blank onto stack..
26058             p = this.getParentElement();
26059         }
26060         
26061         
26062         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26063             a.push(p);
26064             p = p.parentNode;
26065         }
26066         a.push(this.doc.body);
26067         return a;
26068     },
26069     lastSel : false,
26070     lastSelNode : false,
26071     
26072     
26073     getSelection : function() 
26074     {
26075         this.assignDocWin();
26076         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26077     },
26078     
26079     getSelectedNode: function() 
26080     {
26081         // this may only work on Gecko!!!
26082         
26083         // should we cache this!!!!
26084         
26085         
26086         
26087          
26088         var range = this.createRange(this.getSelection()).cloneRange();
26089         
26090         if (Roo.isIE) {
26091             var parent = range.parentElement();
26092             while (true) {
26093                 var testRange = range.duplicate();
26094                 testRange.moveToElementText(parent);
26095                 if (testRange.inRange(range)) {
26096                     break;
26097                 }
26098                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26099                     break;
26100                 }
26101                 parent = parent.parentElement;
26102             }
26103             return parent;
26104         }
26105         
26106         // is ancestor a text element.
26107         var ac =  range.commonAncestorContainer;
26108         if (ac.nodeType == 3) {
26109             ac = ac.parentNode;
26110         }
26111         
26112         var ar = ac.childNodes;
26113          
26114         var nodes = [];
26115         var other_nodes = [];
26116         var has_other_nodes = false;
26117         for (var i=0;i<ar.length;i++) {
26118             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26119                 continue;
26120             }
26121             // fullly contained node.
26122             
26123             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26124                 nodes.push(ar[i]);
26125                 continue;
26126             }
26127             
26128             // probably selected..
26129             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26130                 other_nodes.push(ar[i]);
26131                 continue;
26132             }
26133             // outer..
26134             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26135                 continue;
26136             }
26137             
26138             
26139             has_other_nodes = true;
26140         }
26141         if (!nodes.length && other_nodes.length) {
26142             nodes= other_nodes;
26143         }
26144         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26145             return false;
26146         }
26147         
26148         return nodes[0];
26149     },
26150     createRange: function(sel)
26151     {
26152         // this has strange effects when using with 
26153         // top toolbar - not sure if it's a great idea.
26154         //this.editor.contentWindow.focus();
26155         if (typeof sel != "undefined") {
26156             try {
26157                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26158             } catch(e) {
26159                 return this.doc.createRange();
26160             }
26161         } else {
26162             return this.doc.createRange();
26163         }
26164     },
26165     getParentElement: function()
26166     {
26167         
26168         this.assignDocWin();
26169         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26170         
26171         var range = this.createRange(sel);
26172          
26173         try {
26174             var p = range.commonAncestorContainer;
26175             while (p.nodeType == 3) { // text node
26176                 p = p.parentNode;
26177             }
26178             return p;
26179         } catch (e) {
26180             return null;
26181         }
26182     
26183     },
26184     /***
26185      *
26186      * Range intersection.. the hard stuff...
26187      *  '-1' = before
26188      *  '0' = hits..
26189      *  '1' = after.
26190      *         [ -- selected range --- ]
26191      *   [fail]                        [fail]
26192      *
26193      *    basically..
26194      *      if end is before start or  hits it. fail.
26195      *      if start is after end or hits it fail.
26196      *
26197      *   if either hits (but other is outside. - then it's not 
26198      *   
26199      *    
26200      **/
26201     
26202     
26203     // @see http://www.thismuchiknow.co.uk/?p=64.
26204     rangeIntersectsNode : function(range, node)
26205     {
26206         var nodeRange = node.ownerDocument.createRange();
26207         try {
26208             nodeRange.selectNode(node);
26209         } catch (e) {
26210             nodeRange.selectNodeContents(node);
26211         }
26212     
26213         var rangeStartRange = range.cloneRange();
26214         rangeStartRange.collapse(true);
26215     
26216         var rangeEndRange = range.cloneRange();
26217         rangeEndRange.collapse(false);
26218     
26219         var nodeStartRange = nodeRange.cloneRange();
26220         nodeStartRange.collapse(true);
26221     
26222         var nodeEndRange = nodeRange.cloneRange();
26223         nodeEndRange.collapse(false);
26224     
26225         return rangeStartRange.compareBoundaryPoints(
26226                  Range.START_TO_START, nodeEndRange) == -1 &&
26227                rangeEndRange.compareBoundaryPoints(
26228                  Range.START_TO_START, nodeStartRange) == 1;
26229         
26230          
26231     },
26232     rangeCompareNode : function(range, node)
26233     {
26234         var nodeRange = node.ownerDocument.createRange();
26235         try {
26236             nodeRange.selectNode(node);
26237         } catch (e) {
26238             nodeRange.selectNodeContents(node);
26239         }
26240         
26241         
26242         range.collapse(true);
26243     
26244         nodeRange.collapse(true);
26245      
26246         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26247         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26248          
26249         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26250         
26251         var nodeIsBefore   =  ss == 1;
26252         var nodeIsAfter    = ee == -1;
26253         
26254         if (nodeIsBefore && nodeIsAfter)
26255             return 0; // outer
26256         if (!nodeIsBefore && nodeIsAfter)
26257             return 1; //right trailed.
26258         
26259         if (nodeIsBefore && !nodeIsAfter)
26260             return 2;  // left trailed.
26261         // fully contined.
26262         return 3;
26263     },
26264
26265     // private? - in a new class?
26266     cleanUpPaste :  function()
26267     {
26268         // cleans up the whole document..
26269          Roo.log('cleanuppaste');
26270         this.cleanUpChildren(this.doc.body);
26271         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26272         if (clean != this.doc.body.innerHTML) {
26273             this.doc.body.innerHTML = clean;
26274         }
26275         
26276     },
26277     
26278     cleanWordChars : function(input) {// change the chars to hex code
26279         var he = Roo.form.HtmlEditor;
26280         
26281         var output = input;
26282         Roo.each(he.swapCodes, function(sw) { 
26283             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26284             
26285             output = output.replace(swapper, sw[1]);
26286         });
26287         
26288         return output;
26289     },
26290     
26291     
26292     cleanUpChildren : function (n)
26293     {
26294         if (!n.childNodes.length) {
26295             return;
26296         }
26297         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26298            this.cleanUpChild(n.childNodes[i]);
26299         }
26300     },
26301     
26302     
26303         
26304     
26305     cleanUpChild : function (node)
26306     {
26307         var ed = this;
26308         //console.log(node);
26309         if (node.nodeName == "#text") {
26310             // clean up silly Windows -- stuff?
26311             return; 
26312         }
26313         if (node.nodeName == "#comment") {
26314             node.parentNode.removeChild(node);
26315             // clean up silly Windows -- stuff?
26316             return; 
26317         }
26318         
26319         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26320             // remove node.
26321             node.parentNode.removeChild(node);
26322             return;
26323             
26324         }
26325         
26326         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26327         
26328         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26329         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26330         
26331         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26332         //    remove_keep_children = true;
26333         //}
26334         
26335         if (remove_keep_children) {
26336             this.cleanUpChildren(node);
26337             // inserts everything just before this node...
26338             while (node.childNodes.length) {
26339                 var cn = node.childNodes[0];
26340                 node.removeChild(cn);
26341                 node.parentNode.insertBefore(cn, node);
26342             }
26343             node.parentNode.removeChild(node);
26344             return;
26345         }
26346         
26347         if (!node.attributes || !node.attributes.length) {
26348             this.cleanUpChildren(node);
26349             return;
26350         }
26351         
26352         function cleanAttr(n,v)
26353         {
26354             
26355             if (v.match(/^\./) || v.match(/^\//)) {
26356                 return;
26357             }
26358             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26359                 return;
26360             }
26361             if (v.match(/^#/)) {
26362                 return;
26363             }
26364 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26365             node.removeAttribute(n);
26366             
26367         }
26368         
26369         function cleanStyle(n,v)
26370         {
26371             if (v.match(/expression/)) { //XSS?? should we even bother..
26372                 node.removeAttribute(n);
26373                 return;
26374             }
26375             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26376             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26377             
26378             
26379             var parts = v.split(/;/);
26380             var clean = [];
26381             
26382             Roo.each(parts, function(p) {
26383                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26384                 if (!p.length) {
26385                     return true;
26386                 }
26387                 var l = p.split(':').shift().replace(/\s+/g,'');
26388                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26389                 
26390                 
26391                 if ( cblack.indexOf(l) > -1) {
26392 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26393                     //node.removeAttribute(n);
26394                     return true;
26395                 }
26396                 //Roo.log()
26397                 // only allow 'c whitelisted system attributes'
26398                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26399 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26400                     //node.removeAttribute(n);
26401                     return true;
26402                 }
26403                 
26404                 
26405                  
26406                 
26407                 clean.push(p);
26408                 return true;
26409             });
26410             if (clean.length) { 
26411                 node.setAttribute(n, clean.join(';'));
26412             } else {
26413                 node.removeAttribute(n);
26414             }
26415             
26416         }
26417         
26418         
26419         for (var i = node.attributes.length-1; i > -1 ; i--) {
26420             var a = node.attributes[i];
26421             //console.log(a);
26422             
26423             if (a.name.toLowerCase().substr(0,2)=='on')  {
26424                 node.removeAttribute(a.name);
26425                 continue;
26426             }
26427             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26428                 node.removeAttribute(a.name);
26429                 continue;
26430             }
26431             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26432                 cleanAttr(a.name,a.value); // fixme..
26433                 continue;
26434             }
26435             if (a.name == 'style') {
26436                 cleanStyle(a.name,a.value);
26437                 continue;
26438             }
26439             /// clean up MS crap..
26440             // tecnically this should be a list of valid class'es..
26441             
26442             
26443             if (a.name == 'class') {
26444                 if (a.value.match(/^Mso/)) {
26445                     node.className = '';
26446                 }
26447                 
26448                 if (a.value.match(/body/)) {
26449                     node.className = '';
26450                 }
26451                 continue;
26452             }
26453             
26454             // style cleanup!?
26455             // class cleanup?
26456             
26457         }
26458         
26459         
26460         this.cleanUpChildren(node);
26461         
26462         
26463     }
26464     
26465     
26466     // hide stuff that is not compatible
26467     /**
26468      * @event blur
26469      * @hide
26470      */
26471     /**
26472      * @event change
26473      * @hide
26474      */
26475     /**
26476      * @event focus
26477      * @hide
26478      */
26479     /**
26480      * @event specialkey
26481      * @hide
26482      */
26483     /**
26484      * @cfg {String} fieldClass @hide
26485      */
26486     /**
26487      * @cfg {String} focusClass @hide
26488      */
26489     /**
26490      * @cfg {String} autoCreate @hide
26491      */
26492     /**
26493      * @cfg {String} inputType @hide
26494      */
26495     /**
26496      * @cfg {String} invalidClass @hide
26497      */
26498     /**
26499      * @cfg {String} invalidText @hide
26500      */
26501     /**
26502      * @cfg {String} msgFx @hide
26503      */
26504     /**
26505      * @cfg {String} validateOnBlur @hide
26506      */
26507 });
26508
26509 Roo.form.HtmlEditor.white = [
26510         'area', 'br', 'img', 'input', 'hr', 'wbr',
26511         
26512        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26513        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26514        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26515        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26516        'table',   'ul',         'xmp', 
26517        
26518        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26519       'thead',   'tr', 
26520      
26521       'dir', 'menu', 'ol', 'ul', 'dl',
26522        
26523       'embed',  'object'
26524 ];
26525
26526
26527 Roo.form.HtmlEditor.black = [
26528     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26529         'applet', // 
26530         'base',   'basefont', 'bgsound', 'blink',  'body', 
26531         'frame',  'frameset', 'head',    'html',   'ilayer', 
26532         'iframe', 'layer',  'link',     'meta',    'object',   
26533         'script', 'style' ,'title',  'xml' // clean later..
26534 ];
26535 Roo.form.HtmlEditor.clean = [
26536     'script', 'style', 'title', 'xml'
26537 ];
26538 Roo.form.HtmlEditor.remove = [
26539     'font'
26540 ];
26541 // attributes..
26542
26543 Roo.form.HtmlEditor.ablack = [
26544     'on'
26545 ];
26546     
26547 Roo.form.HtmlEditor.aclean = [ 
26548     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26549 ];
26550
26551 // protocols..
26552 Roo.form.HtmlEditor.pwhite= [
26553         'http',  'https',  'mailto'
26554 ];
26555
26556 // white listed style attributes.
26557 Roo.form.HtmlEditor.cwhite= [
26558       //  'text-align', /// default is to allow most things..
26559       
26560          
26561 //        'font-size'//??
26562 ];
26563
26564 // black listed style attributes.
26565 Roo.form.HtmlEditor.cblack= [
26566       //  'font-size' -- this can be set by the project 
26567 ];
26568
26569
26570 Roo.form.HtmlEditor.swapCodes   =[ 
26571     [    8211, "--" ], 
26572     [    8212, "--" ], 
26573     [    8216,  "'" ],  
26574     [    8217, "'" ],  
26575     [    8220, '"' ],  
26576     [    8221, '"' ],  
26577     [    8226, "*" ],  
26578     [    8230, "..." ]
26579 ]; 
26580
26581     // <script type="text/javascript">
26582 /*
26583  * Based on
26584  * Ext JS Library 1.1.1
26585  * Copyright(c) 2006-2007, Ext JS, LLC.
26586  *  
26587  
26588  */
26589
26590 /**
26591  * @class Roo.form.HtmlEditorToolbar1
26592  * Basic Toolbar
26593  * 
26594  * Usage:
26595  *
26596  new Roo.form.HtmlEditor({
26597     ....
26598     toolbars : [
26599         new Roo.form.HtmlEditorToolbar1({
26600             disable : { fonts: 1 , format: 1, ..., ... , ...],
26601             btns : [ .... ]
26602         })
26603     }
26604      
26605  * 
26606  * @cfg {Object} disable List of elements to disable..
26607  * @cfg {Array} btns List of additional buttons.
26608  * 
26609  * 
26610  * NEEDS Extra CSS? 
26611  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26612  */
26613  
26614 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26615 {
26616     
26617     Roo.apply(this, config);
26618     
26619     // default disabled, based on 'good practice'..
26620     this.disable = this.disable || {};
26621     Roo.applyIf(this.disable, {
26622         fontSize : true,
26623         colors : true,
26624         specialElements : true
26625     });
26626     
26627     
26628     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26629     // dont call parent... till later.
26630 }
26631
26632 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26633     
26634     tb: false,
26635     
26636     rendered: false,
26637     
26638     editor : false,
26639     /**
26640      * @cfg {Object} disable  List of toolbar elements to disable
26641          
26642      */
26643     disable : false,
26644       /**
26645      * @cfg {Array} fontFamilies An array of available font families
26646      */
26647     fontFamilies : [
26648         'Arial',
26649         'Courier New',
26650         'Tahoma',
26651         'Times New Roman',
26652         'Verdana'
26653     ],
26654     
26655     specialChars : [
26656            "&#169;",
26657           "&#174;",     
26658           "&#8482;",    
26659           "&#163;" ,    
26660          // "&#8212;",    
26661           "&#8230;",    
26662           "&#247;" ,    
26663         //  "&#225;" ,     ?? a acute?
26664            "&#8364;"    , //Euro
26665        //   "&#8220;"    ,
26666         //  "&#8221;"    ,
26667         //  "&#8226;"    ,
26668           "&#176;"  //   , // degrees
26669
26670          // "&#233;"     , // e ecute
26671          // "&#250;"     , // u ecute?
26672     ],
26673     
26674     specialElements : [
26675         {
26676             text: "Insert Table",
26677             xtype: 'MenuItem',
26678             xns : Roo.Menu,
26679             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26680                 
26681         },
26682         {    
26683             text: "Insert Image",
26684             xtype: 'MenuItem',
26685             xns : Roo.Menu,
26686             ihtml : '<img src="about:blank"/>'
26687             
26688         }
26689         
26690          
26691     ],
26692     
26693     
26694     inputElements : [ 
26695             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26696             "input:submit", "input:button", "select", "textarea", "label" ],
26697     formats : [
26698         ["p"] ,  
26699         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26700         ["pre"],[ "code"], 
26701         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26702         ['div'],['span']
26703     ],
26704      /**
26705      * @cfg {String} defaultFont default font to use.
26706      */
26707     defaultFont: 'tahoma',
26708    
26709     fontSelect : false,
26710     
26711     
26712     formatCombo : false,
26713     
26714     init : function(editor)
26715     {
26716         this.editor = editor;
26717         
26718         
26719         var fid = editor.frameId;
26720         var etb = this;
26721         function btn(id, toggle, handler){
26722             var xid = fid + '-'+ id ;
26723             return {
26724                 id : xid,
26725                 cmd : id,
26726                 cls : 'x-btn-icon x-edit-'+id,
26727                 enableToggle:toggle !== false,
26728                 scope: editor, // was editor...
26729                 handler:handler||editor.relayBtnCmd,
26730                 clickEvent:'mousedown',
26731                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26732                 tabIndex:-1
26733             };
26734         }
26735         
26736         
26737         
26738         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26739         this.tb = tb;
26740          // stop form submits
26741         tb.el.on('click', function(e){
26742             e.preventDefault(); // what does this do?
26743         });
26744
26745         if(!this.disable.font) { // && !Roo.isSafari){
26746             /* why no safari for fonts 
26747             editor.fontSelect = tb.el.createChild({
26748                 tag:'select',
26749                 tabIndex: -1,
26750                 cls:'x-font-select',
26751                 html: this.createFontOptions()
26752             });
26753             
26754             editor.fontSelect.on('change', function(){
26755                 var font = editor.fontSelect.dom.value;
26756                 editor.relayCmd('fontname', font);
26757                 editor.deferFocus();
26758             }, editor);
26759             
26760             tb.add(
26761                 editor.fontSelect.dom,
26762                 '-'
26763             );
26764             */
26765             
26766         };
26767         if(!this.disable.formats){
26768             this.formatCombo = new Roo.form.ComboBox({
26769                 store: new Roo.data.SimpleStore({
26770                     id : 'tag',
26771                     fields: ['tag'],
26772                     data : this.formats // from states.js
26773                 }),
26774                 blockFocus : true,
26775                 name : '',
26776                 //autoCreate : {tag: "div",  size: "20"},
26777                 displayField:'tag',
26778                 typeAhead: false,
26779                 mode: 'local',
26780                 editable : false,
26781                 triggerAction: 'all',
26782                 emptyText:'Add tag',
26783                 selectOnFocus:true,
26784                 width:135,
26785                 listeners : {
26786                     'select': function(c, r, i) {
26787                         editor.insertTag(r.get('tag'));
26788                         editor.focus();
26789                     }
26790                 }
26791
26792             });
26793             tb.addField(this.formatCombo);
26794             
26795         }
26796         
26797         if(!this.disable.format){
26798             tb.add(
26799                 btn('bold'),
26800                 btn('italic'),
26801                 btn('underline')
26802             );
26803         };
26804         if(!this.disable.fontSize){
26805             tb.add(
26806                 '-',
26807                 
26808                 
26809                 btn('increasefontsize', false, editor.adjustFont),
26810                 btn('decreasefontsize', false, editor.adjustFont)
26811             );
26812         };
26813         
26814         
26815         if(!this.disable.colors){
26816             tb.add(
26817                 '-', {
26818                     id:editor.frameId +'-forecolor',
26819                     cls:'x-btn-icon x-edit-forecolor',
26820                     clickEvent:'mousedown',
26821                     tooltip: this.buttonTips['forecolor'] || undefined,
26822                     tabIndex:-1,
26823                     menu : new Roo.menu.ColorMenu({
26824                         allowReselect: true,
26825                         focus: Roo.emptyFn,
26826                         value:'000000',
26827                         plain:true,
26828                         selectHandler: function(cp, color){
26829                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26830                             editor.deferFocus();
26831                         },
26832                         scope: editor,
26833                         clickEvent:'mousedown'
26834                     })
26835                 }, {
26836                     id:editor.frameId +'backcolor',
26837                     cls:'x-btn-icon x-edit-backcolor',
26838                     clickEvent:'mousedown',
26839                     tooltip: this.buttonTips['backcolor'] || undefined,
26840                     tabIndex:-1,
26841                     menu : new Roo.menu.ColorMenu({
26842                         focus: Roo.emptyFn,
26843                         value:'FFFFFF',
26844                         plain:true,
26845                         allowReselect: true,
26846                         selectHandler: function(cp, color){
26847                             if(Roo.isGecko){
26848                                 editor.execCmd('useCSS', false);
26849                                 editor.execCmd('hilitecolor', color);
26850                                 editor.execCmd('useCSS', true);
26851                                 editor.deferFocus();
26852                             }else{
26853                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26854                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26855                                 editor.deferFocus();
26856                             }
26857                         },
26858                         scope:editor,
26859                         clickEvent:'mousedown'
26860                     })
26861                 }
26862             );
26863         };
26864         // now add all the items...
26865         
26866
26867         if(!this.disable.alignments){
26868             tb.add(
26869                 '-',
26870                 btn('justifyleft'),
26871                 btn('justifycenter'),
26872                 btn('justifyright')
26873             );
26874         };
26875
26876         //if(!Roo.isSafari){
26877             if(!this.disable.links){
26878                 tb.add(
26879                     '-',
26880                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26881                 );
26882             };
26883
26884             if(!this.disable.lists){
26885                 tb.add(
26886                     '-',
26887                     btn('insertorderedlist'),
26888                     btn('insertunorderedlist')
26889                 );
26890             }
26891             if(!this.disable.sourceEdit){
26892                 tb.add(
26893                     '-',
26894                     btn('sourceedit', true, function(btn){
26895                         this.toggleSourceEdit(btn.pressed);
26896                     })
26897                 );
26898             }
26899         //}
26900         
26901         var smenu = { };
26902         // special menu.. - needs to be tidied up..
26903         if (!this.disable.special) {
26904             smenu = {
26905                 text: "&#169;",
26906                 cls: 'x-edit-none',
26907                 
26908                 menu : {
26909                     items : []
26910                 }
26911             };
26912             for (var i =0; i < this.specialChars.length; i++) {
26913                 smenu.menu.items.push({
26914                     
26915                     html: this.specialChars[i],
26916                     handler: function(a,b) {
26917                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26918                         //editor.insertAtCursor(a.html);
26919                         
26920                     },
26921                     tabIndex:-1
26922                 });
26923             }
26924             
26925             
26926             tb.add(smenu);
26927             
26928             
26929         }
26930          
26931         if (!this.disable.specialElements) {
26932             var semenu = {
26933                 text: "Other;",
26934                 cls: 'x-edit-none',
26935                 menu : {
26936                     items : []
26937                 }
26938             };
26939             for (var i =0; i < this.specialElements.length; i++) {
26940                 semenu.menu.items.push(
26941                     Roo.apply({ 
26942                         handler: function(a,b) {
26943                             editor.insertAtCursor(this.ihtml);
26944                         }
26945                     }, this.specialElements[i])
26946                 );
26947                     
26948             }
26949             
26950             tb.add(semenu);
26951             
26952             
26953         }
26954          
26955         
26956         if (this.btns) {
26957             for(var i =0; i< this.btns.length;i++) {
26958                 var b = Roo.factory(this.btns[i],Roo.form);
26959                 b.cls =  'x-edit-none';
26960                 b.scope = editor;
26961                 tb.add(b);
26962             }
26963         
26964         }
26965         
26966         
26967         
26968         // disable everything...
26969         
26970         this.tb.items.each(function(item){
26971            if(item.id != editor.frameId+ '-sourceedit'){
26972                 item.disable();
26973             }
26974         });
26975         this.rendered = true;
26976         
26977         // the all the btns;
26978         editor.on('editorevent', this.updateToolbar, this);
26979         // other toolbars need to implement this..
26980         //editor.on('editmodechange', this.updateToolbar, this);
26981     },
26982     
26983     
26984     
26985     /**
26986      * Protected method that will not generally be called directly. It triggers
26987      * a toolbar update by reading the markup state of the current selection in the editor.
26988      */
26989     updateToolbar: function(){
26990
26991         if(!this.editor.activated){
26992             this.editor.onFirstFocus();
26993             return;
26994         }
26995
26996         var btns = this.tb.items.map, 
26997             doc = this.editor.doc,
26998             frameId = this.editor.frameId;
26999
27000         if(!this.disable.font && !Roo.isSafari){
27001             /*
27002             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27003             if(name != this.fontSelect.dom.value){
27004                 this.fontSelect.dom.value = name;
27005             }
27006             */
27007         }
27008         if(!this.disable.format){
27009             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27010             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27011             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27012         }
27013         if(!this.disable.alignments){
27014             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27015             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27016             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27017         }
27018         if(!Roo.isSafari && !this.disable.lists){
27019             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27020             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27021         }
27022         
27023         var ans = this.editor.getAllAncestors();
27024         if (this.formatCombo) {
27025             
27026             
27027             var store = this.formatCombo.store;
27028             this.formatCombo.setValue("");
27029             for (var i =0; i < ans.length;i++) {
27030                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27031                     // select it..
27032                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27033                     break;
27034                 }
27035             }
27036         }
27037         
27038         
27039         
27040         // hides menus... - so this cant be on a menu...
27041         Roo.menu.MenuMgr.hideAll();
27042
27043         //this.editorsyncValue();
27044     },
27045    
27046     
27047     createFontOptions : function(){
27048         var buf = [], fs = this.fontFamilies, ff, lc;
27049         
27050         
27051         
27052         for(var i = 0, len = fs.length; i< len; i++){
27053             ff = fs[i];
27054             lc = ff.toLowerCase();
27055             buf.push(
27056                 '<option value="',lc,'" style="font-family:',ff,';"',
27057                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27058                     ff,
27059                 '</option>'
27060             );
27061         }
27062         return buf.join('');
27063     },
27064     
27065     toggleSourceEdit : function(sourceEditMode){
27066         if(sourceEditMode === undefined){
27067             sourceEditMode = !this.sourceEditMode;
27068         }
27069         this.sourceEditMode = sourceEditMode === true;
27070         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27071         // just toggle the button?
27072         if(btn.pressed !== this.editor.sourceEditMode){
27073             btn.toggle(this.editor.sourceEditMode);
27074             return;
27075         }
27076         
27077         if(this.sourceEditMode){
27078             this.tb.items.each(function(item){
27079                 if(item.cmd != 'sourceedit'){
27080                     item.disable();
27081                 }
27082             });
27083           
27084         }else{
27085             if(this.initialized){
27086                 this.tb.items.each(function(item){
27087                     item.enable();
27088                 });
27089             }
27090             
27091         }
27092         // tell the editor that it's been pressed..
27093         this.editor.toggleSourceEdit(sourceEditMode);
27094        
27095     },
27096      /**
27097      * Object collection of toolbar tooltips for the buttons in the editor. The key
27098      * is the command id associated with that button and the value is a valid QuickTips object.
27099      * For example:
27100 <pre><code>
27101 {
27102     bold : {
27103         title: 'Bold (Ctrl+B)',
27104         text: 'Make the selected text bold.',
27105         cls: 'x-html-editor-tip'
27106     },
27107     italic : {
27108         title: 'Italic (Ctrl+I)',
27109         text: 'Make the selected text italic.',
27110         cls: 'x-html-editor-tip'
27111     },
27112     ...
27113 </code></pre>
27114     * @type Object
27115      */
27116     buttonTips : {
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         underline : {
27128             title: 'Underline (Ctrl+U)',
27129             text: 'Underline the selected text.',
27130             cls: 'x-html-editor-tip'
27131         },
27132         increasefontsize : {
27133             title: 'Grow Text',
27134             text: 'Increase the font size.',
27135             cls: 'x-html-editor-tip'
27136         },
27137         decreasefontsize : {
27138             title: 'Shrink Text',
27139             text: 'Decrease the font size.',
27140             cls: 'x-html-editor-tip'
27141         },
27142         backcolor : {
27143             title: 'Text Highlight Color',
27144             text: 'Change the background color of the selected text.',
27145             cls: 'x-html-editor-tip'
27146         },
27147         forecolor : {
27148             title: 'Font Color',
27149             text: 'Change the color of the selected text.',
27150             cls: 'x-html-editor-tip'
27151         },
27152         justifyleft : {
27153             title: 'Align Text Left',
27154             text: 'Align text to the left.',
27155             cls: 'x-html-editor-tip'
27156         },
27157         justifycenter : {
27158             title: 'Center Text',
27159             text: 'Center text in the editor.',
27160             cls: 'x-html-editor-tip'
27161         },
27162         justifyright : {
27163             title: 'Align Text Right',
27164             text: 'Align text to the right.',
27165             cls: 'x-html-editor-tip'
27166         },
27167         insertunorderedlist : {
27168             title: 'Bullet List',
27169             text: 'Start a bulleted list.',
27170             cls: 'x-html-editor-tip'
27171         },
27172         insertorderedlist : {
27173             title: 'Numbered List',
27174             text: 'Start a numbered list.',
27175             cls: 'x-html-editor-tip'
27176         },
27177         createlink : {
27178             title: 'Hyperlink',
27179             text: 'Make the selected text a hyperlink.',
27180             cls: 'x-html-editor-tip'
27181         },
27182         sourceedit : {
27183             title: 'Source Edit',
27184             text: 'Switch to source editing mode.',
27185             cls: 'x-html-editor-tip'
27186         }
27187     },
27188     // private
27189     onDestroy : function(){
27190         if(this.rendered){
27191             
27192             this.tb.items.each(function(item){
27193                 if(item.menu){
27194                     item.menu.removeAll();
27195                     if(item.menu.el){
27196                         item.menu.el.destroy();
27197                     }
27198                 }
27199                 item.destroy();
27200             });
27201              
27202         }
27203     },
27204     onFirstFocus: function() {
27205         this.tb.items.each(function(item){
27206            item.enable();
27207         });
27208     }
27209 });
27210
27211
27212
27213
27214 // <script type="text/javascript">
27215 /*
27216  * Based on
27217  * Ext JS Library 1.1.1
27218  * Copyright(c) 2006-2007, Ext JS, LLC.
27219  *  
27220  
27221  */
27222
27223  
27224 /**
27225  * @class Roo.form.HtmlEditor.ToolbarContext
27226  * Context Toolbar
27227  * 
27228  * Usage:
27229  *
27230  new Roo.form.HtmlEditor({
27231     ....
27232     toolbars : [
27233         { xtype: 'ToolbarStandard', styles : {} }
27234         { xtype: 'ToolbarContext', disable : {} }
27235     ]
27236 })
27237
27238      
27239  * 
27240  * @config : {Object} disable List of elements to disable.. (not done yet.)
27241  * @config : {Object} styles  Map of styles available.
27242  * 
27243  */
27244
27245 Roo.form.HtmlEditor.ToolbarContext = function(config)
27246 {
27247     
27248     Roo.apply(this, config);
27249     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27250     // dont call parent... till later.
27251     this.styles = this.styles || {};
27252 }
27253
27254  
27255
27256 Roo.form.HtmlEditor.ToolbarContext.types = {
27257     'IMG' : {
27258         width : {
27259             title: "Width",
27260             width: 40
27261         },
27262         height:  {
27263             title: "Height",
27264             width: 40
27265         },
27266         align: {
27267             title: "Align",
27268             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27269             width : 80
27270             
27271         },
27272         border: {
27273             title: "Border",
27274             width: 40
27275         },
27276         alt: {
27277             title: "Alt",
27278             width: 120
27279         },
27280         src : {
27281             title: "Src",
27282             width: 220
27283         }
27284         
27285     },
27286     'A' : {
27287         name : {
27288             title: "Name",
27289             width: 50
27290         },
27291         href:  {
27292             title: "Href",
27293             width: 220
27294         } // border?
27295         
27296     },
27297     'TABLE' : {
27298         rows : {
27299             title: "Rows",
27300             width: 20
27301         },
27302         cols : {
27303             title: "Cols",
27304             width: 20
27305         },
27306         width : {
27307             title: "Width",
27308             width: 40
27309         },
27310         height : {
27311             title: "Height",
27312             width: 40
27313         },
27314         border : {
27315             title: "Border",
27316             width: 20
27317         }
27318     },
27319     'TD' : {
27320         width : {
27321             title: "Width",
27322             width: 40
27323         },
27324         height : {
27325             title: "Height",
27326             width: 40
27327         },   
27328         align: {
27329             title: "Align",
27330             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27331             width: 80
27332         },
27333         valign: {
27334             title: "Valign",
27335             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27336             width: 80
27337         },
27338         colspan: {
27339             title: "Colspan",
27340             width: 20
27341             
27342         },
27343          'font-family'  : {
27344             title : "Font",
27345             style : 'fontFamily',
27346             displayField: 'display',
27347             optname : 'font-family',
27348             width: 140
27349         }
27350     },
27351     'INPUT' : {
27352         name : {
27353             title: "name",
27354             width: 120
27355         },
27356         value : {
27357             title: "Value",
27358             width: 120
27359         },
27360         width : {
27361             title: "Width",
27362             width: 40
27363         }
27364     },
27365     'LABEL' : {
27366         'for' : {
27367             title: "For",
27368             width: 120
27369         }
27370     },
27371     'TEXTAREA' : {
27372           name : {
27373             title: "name",
27374             width: 120
27375         },
27376         rows : {
27377             title: "Rows",
27378             width: 20
27379         },
27380         cols : {
27381             title: "Cols",
27382             width: 20
27383         }
27384     },
27385     'SELECT' : {
27386         name : {
27387             title: "name",
27388             width: 120
27389         },
27390         selectoptions : {
27391             title: "Options",
27392             width: 200
27393         }
27394     },
27395     
27396     // should we really allow this??
27397     // should this just be 
27398     'BODY' : {
27399         title : {
27400             title: "Title",
27401             width: 200,
27402             disabled : true
27403         }
27404     },
27405     'SPAN' : {
27406         'font-family'  : {
27407             title : "Font",
27408             style : 'fontFamily',
27409             displayField: 'display',
27410             optname : 'font-family',
27411             width: 140
27412         }
27413     },
27414     'DIV' : {
27415         'font-family'  : {
27416             title : "Font",
27417             style : 'fontFamily',
27418             displayField: 'display',
27419             optname : 'font-family',
27420             width: 140
27421         }
27422     },
27423      'P' : {
27424         'font-family'  : {
27425             title : "Font",
27426             style : 'fontFamily',
27427             displayField: 'display',
27428             optname : 'font-family',
27429             width: 140
27430         }
27431     },
27432     
27433     '*' : {
27434         // empty..
27435     }
27436
27437 };
27438
27439 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27440 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27441
27442 Roo.form.HtmlEditor.ToolbarContext.options = {
27443         'font-family'  : [ 
27444                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27445                 [ 'Courier New', 'Courier New'],
27446                 [ 'Tahoma', 'Tahoma'],
27447                 [ 'Times New Roman,serif', 'Times'],
27448                 [ 'Verdana','Verdana' ]
27449         ]
27450 };
27451
27452 // fixme - these need to be configurable..
27453  
27454
27455 Roo.form.HtmlEditor.ToolbarContext.types
27456
27457
27458 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27459     
27460     tb: false,
27461     
27462     rendered: false,
27463     
27464     editor : false,
27465     /**
27466      * @cfg {Object} disable  List of toolbar elements to disable
27467          
27468      */
27469     disable : false,
27470     /**
27471      * @cfg {Object} styles List of styles 
27472      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27473      *
27474      * These must be defined in the page, so they get rendered correctly..
27475      * .headline { }
27476      * TD.underline { }
27477      * 
27478      */
27479     styles : false,
27480     
27481     options: false,
27482     
27483     toolbars : false,
27484     
27485     init : function(editor)
27486     {
27487         this.editor = editor;
27488         
27489         
27490         var fid = editor.frameId;
27491         var etb = this;
27492         function btn(id, toggle, handler){
27493             var xid = fid + '-'+ id ;
27494             return {
27495                 id : xid,
27496                 cmd : id,
27497                 cls : 'x-btn-icon x-edit-'+id,
27498                 enableToggle:toggle !== false,
27499                 scope: editor, // was editor...
27500                 handler:handler||editor.relayBtnCmd,
27501                 clickEvent:'mousedown',
27502                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27503                 tabIndex:-1
27504             };
27505         }
27506         // create a new element.
27507         var wdiv = editor.wrap.createChild({
27508                 tag: 'div'
27509             }, editor.wrap.dom.firstChild.nextSibling, true);
27510         
27511         // can we do this more than once??
27512         
27513          // stop form submits
27514       
27515  
27516         // disable everything...
27517         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27518         this.toolbars = {};
27519            
27520         for (var i in  ty) {
27521           
27522             this.toolbars[i] = this.buildToolbar(ty[i],i);
27523         }
27524         this.tb = this.toolbars.BODY;
27525         this.tb.el.show();
27526         this.buildFooter();
27527         this.footer.show();
27528         editor.on('hide', function( ) { this.footer.hide() }, this);
27529         editor.on('show', function( ) { this.footer.show() }, this);
27530         
27531          
27532         this.rendered = true;
27533         
27534         // the all the btns;
27535         editor.on('editorevent', this.updateToolbar, this);
27536         // other toolbars need to implement this..
27537         //editor.on('editmodechange', this.updateToolbar, this);
27538     },
27539     
27540     
27541     
27542     /**
27543      * Protected method that will not generally be called directly. It triggers
27544      * a toolbar update by reading the markup state of the current selection in the editor.
27545      */
27546     updateToolbar: function(editor,ev,sel){
27547
27548         //Roo.log(ev);
27549         // capture mouse up - this is handy for selecting images..
27550         // perhaps should go somewhere else...
27551         if(!this.editor.activated){
27552              this.editor.onFirstFocus();
27553             return;
27554         }
27555         
27556         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27557         // selectNode - might want to handle IE?
27558         if (ev &&
27559             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27560             ev.target && ev.target.tagName == 'IMG') {
27561             // they have click on an image...
27562             // let's see if we can change the selection...
27563             sel = ev.target;
27564          
27565               var nodeRange = sel.ownerDocument.createRange();
27566             try {
27567                 nodeRange.selectNode(sel);
27568             } catch (e) {
27569                 nodeRange.selectNodeContents(sel);
27570             }
27571             //nodeRange.collapse(true);
27572             var s = editor.win.getSelection();
27573             s.removeAllRanges();
27574             s.addRange(nodeRange);
27575         }  
27576         
27577       
27578         var updateFooter = sel ? false : true;
27579         
27580         
27581         var ans = this.editor.getAllAncestors();
27582         
27583         // pick
27584         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27585         
27586         if (!sel) { 
27587             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27588             sel = sel ? sel : this.editor.doc.body;
27589             sel = sel.tagName.length ? sel : this.editor.doc.body;
27590             
27591         }
27592         // pick a menu that exists..
27593         var tn = sel.tagName.toUpperCase();
27594         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27595         
27596         tn = sel.tagName.toUpperCase();
27597         
27598         var lastSel = this.tb.selectedNode
27599         
27600         this.tb.selectedNode = sel;
27601         
27602         // if current menu does not match..
27603         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27604                 
27605             this.tb.el.hide();
27606             ///console.log("show: " + tn);
27607             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27608             this.tb.el.show();
27609             // update name
27610             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27611             
27612             
27613             // update attributes
27614             if (this.tb.fields) {
27615                 this.tb.fields.each(function(e) {
27616                     if (e.stylename) {
27617                         e.setValue(sel.style[e.stylename]);
27618                         return;
27619                     } 
27620                    e.setValue(sel.getAttribute(e.attrname));
27621                 });
27622             }
27623             
27624             var hasStyles = false;
27625             for(var i in this.styles) {
27626                 hasStyles = true;
27627                 break;
27628             }
27629             
27630             // update styles
27631             if (hasStyles) { 
27632                 var st = this.tb.fields.item(0);
27633                 
27634                 st.store.removeAll();
27635                
27636                 
27637                 var cn = sel.className.split(/\s+/);
27638                 
27639                 var avs = [];
27640                 if (this.styles['*']) {
27641                     
27642                     Roo.each(this.styles['*'], function(v) {
27643                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27644                     });
27645                 }
27646                 if (this.styles[tn]) { 
27647                     Roo.each(this.styles[tn], function(v) {
27648                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27649                     });
27650                 }
27651                 
27652                 st.store.loadData(avs);
27653                 st.collapse();
27654                 st.setValue(cn);
27655             }
27656             // flag our selected Node.
27657             this.tb.selectedNode = sel;
27658            
27659            
27660             Roo.menu.MenuMgr.hideAll();
27661
27662         }
27663         
27664         if (!updateFooter) {
27665             //this.footDisp.dom.innerHTML = ''; 
27666             return;
27667         }
27668         // update the footer
27669         //
27670         var html = '';
27671         
27672         this.footerEls = ans.reverse();
27673         Roo.each(this.footerEls, function(a,i) {
27674             if (!a) { return; }
27675             html += html.length ? ' &gt; '  :  '';
27676             
27677             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27678             
27679         });
27680        
27681         // 
27682         var sz = this.footDisp.up('td').getSize();
27683         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27684         this.footDisp.dom.style.marginLeft = '5px';
27685         
27686         this.footDisp.dom.style.overflow = 'hidden';
27687         
27688         this.footDisp.dom.innerHTML = html;
27689             
27690         //this.editorsyncValue();
27691     },
27692      
27693     
27694    
27695        
27696     // private
27697     onDestroy : function(){
27698         if(this.rendered){
27699             
27700             this.tb.items.each(function(item){
27701                 if(item.menu){
27702                     item.menu.removeAll();
27703                     if(item.menu.el){
27704                         item.menu.el.destroy();
27705                     }
27706                 }
27707                 item.destroy();
27708             });
27709              
27710         }
27711     },
27712     onFirstFocus: function() {
27713         // need to do this for all the toolbars..
27714         this.tb.items.each(function(item){
27715            item.enable();
27716         });
27717     },
27718     buildToolbar: function(tlist, nm)
27719     {
27720         var editor = this.editor;
27721          // create a new element.
27722         var wdiv = editor.wrap.createChild({
27723                 tag: 'div'
27724             }, editor.wrap.dom.firstChild.nextSibling, true);
27725         
27726        
27727         var tb = new Roo.Toolbar(wdiv);
27728         // add the name..
27729         
27730         tb.add(nm+ ":&nbsp;");
27731         
27732         var styles = [];
27733         for(var i in this.styles) {
27734             styles.push(i);
27735         }
27736         
27737         // styles...
27738         if (styles && styles.length) {
27739             
27740             // this needs a multi-select checkbox...
27741             tb.addField( new Roo.form.ComboBox({
27742                 store: new Roo.data.SimpleStore({
27743                     id : 'val',
27744                     fields: ['val', 'selected'],
27745                     data : [] 
27746                 }),
27747                 name : '-roo-edit-className',
27748                 attrname : 'className',
27749                 displayField: 'val',
27750                 typeAhead: false,
27751                 mode: 'local',
27752                 editable : false,
27753                 triggerAction: 'all',
27754                 emptyText:'Select Style',
27755                 selectOnFocus:true,
27756                 width: 130,
27757                 listeners : {
27758                     'select': function(c, r, i) {
27759                         // initial support only for on class per el..
27760                         tb.selectedNode.className =  r ? r.get('val') : '';
27761                         editor.syncValue();
27762                     }
27763                 }
27764     
27765             }));
27766         }
27767         
27768         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27769         var tbops = tbc.options;
27770         
27771         for (var i in tlist) {
27772             
27773             var item = tlist[i];
27774             tb.add(item.title + ":&nbsp;");
27775             
27776             
27777             //optname == used so you can configure the options available..
27778             var opts = item.opts ? item.opts : false;
27779             if (item.optname) {
27780                 opts = tbops[item.optname];
27781            
27782             }
27783             
27784             if (opts) {
27785                 // opts == pulldown..
27786                 tb.addField( new Roo.form.ComboBox({
27787                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27788                         id : 'val',
27789                         fields: ['val', 'display'],
27790                         data : opts  
27791                     }),
27792                     name : '-roo-edit-' + i,
27793                     attrname : i,
27794                     stylename : item.style ? item.style : false,
27795                     displayField: item.displayField ? item.displayField : 'val',
27796                     valueField :  'val',
27797                     typeAhead: false,
27798                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27799                     editable : false,
27800                     triggerAction: 'all',
27801                     emptyText:'Select',
27802                     selectOnFocus:true,
27803                     width: item.width ? item.width  : 130,
27804                     listeners : {
27805                         'select': function(c, r, i) {
27806                             if (c.stylename) {
27807                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27808                                 return;
27809                             }
27810                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27811                         }
27812                     }
27813
27814                 }));
27815                 continue;
27816                     
27817                  
27818                 
27819                 tb.addField( new Roo.form.TextField({
27820                     name: i,
27821                     width: 100,
27822                     //allowBlank:false,
27823                     value: ''
27824                 }));
27825                 continue;
27826             }
27827             tb.addField( new Roo.form.TextField({
27828                 name: '-roo-edit-' + i,
27829                 attrname : i,
27830                 
27831                 width: item.width,
27832                 //allowBlank:true,
27833                 value: '',
27834                 listeners: {
27835                     'change' : function(f, nv, ov) {
27836                         tb.selectedNode.setAttribute(f.attrname, nv);
27837                     }
27838                 }
27839             }));
27840              
27841         }
27842         tb.addFill();
27843         var _this = this;
27844         tb.addButton( {
27845             text: 'Remove Tag',
27846     
27847             listeners : {
27848                 click : function ()
27849                 {
27850                     // remove
27851                     // undo does not work.
27852                      
27853                     var sn = tb.selectedNode;
27854                     
27855                     var pn = sn.parentNode;
27856                     
27857                     var stn =  sn.childNodes[0];
27858                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27859                     while (sn.childNodes.length) {
27860                         var node = sn.childNodes[0];
27861                         sn.removeChild(node);
27862                         //Roo.log(node);
27863                         pn.insertBefore(node, sn);
27864                         
27865                     }
27866                     pn.removeChild(sn);
27867                     var range = editor.createRange();
27868         
27869                     range.setStart(stn,0);
27870                     range.setEnd(en,0); //????
27871                     //range.selectNode(sel);
27872                     
27873                     
27874                     var selection = editor.getSelection();
27875                     selection.removeAllRanges();
27876                     selection.addRange(range);
27877                     
27878                     
27879                     
27880                     //_this.updateToolbar(null, null, pn);
27881                     _this.updateToolbar(null, null, null);
27882                     _this.footDisp.dom.innerHTML = ''; 
27883                 }
27884             }
27885             
27886                     
27887                 
27888             
27889         });
27890         
27891         
27892         tb.el.on('click', function(e){
27893             e.preventDefault(); // what does this do?
27894         });
27895         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27896         tb.el.hide();
27897         tb.name = nm;
27898         // dont need to disable them... as they will get hidden
27899         return tb;
27900          
27901         
27902     },
27903     buildFooter : function()
27904     {
27905         
27906         var fel = this.editor.wrap.createChild();
27907         this.footer = new Roo.Toolbar(fel);
27908         // toolbar has scrolly on left / right?
27909         var footDisp= new Roo.Toolbar.Fill();
27910         var _t = this;
27911         this.footer.add(
27912             {
27913                 text : '&lt;',
27914                 xtype: 'Button',
27915                 handler : function() {
27916                     _t.footDisp.scrollTo('left',0,true)
27917                 }
27918             }
27919         );
27920         this.footer.add( footDisp );
27921         this.footer.add( 
27922             {
27923                 text : '&gt;',
27924                 xtype: 'Button',
27925                 handler : function() {
27926                     // no animation..
27927                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27928                 }
27929             }
27930         );
27931         var fel = Roo.get(footDisp.el);
27932         fel.addClass('x-editor-context');
27933         this.footDispWrap = fel; 
27934         this.footDispWrap.overflow  = 'hidden';
27935         
27936         this.footDisp = fel.createChild();
27937         this.footDispWrap.on('click', this.onContextClick, this)
27938         
27939         
27940     },
27941     onContextClick : function (ev,dom)
27942     {
27943         ev.preventDefault();
27944         var  cn = dom.className;
27945         //Roo.log(cn);
27946         if (!cn.match(/x-ed-loc-/)) {
27947             return;
27948         }
27949         var n = cn.split('-').pop();
27950         var ans = this.footerEls;
27951         var sel = ans[n];
27952         
27953          // pick
27954         var range = this.editor.createRange();
27955         
27956         range.selectNodeContents(sel);
27957         //range.selectNode(sel);
27958         
27959         
27960         var selection = this.editor.getSelection();
27961         selection.removeAllRanges();
27962         selection.addRange(range);
27963         
27964         
27965         
27966         this.updateToolbar(null, null, sel);
27967         
27968         
27969     }
27970     
27971     
27972     
27973     
27974     
27975 });
27976
27977
27978
27979
27980
27981 /*
27982  * Based on:
27983  * Ext JS Library 1.1.1
27984  * Copyright(c) 2006-2007, Ext JS, LLC.
27985  *
27986  * Originally Released Under LGPL - original licence link has changed is not relivant.
27987  *
27988  * Fork - LGPL
27989  * <script type="text/javascript">
27990  */
27991  
27992 /**
27993  * @class Roo.form.BasicForm
27994  * @extends Roo.util.Observable
27995  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27996  * @constructor
27997  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27998  * @param {Object} config Configuration options
27999  */
28000 Roo.form.BasicForm = function(el, config){
28001     this.allItems = [];
28002     this.childForms = [];
28003     Roo.apply(this, config);
28004     /*
28005      * The Roo.form.Field items in this form.
28006      * @type MixedCollection
28007      */
28008      
28009      
28010     this.items = new Roo.util.MixedCollection(false, function(o){
28011         return o.id || (o.id = Roo.id());
28012     });
28013     this.addEvents({
28014         /**
28015          * @event beforeaction
28016          * Fires before any action is performed. Return false to cancel the action.
28017          * @param {Form} this
28018          * @param {Action} action The action to be performed
28019          */
28020         beforeaction: true,
28021         /**
28022          * @event actionfailed
28023          * Fires when an action fails.
28024          * @param {Form} this
28025          * @param {Action} action The action that failed
28026          */
28027         actionfailed : true,
28028         /**
28029          * @event actioncomplete
28030          * Fires when an action is completed.
28031          * @param {Form} this
28032          * @param {Action} action The action that completed
28033          */
28034         actioncomplete : true
28035     });
28036     if(el){
28037         this.initEl(el);
28038     }
28039     Roo.form.BasicForm.superclass.constructor.call(this);
28040 };
28041
28042 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28043     /**
28044      * @cfg {String} method
28045      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28046      */
28047     /**
28048      * @cfg {DataReader} reader
28049      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28050      * This is optional as there is built-in support for processing JSON.
28051      */
28052     /**
28053      * @cfg {DataReader} errorReader
28054      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28055      * This is completely optional as there is built-in support for processing JSON.
28056      */
28057     /**
28058      * @cfg {String} url
28059      * The URL to use for form actions if one isn't supplied in the action options.
28060      */
28061     /**
28062      * @cfg {Boolean} fileUpload
28063      * Set to true if this form is a file upload.
28064      */
28065      
28066     /**
28067      * @cfg {Object} baseParams
28068      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28069      */
28070      /**
28071      
28072     /**
28073      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28074      */
28075     timeout: 30,
28076
28077     // private
28078     activeAction : null,
28079
28080     /**
28081      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28082      * or setValues() data instead of when the form was first created.
28083      */
28084     trackResetOnLoad : false,
28085     
28086     
28087     /**
28088      * childForms - used for multi-tab forms
28089      * @type {Array}
28090      */
28091     childForms : false,
28092     
28093     /**
28094      * allItems - full list of fields.
28095      * @type {Array}
28096      */
28097     allItems : false,
28098     
28099     /**
28100      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28101      * element by passing it or its id or mask the form itself by passing in true.
28102      * @type Mixed
28103      */
28104     waitMsgTarget : false,
28105
28106     // private
28107     initEl : function(el){
28108         this.el = Roo.get(el);
28109         this.id = this.el.id || Roo.id();
28110         this.el.on('submit', this.onSubmit, this);
28111         this.el.addClass('x-form');
28112     },
28113
28114     // private
28115     onSubmit : function(e){
28116         e.stopEvent();
28117     },
28118
28119     /**
28120      * Returns true if client-side validation on the form is successful.
28121      * @return Boolean
28122      */
28123     isValid : function(){
28124         var valid = true;
28125         this.items.each(function(f){
28126            if(!f.validate()){
28127                valid = false;
28128            }
28129         });
28130         return valid;
28131     },
28132
28133     /**
28134      * Returns true if any fields in this form have changed since their original load.
28135      * @return Boolean
28136      */
28137     isDirty : function(){
28138         var dirty = false;
28139         this.items.each(function(f){
28140            if(f.isDirty()){
28141                dirty = true;
28142                return false;
28143            }
28144         });
28145         return dirty;
28146     },
28147
28148     /**
28149      * Performs a predefined action (submit or load) or custom actions you define on this form.
28150      * @param {String} actionName The name of the action type
28151      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28152      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28153      * accept other config options):
28154      * <pre>
28155 Property          Type             Description
28156 ----------------  ---------------  ----------------------------------------------------------------------------------
28157 url               String           The url for the action (defaults to the form's url)
28158 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28159 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28160 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28161                                    validate the form on the client (defaults to false)
28162      * </pre>
28163      * @return {BasicForm} this
28164      */
28165     doAction : function(action, options){
28166         if(typeof action == 'string'){
28167             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28168         }
28169         if(this.fireEvent('beforeaction', this, action) !== false){
28170             this.beforeAction(action);
28171             action.run.defer(100, action);
28172         }
28173         return this;
28174     },
28175
28176     /**
28177      * Shortcut to do a submit action.
28178      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28179      * @return {BasicForm} this
28180      */
28181     submit : function(options){
28182         this.doAction('submit', options);
28183         return this;
28184     },
28185
28186     /**
28187      * Shortcut to do a load action.
28188      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28189      * @return {BasicForm} this
28190      */
28191     load : function(options){
28192         this.doAction('load', options);
28193         return this;
28194     },
28195
28196     /**
28197      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28198      * @param {Record} record The record to edit
28199      * @return {BasicForm} this
28200      */
28201     updateRecord : function(record){
28202         record.beginEdit();
28203         var fs = record.fields;
28204         fs.each(function(f){
28205             var field = this.findField(f.name);
28206             if(field){
28207                 record.set(f.name, field.getValue());
28208             }
28209         }, this);
28210         record.endEdit();
28211         return this;
28212     },
28213
28214     /**
28215      * Loads an Roo.data.Record into this form.
28216      * @param {Record} record The record to load
28217      * @return {BasicForm} this
28218      */
28219     loadRecord : function(record){
28220         this.setValues(record.data);
28221         return this;
28222     },
28223
28224     // private
28225     beforeAction : function(action){
28226         var o = action.options;
28227         
28228        
28229         if(this.waitMsgTarget === true){
28230             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28231         }else if(this.waitMsgTarget){
28232             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28233             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28234         }else {
28235             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28236         }
28237          
28238     },
28239
28240     // private
28241     afterAction : function(action, success){
28242         this.activeAction = null;
28243         var o = action.options;
28244         
28245         if(this.waitMsgTarget === true){
28246             this.el.unmask();
28247         }else if(this.waitMsgTarget){
28248             this.waitMsgTarget.unmask();
28249         }else{
28250             Roo.MessageBox.updateProgress(1);
28251             Roo.MessageBox.hide();
28252         }
28253          
28254         if(success){
28255             if(o.reset){
28256                 this.reset();
28257             }
28258             Roo.callback(o.success, o.scope, [this, action]);
28259             this.fireEvent('actioncomplete', this, action);
28260             
28261         }else{
28262             
28263             // failure condition..
28264             // we have a scenario where updates need confirming.
28265             // eg. if a locking scenario exists..
28266             // we look for { errors : { needs_confirm : true }} in the response.
28267             if (
28268                 (typeof(action.result) != 'undefined')  &&
28269                 (typeof(action.result.errors) != 'undefined')  &&
28270                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28271            ){
28272                 var _t = this;
28273                 Roo.MessageBox.confirm(
28274                     "Change requires confirmation",
28275                     action.result.errorMsg,
28276                     function(r) {
28277                         if (r != 'yes') {
28278                             return;
28279                         }
28280                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28281                     }
28282                     
28283                 );
28284                 
28285                 
28286                 
28287                 return;
28288             }
28289             
28290             Roo.callback(o.failure, o.scope, [this, action]);
28291             // show an error message if no failed handler is set..
28292             if (!this.hasListener('actionfailed')) {
28293                 Roo.MessageBox.alert("Error",
28294                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28295                         action.result.errorMsg :
28296                         "Saving Failed, please check your entries or try again"
28297                 );
28298             }
28299             
28300             this.fireEvent('actionfailed', this, action);
28301         }
28302         
28303     },
28304
28305     /**
28306      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28307      * @param {String} id The value to search for
28308      * @return Field
28309      */
28310     findField : function(id){
28311         var field = this.items.get(id);
28312         if(!field){
28313             this.items.each(function(f){
28314                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28315                     field = f;
28316                     return false;
28317                 }
28318             });
28319         }
28320         return field || null;
28321     },
28322
28323     /**
28324      * Add a secondary form to this one, 
28325      * Used to provide tabbed forms. One form is primary, with hidden values 
28326      * which mirror the elements from the other forms.
28327      * 
28328      * @param {Roo.form.Form} form to add.
28329      * 
28330      */
28331     addForm : function(form)
28332     {
28333        
28334         if (this.childForms.indexOf(form) > -1) {
28335             // already added..
28336             return;
28337         }
28338         this.childForms.push(form);
28339         var n = '';
28340         Roo.each(form.allItems, function (fe) {
28341             
28342             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28343             if (this.findField(n)) { // already added..
28344                 return;
28345             }
28346             var add = new Roo.form.Hidden({
28347                 name : n
28348             });
28349             add.render(this.el);
28350             
28351             this.add( add );
28352         }, this);
28353         
28354     },
28355     /**
28356      * Mark fields in this form invalid in bulk.
28357      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28358      * @return {BasicForm} this
28359      */
28360     markInvalid : function(errors){
28361         if(errors instanceof Array){
28362             for(var i = 0, len = errors.length; i < len; i++){
28363                 var fieldError = errors[i];
28364                 var f = this.findField(fieldError.id);
28365                 if(f){
28366                     f.markInvalid(fieldError.msg);
28367                 }
28368             }
28369         }else{
28370             var field, id;
28371             for(id in errors){
28372                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28373                     field.markInvalid(errors[id]);
28374                 }
28375             }
28376         }
28377         Roo.each(this.childForms || [], function (f) {
28378             f.markInvalid(errors);
28379         });
28380         
28381         return this;
28382     },
28383
28384     /**
28385      * Set values for fields in this form in bulk.
28386      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28387      * @return {BasicForm} this
28388      */
28389     setValues : function(values){
28390         if(values instanceof Array){ // array of objects
28391             for(var i = 0, len = values.length; i < len; i++){
28392                 var v = values[i];
28393                 var f = this.findField(v.id);
28394                 if(f){
28395                     f.setValue(v.value);
28396                     if(this.trackResetOnLoad){
28397                         f.originalValue = f.getValue();
28398                     }
28399                 }
28400             }
28401         }else{ // object hash
28402             var field, id;
28403             for(id in values){
28404                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28405                     
28406                     if (field.setFromData && 
28407                         field.valueField && 
28408                         field.displayField &&
28409                         // combos' with local stores can 
28410                         // be queried via setValue()
28411                         // to set their value..
28412                         (field.store && !field.store.isLocal)
28413                         ) {
28414                         // it's a combo
28415                         var sd = { };
28416                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28417                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28418                         field.setFromData(sd);
28419                         
28420                     } else {
28421                         field.setValue(values[id]);
28422                     }
28423                     
28424                     
28425                     if(this.trackResetOnLoad){
28426                         field.originalValue = field.getValue();
28427                     }
28428                 }
28429             }
28430         }
28431          
28432         Roo.each(this.childForms || [], function (f) {
28433             f.setValues(values);
28434         });
28435                 
28436         return this;
28437     },
28438
28439     /**
28440      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28441      * they are returned as an array.
28442      * @param {Boolean} asString
28443      * @return {Object}
28444      */
28445     getValues : function(asString){
28446         if (this.childForms) {
28447             // copy values from the child forms
28448             Roo.each(this.childForms, function (f) {
28449                 this.setValues(f.getValues());
28450             }, this);
28451         }
28452         
28453         
28454         
28455         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28456         if(asString === true){
28457             return fs;
28458         }
28459         return Roo.urlDecode(fs);
28460     },
28461     
28462     /**
28463      * Returns the fields in this form as an object with key/value pairs. 
28464      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28465      * @return {Object}
28466      */
28467     getFieldValues : function(with_hidden)
28468     {
28469         if (this.childForms) {
28470             // copy values from the child forms
28471             // should this call getFieldValues - probably not as we do not currently copy
28472             // hidden fields when we generate..
28473             Roo.each(this.childForms, function (f) {
28474                 this.setValues(f.getValues());
28475             }, this);
28476         }
28477         
28478         var ret = {};
28479         this.items.each(function(f){
28480             if (!f.getName()) {
28481                 return;
28482             }
28483             var v = f.getValue();
28484             if (f.inputType =='radio') {
28485                 if (typeof(ret[f.getName()]) == 'undefined') {
28486                     ret[f.getName()] = ''; // empty..
28487                 }
28488                 
28489                 if (!f.el.dom.checked) {
28490                     return;
28491                     
28492                 }
28493                 v = f.el.dom.value;
28494                 
28495             }
28496             
28497             // not sure if this supported any more..
28498             if ((typeof(v) == 'object') && f.getRawValue) {
28499                 v = f.getRawValue() ; // dates..
28500             }
28501             // combo boxes where name != hiddenName...
28502             if (f.name != f.getName()) {
28503                 ret[f.name] = f.getRawValue();
28504             }
28505             ret[f.getName()] = v;
28506         });
28507         
28508         return ret;
28509     },
28510
28511     /**
28512      * Clears all invalid messages in this form.
28513      * @return {BasicForm} this
28514      */
28515     clearInvalid : function(){
28516         this.items.each(function(f){
28517            f.clearInvalid();
28518         });
28519         
28520         Roo.each(this.childForms || [], function (f) {
28521             f.clearInvalid();
28522         });
28523         
28524         
28525         return this;
28526     },
28527
28528     /**
28529      * Resets this form.
28530      * @return {BasicForm} this
28531      */
28532     reset : function(){
28533         this.items.each(function(f){
28534             f.reset();
28535         });
28536         
28537         Roo.each(this.childForms || [], function (f) {
28538             f.reset();
28539         });
28540        
28541         
28542         return this;
28543     },
28544
28545     /**
28546      * Add Roo.form components to this form.
28547      * @param {Field} field1
28548      * @param {Field} field2 (optional)
28549      * @param {Field} etc (optional)
28550      * @return {BasicForm} this
28551      */
28552     add : function(){
28553         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28554         return this;
28555     },
28556
28557
28558     /**
28559      * Removes a field from the items collection (does NOT remove its markup).
28560      * @param {Field} field
28561      * @return {BasicForm} this
28562      */
28563     remove : function(field){
28564         this.items.remove(field);
28565         return this;
28566     },
28567
28568     /**
28569      * Looks at the fields in this form, checks them for an id attribute,
28570      * and calls applyTo on the existing dom element with that id.
28571      * @return {BasicForm} this
28572      */
28573     render : function(){
28574         this.items.each(function(f){
28575             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28576                 f.applyTo(f.id);
28577             }
28578         });
28579         return this;
28580     },
28581
28582     /**
28583      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28584      * @param {Object} values
28585      * @return {BasicForm} this
28586      */
28587     applyToFields : function(o){
28588         this.items.each(function(f){
28589            Roo.apply(f, o);
28590         });
28591         return this;
28592     },
28593
28594     /**
28595      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28596      * @param {Object} values
28597      * @return {BasicForm} this
28598      */
28599     applyIfToFields : function(o){
28600         this.items.each(function(f){
28601            Roo.applyIf(f, o);
28602         });
28603         return this;
28604     }
28605 });
28606
28607 // back compat
28608 Roo.BasicForm = Roo.form.BasicForm;/*
28609  * Based on:
28610  * Ext JS Library 1.1.1
28611  * Copyright(c) 2006-2007, Ext JS, LLC.
28612  *
28613  * Originally Released Under LGPL - original licence link has changed is not relivant.
28614  *
28615  * Fork - LGPL
28616  * <script type="text/javascript">
28617  */
28618
28619 /**
28620  * @class Roo.form.Form
28621  * @extends Roo.form.BasicForm
28622  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28623  * @constructor
28624  * @param {Object} config Configuration options
28625  */
28626 Roo.form.Form = function(config){
28627     var xitems =  [];
28628     if (config.items) {
28629         xitems = config.items;
28630         delete config.items;
28631     }
28632    
28633     
28634     Roo.form.Form.superclass.constructor.call(this, null, config);
28635     this.url = this.url || this.action;
28636     if(!this.root){
28637         this.root = new Roo.form.Layout(Roo.applyIf({
28638             id: Roo.id()
28639         }, config));
28640     }
28641     this.active = this.root;
28642     /**
28643      * Array of all the buttons that have been added to this form via {@link addButton}
28644      * @type Array
28645      */
28646     this.buttons = [];
28647     this.allItems = [];
28648     this.addEvents({
28649         /**
28650          * @event clientvalidation
28651          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28652          * @param {Form} this
28653          * @param {Boolean} valid true if the form has passed client-side validation
28654          */
28655         clientvalidation: true,
28656         /**
28657          * @event rendered
28658          * Fires when the form is rendered
28659          * @param {Roo.form.Form} form
28660          */
28661         rendered : true
28662     });
28663     
28664     if (this.progressUrl) {
28665             // push a hidden field onto the list of fields..
28666             this.addxtype( {
28667                     xns: Roo.form, 
28668                     xtype : 'Hidden', 
28669                     name : 'UPLOAD_IDENTIFIER' 
28670             });
28671         }
28672         
28673     
28674     Roo.each(xitems, this.addxtype, this);
28675     
28676     
28677     
28678 };
28679
28680 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28681     /**
28682      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28683      */
28684     /**
28685      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28686      */
28687     /**
28688      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28689      */
28690     buttonAlign:'center',
28691
28692     /**
28693      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28694      */
28695     minButtonWidth:75,
28696
28697     /**
28698      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28699      * This property cascades to child containers if not set.
28700      */
28701     labelAlign:'left',
28702
28703     /**
28704      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28705      * fires a looping event with that state. This is required to bind buttons to the valid
28706      * state using the config value formBind:true on the button.
28707      */
28708     monitorValid : false,
28709
28710     /**
28711      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28712      */
28713     monitorPoll : 200,
28714     
28715     /**
28716      * @cfg {String} progressUrl - Url to return progress data 
28717      */
28718     
28719     progressUrl : false,
28720   
28721     /**
28722      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28723      * fields are added and the column is closed. If no fields are passed the column remains open
28724      * until end() is called.
28725      * @param {Object} config The config to pass to the column
28726      * @param {Field} field1 (optional)
28727      * @param {Field} field2 (optional)
28728      * @param {Field} etc (optional)
28729      * @return Column The column container object
28730      */
28731     column : function(c){
28732         var col = new Roo.form.Column(c);
28733         this.start(col);
28734         if(arguments.length > 1){ // duplicate code required because of Opera
28735             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28736             this.end();
28737         }
28738         return col;
28739     },
28740
28741     /**
28742      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28743      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28744      * until end() is called.
28745      * @param {Object} config The config to pass to the fieldset
28746      * @param {Field} field1 (optional)
28747      * @param {Field} field2 (optional)
28748      * @param {Field} etc (optional)
28749      * @return FieldSet The fieldset container object
28750      */
28751     fieldset : function(c){
28752         var fs = new Roo.form.FieldSet(c);
28753         this.start(fs);
28754         if(arguments.length > 1){ // duplicate code required because of Opera
28755             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28756             this.end();
28757         }
28758         return fs;
28759     },
28760
28761     /**
28762      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28763      * fields are added and the container is closed. If no fields are passed the container remains open
28764      * until end() is called.
28765      * @param {Object} config The config to pass to the Layout
28766      * @param {Field} field1 (optional)
28767      * @param {Field} field2 (optional)
28768      * @param {Field} etc (optional)
28769      * @return Layout The container object
28770      */
28771     container : function(c){
28772         var l = new Roo.form.Layout(c);
28773         this.start(l);
28774         if(arguments.length > 1){ // duplicate code required because of Opera
28775             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28776             this.end();
28777         }
28778         return l;
28779     },
28780
28781     /**
28782      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28783      * @param {Object} container A Roo.form.Layout or subclass of Layout
28784      * @return {Form} this
28785      */
28786     start : function(c){
28787         // cascade label info
28788         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28789         this.active.stack.push(c);
28790         c.ownerCt = this.active;
28791         this.active = c;
28792         return this;
28793     },
28794
28795     /**
28796      * Closes the current open container
28797      * @return {Form} this
28798      */
28799     end : function(){
28800         if(this.active == this.root){
28801             return this;
28802         }
28803         this.active = this.active.ownerCt;
28804         return this;
28805     },
28806
28807     /**
28808      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28809      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28810      * as the label of the field.
28811      * @param {Field} field1
28812      * @param {Field} field2 (optional)
28813      * @param {Field} etc. (optional)
28814      * @return {Form} this
28815      */
28816     add : function(){
28817         this.active.stack.push.apply(this.active.stack, arguments);
28818         this.allItems.push.apply(this.allItems,arguments);
28819         var r = [];
28820         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28821             if(a[i].isFormField){
28822                 r.push(a[i]);
28823             }
28824         }
28825         if(r.length > 0){
28826             Roo.form.Form.superclass.add.apply(this, r);
28827         }
28828         return this;
28829     },
28830     
28831
28832     
28833     
28834     
28835      /**
28836      * Find any element that has been added to a form, using it's ID or name
28837      * This can include framesets, columns etc. along with regular fields..
28838      * @param {String} id - id or name to find.
28839      
28840      * @return {Element} e - or false if nothing found.
28841      */
28842     findbyId : function(id)
28843     {
28844         var ret = false;
28845         if (!id) {
28846             return ret;
28847         }
28848         Roo.each(this.allItems, function(f){
28849             if (f.id == id || f.name == id ){
28850                 ret = f;
28851                 return false;
28852             }
28853         });
28854         return ret;
28855     },
28856
28857     
28858     
28859     /**
28860      * Render this form into the passed container. This should only be called once!
28861      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28862      * @return {Form} this
28863      */
28864     render : function(ct)
28865     {
28866         
28867         
28868         
28869         ct = Roo.get(ct);
28870         var o = this.autoCreate || {
28871             tag: 'form',
28872             method : this.method || 'POST',
28873             id : this.id || Roo.id()
28874         };
28875         this.initEl(ct.createChild(o));
28876
28877         this.root.render(this.el);
28878         
28879        
28880              
28881         this.items.each(function(f){
28882             f.render('x-form-el-'+f.id);
28883         });
28884
28885         if(this.buttons.length > 0){
28886             // tables are required to maintain order and for correct IE layout
28887             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28888                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28889                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28890             }}, null, true);
28891             var tr = tb.getElementsByTagName('tr')[0];
28892             for(var i = 0, len = this.buttons.length; i < len; i++) {
28893                 var b = this.buttons[i];
28894                 var td = document.createElement('td');
28895                 td.className = 'x-form-btn-td';
28896                 b.render(tr.appendChild(td));
28897             }
28898         }
28899         if(this.monitorValid){ // initialize after render
28900             this.startMonitoring();
28901         }
28902         this.fireEvent('rendered', this);
28903         return this;
28904     },
28905
28906     /**
28907      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28908      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28909      * object or a valid Roo.DomHelper element config
28910      * @param {Function} handler The function called when the button is clicked
28911      * @param {Object} scope (optional) The scope of the handler function
28912      * @return {Roo.Button}
28913      */
28914     addButton : function(config, handler, scope){
28915         var bc = {
28916             handler: handler,
28917             scope: scope,
28918             minWidth: this.minButtonWidth,
28919             hideParent:true
28920         };
28921         if(typeof config == "string"){
28922             bc.text = config;
28923         }else{
28924             Roo.apply(bc, config);
28925         }
28926         var btn = new Roo.Button(null, bc);
28927         this.buttons.push(btn);
28928         return btn;
28929     },
28930
28931      /**
28932      * Adds a series of form elements (using the xtype property as the factory method.
28933      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28934      * @param {Object} config 
28935      */
28936     
28937     addxtype : function()
28938     {
28939         var ar = Array.prototype.slice.call(arguments, 0);
28940         var ret = false;
28941         for(var i = 0; i < ar.length; i++) {
28942             if (!ar[i]) {
28943                 continue; // skip -- if this happends something invalid got sent, we 
28944                 // should ignore it, as basically that interface element will not show up
28945                 // and that should be pretty obvious!!
28946             }
28947             
28948             if (Roo.form[ar[i].xtype]) {
28949                 ar[i].form = this;
28950                 var fe = Roo.factory(ar[i], Roo.form);
28951                 if (!ret) {
28952                     ret = fe;
28953                 }
28954                 fe.form = this;
28955                 if (fe.store) {
28956                     fe.store.form = this;
28957                 }
28958                 if (fe.isLayout) {  
28959                          
28960                     this.start(fe);
28961                     this.allItems.push(fe);
28962                     if (fe.items && fe.addxtype) {
28963                         fe.addxtype.apply(fe, fe.items);
28964                         delete fe.items;
28965                     }
28966                      this.end();
28967                     continue;
28968                 }
28969                 
28970                 
28971                  
28972                 this.add(fe);
28973               //  console.log('adding ' + ar[i].xtype);
28974             }
28975             if (ar[i].xtype == 'Button') {  
28976                 //console.log('adding button');
28977                 //console.log(ar[i]);
28978                 this.addButton(ar[i]);
28979                 this.allItems.push(fe);
28980                 continue;
28981             }
28982             
28983             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28984                 alert('end is not supported on xtype any more, use items');
28985             //    this.end();
28986             //    //console.log('adding end');
28987             }
28988             
28989         }
28990         return ret;
28991     },
28992     
28993     /**
28994      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28995      * option "monitorValid"
28996      */
28997     startMonitoring : function(){
28998         if(!this.bound){
28999             this.bound = true;
29000             Roo.TaskMgr.start({
29001                 run : this.bindHandler,
29002                 interval : this.monitorPoll || 200,
29003                 scope: this
29004             });
29005         }
29006     },
29007
29008     /**
29009      * Stops monitoring of the valid state of this form
29010      */
29011     stopMonitoring : function(){
29012         this.bound = false;
29013     },
29014
29015     // private
29016     bindHandler : function(){
29017         if(!this.bound){
29018             return false; // stops binding
29019         }
29020         var valid = true;
29021         this.items.each(function(f){
29022             if(!f.isValid(true)){
29023                 valid = false;
29024                 return false;
29025             }
29026         });
29027         for(var i = 0, len = this.buttons.length; i < len; i++){
29028             var btn = this.buttons[i];
29029             if(btn.formBind === true && btn.disabled === valid){
29030                 btn.setDisabled(!valid);
29031             }
29032         }
29033         this.fireEvent('clientvalidation', this, valid);
29034     }
29035     
29036     
29037     
29038     
29039     
29040     
29041     
29042     
29043 });
29044
29045
29046 // back compat
29047 Roo.Form = Roo.form.Form;
29048 /*
29049  * Based on:
29050  * Ext JS Library 1.1.1
29051  * Copyright(c) 2006-2007, Ext JS, LLC.
29052  *
29053  * Originally Released Under LGPL - original licence link has changed is not relivant.
29054  *
29055  * Fork - LGPL
29056  * <script type="text/javascript">
29057  */
29058  
29059  /**
29060  * @class Roo.form.Action
29061  * Internal Class used to handle form actions
29062  * @constructor
29063  * @param {Roo.form.BasicForm} el The form element or its id
29064  * @param {Object} config Configuration options
29065  */
29066  
29067  
29068 // define the action interface
29069 Roo.form.Action = function(form, options){
29070     this.form = form;
29071     this.options = options || {};
29072 };
29073 /**
29074  * Client Validation Failed
29075  * @const 
29076  */
29077 Roo.form.Action.CLIENT_INVALID = 'client';
29078 /**
29079  * Server Validation Failed
29080  * @const 
29081  */
29082  Roo.form.Action.SERVER_INVALID = 'server';
29083  /**
29084  * Connect to Server Failed
29085  * @const 
29086  */
29087 Roo.form.Action.CONNECT_FAILURE = 'connect';
29088 /**
29089  * Reading Data from Server Failed
29090  * @const 
29091  */
29092 Roo.form.Action.LOAD_FAILURE = 'load';
29093
29094 Roo.form.Action.prototype = {
29095     type : 'default',
29096     failureType : undefined,
29097     response : undefined,
29098     result : undefined,
29099
29100     // interface method
29101     run : function(options){
29102
29103     },
29104
29105     // interface method
29106     success : function(response){
29107
29108     },
29109
29110     // interface method
29111     handleResponse : function(response){
29112
29113     },
29114
29115     // default connection failure
29116     failure : function(response){
29117         
29118         this.response = response;
29119         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29120         this.form.afterAction(this, false);
29121     },
29122
29123     processResponse : function(response){
29124         this.response = response;
29125         if(!response.responseText){
29126             return true;
29127         }
29128         this.result = this.handleResponse(response);
29129         return this.result;
29130     },
29131
29132     // utility functions used internally
29133     getUrl : function(appendParams){
29134         var url = this.options.url || this.form.url || this.form.el.dom.action;
29135         if(appendParams){
29136             var p = this.getParams();
29137             if(p){
29138                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29139             }
29140         }
29141         return url;
29142     },
29143
29144     getMethod : function(){
29145         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29146     },
29147
29148     getParams : function(){
29149         var bp = this.form.baseParams;
29150         var p = this.options.params;
29151         if(p){
29152             if(typeof p == "object"){
29153                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29154             }else if(typeof p == 'string' && bp){
29155                 p += '&' + Roo.urlEncode(bp);
29156             }
29157         }else if(bp){
29158             p = Roo.urlEncode(bp);
29159         }
29160         return p;
29161     },
29162
29163     createCallback : function(){
29164         return {
29165             success: this.success,
29166             failure: this.failure,
29167             scope: this,
29168             timeout: (this.form.timeout*1000),
29169             upload: this.form.fileUpload ? this.success : undefined
29170         };
29171     }
29172 };
29173
29174 Roo.form.Action.Submit = function(form, options){
29175     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29176 };
29177
29178 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29179     type : 'submit',
29180
29181     haveProgress : false,
29182     uploadComplete : false,
29183     
29184     // uploadProgress indicator.
29185     uploadProgress : function()
29186     {
29187         if (!this.form.progressUrl) {
29188             return;
29189         }
29190         
29191         if (!this.haveProgress) {
29192             Roo.MessageBox.progress("Uploading", "Uploading");
29193         }
29194         if (this.uploadComplete) {
29195            Roo.MessageBox.hide();
29196            return;
29197         }
29198         
29199         this.haveProgress = true;
29200    
29201         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29202         
29203         var c = new Roo.data.Connection();
29204         c.request({
29205             url : this.form.progressUrl,
29206             params: {
29207                 id : uid
29208             },
29209             method: 'GET',
29210             success : function(req){
29211                //console.log(data);
29212                 var rdata = false;
29213                 var edata;
29214                 try  {
29215                    rdata = Roo.decode(req.responseText)
29216                 } catch (e) {
29217                     Roo.log("Invalid data from server..");
29218                     Roo.log(edata);
29219                     return;
29220                 }
29221                 if (!rdata || !rdata.success) {
29222                     Roo.log(rdata);
29223                     Roo.MessageBox.alert(Roo.encode(rdata));
29224                     return;
29225                 }
29226                 var data = rdata.data;
29227                 
29228                 if (this.uploadComplete) {
29229                    Roo.MessageBox.hide();
29230                    return;
29231                 }
29232                    
29233                 if (data){
29234                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29235                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29236                     );
29237                 }
29238                 this.uploadProgress.defer(2000,this);
29239             },
29240        
29241             failure: function(data) {
29242                 Roo.log('progress url failed ');
29243                 Roo.log(data);
29244             },
29245             scope : this
29246         });
29247            
29248     },
29249     
29250     
29251     run : function()
29252     {
29253         // run get Values on the form, so it syncs any secondary forms.
29254         this.form.getValues();
29255         
29256         var o = this.options;
29257         var method = this.getMethod();
29258         var isPost = method == 'POST';
29259         if(o.clientValidation === false || this.form.isValid()){
29260             
29261             if (this.form.progressUrl) {
29262                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29263                     (new Date() * 1) + '' + Math.random());
29264                     
29265             } 
29266             
29267             
29268             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29269                 form:this.form.el.dom,
29270                 url:this.getUrl(!isPost),
29271                 method: method,
29272                 params:isPost ? this.getParams() : null,
29273                 isUpload: this.form.fileUpload
29274             }));
29275             
29276             this.uploadProgress();
29277
29278         }else if (o.clientValidation !== false){ // client validation failed
29279             this.failureType = Roo.form.Action.CLIENT_INVALID;
29280             this.form.afterAction(this, false);
29281         }
29282     },
29283
29284     success : function(response)
29285     {
29286         this.uploadComplete= true;
29287         if (this.haveProgress) {
29288             Roo.MessageBox.hide();
29289         }
29290         
29291         
29292         var result = this.processResponse(response);
29293         if(result === true || result.success){
29294             this.form.afterAction(this, true);
29295             return;
29296         }
29297         if(result.errors){
29298             this.form.markInvalid(result.errors);
29299             this.failureType = Roo.form.Action.SERVER_INVALID;
29300         }
29301         this.form.afterAction(this, false);
29302     },
29303     failure : function(response)
29304     {
29305         this.uploadComplete= true;
29306         if (this.haveProgress) {
29307             Roo.MessageBox.hide();
29308         }
29309         
29310         this.response = response;
29311         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29312         this.form.afterAction(this, false);
29313     },
29314     
29315     handleResponse : function(response){
29316         if(this.form.errorReader){
29317             var rs = this.form.errorReader.read(response);
29318             var errors = [];
29319             if(rs.records){
29320                 for(var i = 0, len = rs.records.length; i < len; i++) {
29321                     var r = rs.records[i];
29322                     errors[i] = r.data;
29323                 }
29324             }
29325             if(errors.length < 1){
29326                 errors = null;
29327             }
29328             return {
29329                 success : rs.success,
29330                 errors : errors
29331             };
29332         }
29333         var ret = false;
29334         try {
29335             ret = Roo.decode(response.responseText);
29336         } catch (e) {
29337             ret = {
29338                 success: false,
29339                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29340                 errors : []
29341             };
29342         }
29343         return ret;
29344         
29345     }
29346 });
29347
29348
29349 Roo.form.Action.Load = function(form, options){
29350     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29351     this.reader = this.form.reader;
29352 };
29353
29354 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29355     type : 'load',
29356
29357     run : function(){
29358         
29359         Roo.Ajax.request(Roo.apply(
29360                 this.createCallback(), {
29361                     method:this.getMethod(),
29362                     url:this.getUrl(false),
29363                     params:this.getParams()
29364         }));
29365     },
29366
29367     success : function(response){
29368         
29369         var result = this.processResponse(response);
29370         if(result === true || !result.success || !result.data){
29371             this.failureType = Roo.form.Action.LOAD_FAILURE;
29372             this.form.afterAction(this, false);
29373             return;
29374         }
29375         this.form.clearInvalid();
29376         this.form.setValues(result.data);
29377         this.form.afterAction(this, true);
29378     },
29379
29380     handleResponse : function(response){
29381         if(this.form.reader){
29382             var rs = this.form.reader.read(response);
29383             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29384             return {
29385                 success : rs.success,
29386                 data : data
29387             };
29388         }
29389         return Roo.decode(response.responseText);
29390     }
29391 });
29392
29393 Roo.form.Action.ACTION_TYPES = {
29394     'load' : Roo.form.Action.Load,
29395     'submit' : Roo.form.Action.Submit
29396 };/*
29397  * Based on:
29398  * Ext JS Library 1.1.1
29399  * Copyright(c) 2006-2007, Ext JS, LLC.
29400  *
29401  * Originally Released Under LGPL - original licence link has changed is not relivant.
29402  *
29403  * Fork - LGPL
29404  * <script type="text/javascript">
29405  */
29406  
29407 /**
29408  * @class Roo.form.Layout
29409  * @extends Roo.Component
29410  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29411  * @constructor
29412  * @param {Object} config Configuration options
29413  */
29414 Roo.form.Layout = function(config){
29415     var xitems = [];
29416     if (config.items) {
29417         xitems = config.items;
29418         delete config.items;
29419     }
29420     Roo.form.Layout.superclass.constructor.call(this, config);
29421     this.stack = [];
29422     Roo.each(xitems, this.addxtype, this);
29423      
29424 };
29425
29426 Roo.extend(Roo.form.Layout, Roo.Component, {
29427     /**
29428      * @cfg {String/Object} autoCreate
29429      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29430      */
29431     /**
29432      * @cfg {String/Object/Function} style
29433      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29434      * a function which returns such a specification.
29435      */
29436     /**
29437      * @cfg {String} labelAlign
29438      * Valid values are "left," "top" and "right" (defaults to "left")
29439      */
29440     /**
29441      * @cfg {Number} labelWidth
29442      * Fixed width in pixels of all field labels (defaults to undefined)
29443      */
29444     /**
29445      * @cfg {Boolean} clear
29446      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29447      */
29448     clear : true,
29449     /**
29450      * @cfg {String} labelSeparator
29451      * The separator to use after field labels (defaults to ':')
29452      */
29453     labelSeparator : ':',
29454     /**
29455      * @cfg {Boolean} hideLabels
29456      * True to suppress the display of field labels in this layout (defaults to false)
29457      */
29458     hideLabels : false,
29459
29460     // private
29461     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29462     
29463     isLayout : true,
29464     
29465     // private
29466     onRender : function(ct, position){
29467         if(this.el){ // from markup
29468             this.el = Roo.get(this.el);
29469         }else {  // generate
29470             var cfg = this.getAutoCreate();
29471             this.el = ct.createChild(cfg, position);
29472         }
29473         if(this.style){
29474             this.el.applyStyles(this.style);
29475         }
29476         if(this.labelAlign){
29477             this.el.addClass('x-form-label-'+this.labelAlign);
29478         }
29479         if(this.hideLabels){
29480             this.labelStyle = "display:none";
29481             this.elementStyle = "padding-left:0;";
29482         }else{
29483             if(typeof this.labelWidth == 'number'){
29484                 this.labelStyle = "width:"+this.labelWidth+"px;";
29485                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29486             }
29487             if(this.labelAlign == 'top'){
29488                 this.labelStyle = "width:auto;";
29489                 this.elementStyle = "padding-left:0;";
29490             }
29491         }
29492         var stack = this.stack;
29493         var slen = stack.length;
29494         if(slen > 0){
29495             if(!this.fieldTpl){
29496                 var t = new Roo.Template(
29497                     '<div class="x-form-item {5}">',
29498                         '<label for="{0}" style="{2}">{1}{4}</label>',
29499                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29500                         '</div>',
29501                     '</div><div class="x-form-clear-left"></div>'
29502                 );
29503                 t.disableFormats = true;
29504                 t.compile();
29505                 Roo.form.Layout.prototype.fieldTpl = t;
29506             }
29507             for(var i = 0; i < slen; i++) {
29508                 if(stack[i].isFormField){
29509                     this.renderField(stack[i]);
29510                 }else{
29511                     this.renderComponent(stack[i]);
29512                 }
29513             }
29514         }
29515         if(this.clear){
29516             this.el.createChild({cls:'x-form-clear'});
29517         }
29518     },
29519
29520     // private
29521     renderField : function(f){
29522         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29523                f.id, //0
29524                f.fieldLabel, //1
29525                f.labelStyle||this.labelStyle||'', //2
29526                this.elementStyle||'', //3
29527                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29528                f.itemCls||this.itemCls||''  //5
29529        ], true).getPrevSibling());
29530     },
29531
29532     // private
29533     renderComponent : function(c){
29534         c.render(c.isLayout ? this.el : this.el.createChild());    
29535     },
29536     /**
29537      * Adds a object form elements (using the xtype property as the factory method.)
29538      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29539      * @param {Object} config 
29540      */
29541     addxtype : function(o)
29542     {
29543         // create the lement.
29544         o.form = this.form;
29545         var fe = Roo.factory(o, Roo.form);
29546         this.form.allItems.push(fe);
29547         this.stack.push(fe);
29548         
29549         if (fe.isFormField) {
29550             this.form.items.add(fe);
29551         }
29552          
29553         return fe;
29554     }
29555 });
29556
29557 /**
29558  * @class Roo.form.Column
29559  * @extends Roo.form.Layout
29560  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29561  * @constructor
29562  * @param {Object} config Configuration options
29563  */
29564 Roo.form.Column = function(config){
29565     Roo.form.Column.superclass.constructor.call(this, config);
29566 };
29567
29568 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29569     /**
29570      * @cfg {Number/String} width
29571      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29572      */
29573     /**
29574      * @cfg {String/Object} autoCreate
29575      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29576      */
29577
29578     // private
29579     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29580
29581     // private
29582     onRender : function(ct, position){
29583         Roo.form.Column.superclass.onRender.call(this, ct, position);
29584         if(this.width){
29585             this.el.setWidth(this.width);
29586         }
29587     }
29588 });
29589
29590
29591 /**
29592  * @class Roo.form.Row
29593  * @extends Roo.form.Layout
29594  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29595  * @constructor
29596  * @param {Object} config Configuration options
29597  */
29598
29599  
29600 Roo.form.Row = function(config){
29601     Roo.form.Row.superclass.constructor.call(this, config);
29602 };
29603  
29604 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29605       /**
29606      * @cfg {Number/String} width
29607      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29608      */
29609     /**
29610      * @cfg {Number/String} height
29611      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29612      */
29613     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29614     
29615     padWidth : 20,
29616     // private
29617     onRender : function(ct, position){
29618         //console.log('row render');
29619         if(!this.rowTpl){
29620             var t = new Roo.Template(
29621                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29622                     '<label for="{0}" style="{2}">{1}{4}</label>',
29623                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29624                     '</div>',
29625                 '</div>'
29626             );
29627             t.disableFormats = true;
29628             t.compile();
29629             Roo.form.Layout.prototype.rowTpl = t;
29630         }
29631         this.fieldTpl = this.rowTpl;
29632         
29633         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29634         var labelWidth = 100;
29635         
29636         if ((this.labelAlign != 'top')) {
29637             if (typeof this.labelWidth == 'number') {
29638                 labelWidth = this.labelWidth
29639             }
29640             this.padWidth =  20 + labelWidth;
29641             
29642         }
29643         
29644         Roo.form.Column.superclass.onRender.call(this, ct, position);
29645         if(this.width){
29646             this.el.setWidth(this.width);
29647         }
29648         if(this.height){
29649             this.el.setHeight(this.height);
29650         }
29651     },
29652     
29653     // private
29654     renderField : function(f){
29655         f.fieldEl = this.fieldTpl.append(this.el, [
29656                f.id, f.fieldLabel,
29657                f.labelStyle||this.labelStyle||'',
29658                this.elementStyle||'',
29659                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29660                f.itemCls||this.itemCls||'',
29661                f.width ? f.width + this.padWidth : 160 + this.padWidth
29662        ],true);
29663     }
29664 });
29665  
29666
29667 /**
29668  * @class Roo.form.FieldSet
29669  * @extends Roo.form.Layout
29670  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29671  * @constructor
29672  * @param {Object} config Configuration options
29673  */
29674 Roo.form.FieldSet = function(config){
29675     Roo.form.FieldSet.superclass.constructor.call(this, config);
29676 };
29677
29678 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29679     /**
29680      * @cfg {String} legend
29681      * The text to display as the legend for the FieldSet (defaults to '')
29682      */
29683     /**
29684      * @cfg {String/Object} autoCreate
29685      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29686      */
29687
29688     // private
29689     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29690
29691     // private
29692     onRender : function(ct, position){
29693         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29694         if(this.legend){
29695             this.setLegend(this.legend);
29696         }
29697     },
29698
29699     // private
29700     setLegend : function(text){
29701         if(this.rendered){
29702             this.el.child('legend').update(text);
29703         }
29704     }
29705 });/*
29706  * Based on:
29707  * Ext JS Library 1.1.1
29708  * Copyright(c) 2006-2007, Ext JS, LLC.
29709  *
29710  * Originally Released Under LGPL - original licence link has changed is not relivant.
29711  *
29712  * Fork - LGPL
29713  * <script type="text/javascript">
29714  */
29715 /**
29716  * @class Roo.form.VTypes
29717  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29718  * @singleton
29719  */
29720 Roo.form.VTypes = function(){
29721     // closure these in so they are only created once.
29722     var alpha = /^[a-zA-Z_]+$/;
29723     var alphanum = /^[a-zA-Z0-9_]+$/;
29724     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29725     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29726
29727     // All these messages and functions are configurable
29728     return {
29729         /**
29730          * The function used to validate email addresses
29731          * @param {String} value The email address
29732          */
29733         'email' : function(v){
29734             return email.test(v);
29735         },
29736         /**
29737          * The error text to display when the email validation function returns false
29738          * @type String
29739          */
29740         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29741         /**
29742          * The keystroke filter mask to be applied on email input
29743          * @type RegExp
29744          */
29745         'emailMask' : /[a-z0-9_\.\-@]/i,
29746
29747         /**
29748          * The function used to validate URLs
29749          * @param {String} value The URL
29750          */
29751         'url' : function(v){
29752             return url.test(v);
29753         },
29754         /**
29755          * The error text to display when the url validation function returns false
29756          * @type String
29757          */
29758         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29759         
29760         /**
29761          * The function used to validate alpha values
29762          * @param {String} value The value
29763          */
29764         'alpha' : function(v){
29765             return alpha.test(v);
29766         },
29767         /**
29768          * The error text to display when the alpha validation function returns false
29769          * @type String
29770          */
29771         'alphaText' : 'This field should only contain letters and _',
29772         /**
29773          * The keystroke filter mask to be applied on alpha input
29774          * @type RegExp
29775          */
29776         'alphaMask' : /[a-z_]/i,
29777
29778         /**
29779          * The function used to validate alphanumeric values
29780          * @param {String} value The value
29781          */
29782         'alphanum' : function(v){
29783             return alphanum.test(v);
29784         },
29785         /**
29786          * The error text to display when the alphanumeric validation function returns false
29787          * @type String
29788          */
29789         'alphanumText' : 'This field should only contain letters, numbers and _',
29790         /**
29791          * The keystroke filter mask to be applied on alphanumeric input
29792          * @type RegExp
29793          */
29794         'alphanumMask' : /[a-z0-9_]/i
29795     };
29796 }();//<script type="text/javascript">
29797
29798 /**
29799  * @class Roo.form.FCKeditor
29800  * @extends Roo.form.TextArea
29801  * Wrapper around the FCKEditor http://www.fckeditor.net
29802  * @constructor
29803  * Creates a new FCKeditor
29804  * @param {Object} config Configuration options
29805  */
29806 Roo.form.FCKeditor = function(config){
29807     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29808     this.addEvents({
29809          /**
29810          * @event editorinit
29811          * Fired when the editor is initialized - you can add extra handlers here..
29812          * @param {FCKeditor} this
29813          * @param {Object} the FCK object.
29814          */
29815         editorinit : true
29816     });
29817     
29818     
29819 };
29820 Roo.form.FCKeditor.editors = { };
29821 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29822 {
29823     //defaultAutoCreate : {
29824     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29825     //},
29826     // private
29827     /**
29828      * @cfg {Object} fck options - see fck manual for details.
29829      */
29830     fckconfig : false,
29831     
29832     /**
29833      * @cfg {Object} fck toolbar set (Basic or Default)
29834      */
29835     toolbarSet : 'Basic',
29836     /**
29837      * @cfg {Object} fck BasePath
29838      */ 
29839     basePath : '/fckeditor/',
29840     
29841     
29842     frame : false,
29843     
29844     value : '',
29845     
29846    
29847     onRender : function(ct, position)
29848     {
29849         if(!this.el){
29850             this.defaultAutoCreate = {
29851                 tag: "textarea",
29852                 style:"width:300px;height:60px;",
29853                 autocomplete: "off"
29854             };
29855         }
29856         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29857         /*
29858         if(this.grow){
29859             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29860             if(this.preventScrollbars){
29861                 this.el.setStyle("overflow", "hidden");
29862             }
29863             this.el.setHeight(this.growMin);
29864         }
29865         */
29866         //console.log('onrender' + this.getId() );
29867         Roo.form.FCKeditor.editors[this.getId()] = this;
29868          
29869
29870         this.replaceTextarea() ;
29871         
29872     },
29873     
29874     getEditor : function() {
29875         return this.fckEditor;
29876     },
29877     /**
29878      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29879      * @param {Mixed} value The value to set
29880      */
29881     
29882     
29883     setValue : function(value)
29884     {
29885         //console.log('setValue: ' + value);
29886         
29887         if(typeof(value) == 'undefined') { // not sure why this is happending...
29888             return;
29889         }
29890         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29891         
29892         //if(!this.el || !this.getEditor()) {
29893         //    this.value = value;
29894             //this.setValue.defer(100,this,[value]);    
29895         //    return;
29896         //} 
29897         
29898         if(!this.getEditor()) {
29899             return;
29900         }
29901         
29902         this.getEditor().SetData(value);
29903         
29904         //
29905
29906     },
29907
29908     /**
29909      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29910      * @return {Mixed} value The field value
29911      */
29912     getValue : function()
29913     {
29914         
29915         if (this.frame && this.frame.dom.style.display == 'none') {
29916             return Roo.form.FCKeditor.superclass.getValue.call(this);
29917         }
29918         
29919         if(!this.el || !this.getEditor()) {
29920            
29921            // this.getValue.defer(100,this); 
29922             return this.value;
29923         }
29924        
29925         
29926         var value=this.getEditor().GetData();
29927         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29928         return Roo.form.FCKeditor.superclass.getValue.call(this);
29929         
29930
29931     },
29932
29933     /**
29934      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29935      * @return {Mixed} value The field value
29936      */
29937     getRawValue : function()
29938     {
29939         if (this.frame && this.frame.dom.style.display == 'none') {
29940             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29941         }
29942         
29943         if(!this.el || !this.getEditor()) {
29944             //this.getRawValue.defer(100,this); 
29945             return this.value;
29946             return;
29947         }
29948         
29949         
29950         
29951         var value=this.getEditor().GetData();
29952         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29953         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29954          
29955     },
29956     
29957     setSize : function(w,h) {
29958         
29959         
29960         
29961         //if (this.frame && this.frame.dom.style.display == 'none') {
29962         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29963         //    return;
29964         //}
29965         //if(!this.el || !this.getEditor()) {
29966         //    this.setSize.defer(100,this, [w,h]); 
29967         //    return;
29968         //}
29969         
29970         
29971         
29972         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29973         
29974         this.frame.dom.setAttribute('width', w);
29975         this.frame.dom.setAttribute('height', h);
29976         this.frame.setSize(w,h);
29977         
29978     },
29979     
29980     toggleSourceEdit : function(value) {
29981         
29982       
29983          
29984         this.el.dom.style.display = value ? '' : 'none';
29985         this.frame.dom.style.display = value ?  'none' : '';
29986         
29987     },
29988     
29989     
29990     focus: function(tag)
29991     {
29992         if (this.frame.dom.style.display == 'none') {
29993             return Roo.form.FCKeditor.superclass.focus.call(this);
29994         }
29995         if(!this.el || !this.getEditor()) {
29996             this.focus.defer(100,this, [tag]); 
29997             return;
29998         }
29999         
30000         
30001         
30002         
30003         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30004         this.getEditor().Focus();
30005         if (tgs.length) {
30006             if (!this.getEditor().Selection.GetSelection()) {
30007                 this.focus.defer(100,this, [tag]); 
30008                 return;
30009             }
30010             
30011             
30012             var r = this.getEditor().EditorDocument.createRange();
30013             r.setStart(tgs[0],0);
30014             r.setEnd(tgs[0],0);
30015             this.getEditor().Selection.GetSelection().removeAllRanges();
30016             this.getEditor().Selection.GetSelection().addRange(r);
30017             this.getEditor().Focus();
30018         }
30019         
30020     },
30021     
30022     
30023     
30024     replaceTextarea : function()
30025     {
30026         if ( document.getElementById( this.getId() + '___Frame' ) )
30027             return ;
30028         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30029         //{
30030             // We must check the elements firstly using the Id and then the name.
30031         var oTextarea = document.getElementById( this.getId() );
30032         
30033         var colElementsByName = document.getElementsByName( this.getId() ) ;
30034          
30035         oTextarea.style.display = 'none' ;
30036
30037         if ( oTextarea.tabIndex ) {            
30038             this.TabIndex = oTextarea.tabIndex ;
30039         }
30040         
30041         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30042         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30043         this.frame = Roo.get(this.getId() + '___Frame')
30044     },
30045     
30046     _getConfigHtml : function()
30047     {
30048         var sConfig = '' ;
30049
30050         for ( var o in this.fckconfig ) {
30051             sConfig += sConfig.length > 0  ? '&amp;' : '';
30052             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30053         }
30054
30055         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30056     },
30057     
30058     
30059     _getIFrameHtml : function()
30060     {
30061         var sFile = 'fckeditor.html' ;
30062         /* no idea what this is about..
30063         try
30064         {
30065             if ( (/fcksource=true/i).test( window.top.location.search ) )
30066                 sFile = 'fckeditor.original.html' ;
30067         }
30068         catch (e) { 
30069         */
30070
30071         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30072         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30073         
30074         
30075         var html = '<iframe id="' + this.getId() +
30076             '___Frame" src="' + sLink +
30077             '" width="' + this.width +
30078             '" height="' + this.height + '"' +
30079             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30080             ' frameborder="0" scrolling="no"></iframe>' ;
30081
30082         return html ;
30083     },
30084     
30085     _insertHtmlBefore : function( html, element )
30086     {
30087         if ( element.insertAdjacentHTML )       {
30088             // IE
30089             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30090         } else { // Gecko
30091             var oRange = document.createRange() ;
30092             oRange.setStartBefore( element ) ;
30093             var oFragment = oRange.createContextualFragment( html );
30094             element.parentNode.insertBefore( oFragment, element ) ;
30095         }
30096     }
30097     
30098     
30099   
30100     
30101     
30102     
30103     
30104
30105 });
30106
30107 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30108
30109 function FCKeditor_OnComplete(editorInstance){
30110     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30111     f.fckEditor = editorInstance;
30112     //console.log("loaded");
30113     f.fireEvent('editorinit', f, editorInstance);
30114
30115   
30116
30117  
30118
30119
30120
30121
30122
30123
30124
30125
30126
30127
30128
30129
30130
30131
30132
30133 //<script type="text/javascript">
30134 /**
30135  * @class Roo.form.GridField
30136  * @extends Roo.form.Field
30137  * Embed a grid (or editable grid into a form)
30138  * STATUS ALPHA
30139  * 
30140  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30141  * it needs 
30142  * xgrid.store = Roo.data.Store
30143  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30144  * xgrid.store.reader = Roo.data.JsonReader 
30145  * 
30146  * 
30147  * @constructor
30148  * Creates a new GridField
30149  * @param {Object} config Configuration options
30150  */
30151 Roo.form.GridField = function(config){
30152     Roo.form.GridField.superclass.constructor.call(this, config);
30153      
30154 };
30155
30156 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30157     /**
30158      * @cfg {Number} width  - used to restrict width of grid..
30159      */
30160     width : 100,
30161     /**
30162      * @cfg {Number} height - used to restrict height of grid..
30163      */
30164     height : 50,
30165      /**
30166      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30167          * 
30168          *}
30169      */
30170     xgrid : false, 
30171     /**
30172      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30173      * {tag: "input", type: "checkbox", autocomplete: "off"})
30174      */
30175    // defaultAutoCreate : { tag: 'div' },
30176     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30177     /**
30178      * @cfg {String} addTitle Text to include for adding a title.
30179      */
30180     addTitle : false,
30181     //
30182     onResize : function(){
30183         Roo.form.Field.superclass.onResize.apply(this, arguments);
30184     },
30185
30186     initEvents : function(){
30187         // Roo.form.Checkbox.superclass.initEvents.call(this);
30188         // has no events...
30189        
30190     },
30191
30192
30193     getResizeEl : function(){
30194         return this.wrap;
30195     },
30196
30197     getPositionEl : function(){
30198         return this.wrap;
30199     },
30200
30201     // private
30202     onRender : function(ct, position){
30203         
30204         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30205         var style = this.style;
30206         delete this.style;
30207         
30208         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30209         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30210         this.viewEl = this.wrap.createChild({ tag: 'div' });
30211         if (style) {
30212             this.viewEl.applyStyles(style);
30213         }
30214         if (this.width) {
30215             this.viewEl.setWidth(this.width);
30216         }
30217         if (this.height) {
30218             this.viewEl.setHeight(this.height);
30219         }
30220         //if(this.inputValue !== undefined){
30221         //this.setValue(this.value);
30222         
30223         
30224         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30225         
30226         
30227         this.grid.render();
30228         this.grid.getDataSource().on('remove', this.refreshValue, this);
30229         this.grid.getDataSource().on('update', this.refreshValue, this);
30230         this.grid.on('afteredit', this.refreshValue, this);
30231  
30232     },
30233      
30234     
30235     /**
30236      * Sets the value of the item. 
30237      * @param {String} either an object  or a string..
30238      */
30239     setValue : function(v){
30240         //this.value = v;
30241         v = v || []; // empty set..
30242         // this does not seem smart - it really only affects memoryproxy grids..
30243         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30244             var ds = this.grid.getDataSource();
30245             // assumes a json reader..
30246             var data = {}
30247             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30248             ds.loadData( data);
30249         }
30250         // clear selection so it does not get stale.
30251         if (this.grid.sm) { 
30252             this.grid.sm.clearSelections();
30253         }
30254         
30255         Roo.form.GridField.superclass.setValue.call(this, v);
30256         this.refreshValue();
30257         // should load data in the grid really....
30258     },
30259     
30260     // private
30261     refreshValue: function() {
30262          var val = [];
30263         this.grid.getDataSource().each(function(r) {
30264             val.push(r.data);
30265         });
30266         this.el.dom.value = Roo.encode(val);
30267     }
30268     
30269      
30270     
30271     
30272 });/*
30273  * Based on:
30274  * Ext JS Library 1.1.1
30275  * Copyright(c) 2006-2007, Ext JS, LLC.
30276  *
30277  * Originally Released Under LGPL - original licence link has changed is not relivant.
30278  *
30279  * Fork - LGPL
30280  * <script type="text/javascript">
30281  */
30282 /**
30283  * @class Roo.form.DisplayField
30284  * @extends Roo.form.Field
30285  * A generic Field to display non-editable data.
30286  * @constructor
30287  * Creates a new Display Field item.
30288  * @param {Object} config Configuration options
30289  */
30290 Roo.form.DisplayField = function(config){
30291     Roo.form.DisplayField.superclass.constructor.call(this, config);
30292     
30293 };
30294
30295 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30296     inputType:      'hidden',
30297     allowBlank:     true,
30298     readOnly:         true,
30299     
30300  
30301     /**
30302      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30303      */
30304     focusClass : undefined,
30305     /**
30306      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30307      */
30308     fieldClass: 'x-form-field',
30309     
30310      /**
30311      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30312      */
30313     valueRenderer: undefined,
30314     
30315     width: 100,
30316     /**
30317      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30318      * {tag: "input", type: "checkbox", autocomplete: "off"})
30319      */
30320      
30321  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30322
30323     onResize : function(){
30324         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30325         
30326     },
30327
30328     initEvents : function(){
30329         // Roo.form.Checkbox.superclass.initEvents.call(this);
30330         // has no events...
30331        
30332     },
30333
30334
30335     getResizeEl : function(){
30336         return this.wrap;
30337     },
30338
30339     getPositionEl : function(){
30340         return this.wrap;
30341     },
30342
30343     // private
30344     onRender : function(ct, position){
30345         
30346         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30347         //if(this.inputValue !== undefined){
30348         this.wrap = this.el.wrap();
30349         
30350         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30351         
30352         if (this.bodyStyle) {
30353             this.viewEl.applyStyles(this.bodyStyle);
30354         }
30355         //this.viewEl.setStyle('padding', '2px');
30356         
30357         this.setValue(this.value);
30358         
30359     },
30360 /*
30361     // private
30362     initValue : Roo.emptyFn,
30363
30364   */
30365
30366         // private
30367     onClick : function(){
30368         
30369     },
30370
30371     /**
30372      * Sets the checked state of the checkbox.
30373      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30374      */
30375     setValue : function(v){
30376         this.value = v;
30377         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30378         // this might be called before we have a dom element..
30379         if (!this.viewEl) {
30380             return;
30381         }
30382         this.viewEl.dom.innerHTML = html;
30383         Roo.form.DisplayField.superclass.setValue.call(this, v);
30384
30385     }
30386 });/*
30387  * 
30388  * Licence- LGPL
30389  * 
30390  */
30391
30392 /**
30393  * @class Roo.form.DayPicker
30394  * @extends Roo.form.Field
30395  * A Day picker show [M] [T] [W] ....
30396  * @constructor
30397  * Creates a new Day Picker
30398  * @param {Object} config Configuration options
30399  */
30400 Roo.form.DayPicker= function(config){
30401     Roo.form.DayPicker.superclass.constructor.call(this, config);
30402      
30403 };
30404
30405 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30406     /**
30407      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30408      */
30409     focusClass : undefined,
30410     /**
30411      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30412      */
30413     fieldClass: "x-form-field",
30414    
30415     /**
30416      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30417      * {tag: "input", type: "checkbox", autocomplete: "off"})
30418      */
30419     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30420     
30421    
30422     actionMode : 'viewEl', 
30423     //
30424     // private
30425  
30426     inputType : 'hidden',
30427     
30428      
30429     inputElement: false, // real input element?
30430     basedOn: false, // ????
30431     
30432     isFormField: true, // not sure where this is needed!!!!
30433
30434     onResize : function(){
30435         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30436         if(!this.boxLabel){
30437             this.el.alignTo(this.wrap, 'c-c');
30438         }
30439     },
30440
30441     initEvents : function(){
30442         Roo.form.Checkbox.superclass.initEvents.call(this);
30443         this.el.on("click", this.onClick,  this);
30444         this.el.on("change", this.onClick,  this);
30445     },
30446
30447
30448     getResizeEl : function(){
30449         return this.wrap;
30450     },
30451
30452     getPositionEl : function(){
30453         return this.wrap;
30454     },
30455
30456     
30457     // private
30458     onRender : function(ct, position){
30459         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30460        
30461         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30462         
30463         var r1 = '<table><tr>';
30464         var r2 = '<tr class="x-form-daypick-icons">';
30465         for (var i=0; i < 7; i++) {
30466             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30467             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30468         }
30469         
30470         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30471         viewEl.select('img').on('click', this.onClick, this);
30472         this.viewEl = viewEl;   
30473         
30474         
30475         // this will not work on Chrome!!!
30476         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30477         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30478         
30479         
30480           
30481
30482     },
30483
30484     // private
30485     initValue : Roo.emptyFn,
30486
30487     /**
30488      * Returns the checked state of the checkbox.
30489      * @return {Boolean} True if checked, else false
30490      */
30491     getValue : function(){
30492         return this.el.dom.value;
30493         
30494     },
30495
30496         // private
30497     onClick : function(e){ 
30498         //this.setChecked(!this.checked);
30499         Roo.get(e.target).toggleClass('x-menu-item-checked');
30500         this.refreshValue();
30501         //if(this.el.dom.checked != this.checked){
30502         //    this.setValue(this.el.dom.checked);
30503        // }
30504     },
30505     
30506     // private
30507     refreshValue : function()
30508     {
30509         var val = '';
30510         this.viewEl.select('img',true).each(function(e,i,n)  {
30511             val += e.is(".x-menu-item-checked") ? String(n) : '';
30512         });
30513         this.setValue(val, true);
30514     },
30515
30516     /**
30517      * Sets the checked state of the checkbox.
30518      * On is always based on a string comparison between inputValue and the param.
30519      * @param {Boolean/String} value - the value to set 
30520      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30521      */
30522     setValue : function(v,suppressEvent){
30523         if (!this.el.dom) {
30524             return;
30525         }
30526         var old = this.el.dom.value ;
30527         this.el.dom.value = v;
30528         if (suppressEvent) {
30529             return ;
30530         }
30531          
30532         // update display..
30533         this.viewEl.select('img',true).each(function(e,i,n)  {
30534             
30535             var on = e.is(".x-menu-item-checked");
30536             var newv = v.indexOf(String(n)) > -1;
30537             if (on != newv) {
30538                 e.toggleClass('x-menu-item-checked');
30539             }
30540             
30541         });
30542         
30543         
30544         this.fireEvent('change', this, v, old);
30545         
30546         
30547     },
30548    
30549     // handle setting of hidden value by some other method!!?!?
30550     setFromHidden: function()
30551     {
30552         if(!this.el){
30553             return;
30554         }
30555         //console.log("SET FROM HIDDEN");
30556         //alert('setFrom hidden');
30557         this.setValue(this.el.dom.value);
30558     },
30559     
30560     onDestroy : function()
30561     {
30562         if(this.viewEl){
30563             Roo.get(this.viewEl).remove();
30564         }
30565          
30566         Roo.form.DayPicker.superclass.onDestroy.call(this);
30567     }
30568
30569 });/*
30570  * RooJS Library 1.1.1
30571  * Copyright(c) 2008-2011  Alan Knowles
30572  *
30573  * License - LGPL
30574  */
30575  
30576
30577 /**
30578  * @class Roo.form.ComboCheck
30579  * @extends Roo.form.ComboBox
30580  * A combobox for multiple select items.
30581  *
30582  * FIXME - could do with a reset button..
30583  * 
30584  * @constructor
30585  * Create a new ComboCheck
30586  * @param {Object} config Configuration options
30587  */
30588 Roo.form.ComboCheck = function(config){
30589     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30590     // should verify some data...
30591     // like
30592     // hiddenName = required..
30593     // displayField = required
30594     // valudField == required
30595     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30596     var _t = this;
30597     Roo.each(req, function(e) {
30598         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30599             throw "Roo.form.ComboCheck : missing value for: " + e;
30600         }
30601     });
30602     
30603     
30604 };
30605
30606 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30607      
30608      
30609     editable : false,
30610      
30611     selectedClass: 'x-menu-item-checked', 
30612     
30613     // private
30614     onRender : function(ct, position){
30615         var _t = this;
30616         
30617         
30618         
30619         if(!this.tpl){
30620             var cls = 'x-combo-list';
30621
30622             
30623             this.tpl =  new Roo.Template({
30624                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30625                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30626                    '<span>{' + this.displayField + '}</span>' +
30627                     '</div>' 
30628                 
30629             });
30630         }
30631  
30632         
30633         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30634         this.view.singleSelect = false;
30635         this.view.multiSelect = true;
30636         this.view.toggleSelect = true;
30637         this.pageTb.add(new Roo.Toolbar.Fill(), {
30638             
30639             text: 'Done',
30640             handler: function()
30641             {
30642                 _t.collapse();
30643             }
30644         });
30645     },
30646     
30647     onViewOver : function(e, t){
30648         // do nothing...
30649         return;
30650         
30651     },
30652     
30653     onViewClick : function(doFocus,index){
30654         return;
30655         
30656     },
30657     select: function () {
30658         //Roo.log("SELECT CALLED");
30659     },
30660      
30661     selectByValue : function(xv, scrollIntoView){
30662         var ar = this.getValueArray();
30663         var sels = [];
30664         
30665         Roo.each(ar, function(v) {
30666             if(v === undefined || v === null){
30667                 return;
30668             }
30669             var r = this.findRecord(this.valueField, v);
30670             if(r){
30671                 sels.push(this.store.indexOf(r))
30672                 
30673             }
30674         },this);
30675         this.view.select(sels);
30676         return false;
30677     },
30678     
30679     
30680     
30681     onSelect : function(record, index){
30682        // Roo.log("onselect Called");
30683        // this is only called by the clear button now..
30684         this.view.clearSelections();
30685         this.setValue('[]');
30686         if (this.value != this.valueBefore) {
30687             this.fireEvent('change', this, this.value, this.valueBefore);
30688             this.valueBefore = this.value;
30689         }
30690     },
30691     getValueArray : function()
30692     {
30693         var ar = [] ;
30694         
30695         try {
30696             //Roo.log(this.value);
30697             if (typeof(this.value) == 'undefined') {
30698                 return [];
30699             }
30700             var ar = Roo.decode(this.value);
30701             return  ar instanceof Array ? ar : []; //?? valid?
30702             
30703         } catch(e) {
30704             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30705             return [];
30706         }
30707          
30708     },
30709     expand : function ()
30710     {
30711         
30712         Roo.form.ComboCheck.superclass.expand.call(this);
30713         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30714         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30715         
30716
30717     },
30718     
30719     collapse : function(){
30720         Roo.form.ComboCheck.superclass.collapse.call(this);
30721         var sl = this.view.getSelectedIndexes();
30722         var st = this.store;
30723         var nv = [];
30724         var tv = [];
30725         var r;
30726         Roo.each(sl, function(i) {
30727             r = st.getAt(i);
30728             nv.push(r.get(this.valueField));
30729         },this);
30730         this.setValue(Roo.encode(nv));
30731         if (this.value != this.valueBefore) {
30732
30733             this.fireEvent('change', this, this.value, this.valueBefore);
30734             this.valueBefore = this.value;
30735         }
30736         
30737     },
30738     
30739     setValue : function(v){
30740         // Roo.log(v);
30741         this.value = v;
30742         
30743         var vals = this.getValueArray();
30744         var tv = [];
30745         Roo.each(vals, function(k) {
30746             var r = this.findRecord(this.valueField, k);
30747             if(r){
30748                 tv.push(r.data[this.displayField]);
30749             }else if(this.valueNotFoundText !== undefined){
30750                 tv.push( this.valueNotFoundText );
30751             }
30752         },this);
30753        // Roo.log(tv);
30754         
30755         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30756         this.hiddenField.value = v;
30757         this.value = v;
30758     }
30759     
30760 });/*
30761  * Based on:
30762  * Ext JS Library 1.1.1
30763  * Copyright(c) 2006-2007, Ext JS, LLC.
30764  *
30765  * Originally Released Under LGPL - original licence link has changed is not relivant.
30766  *
30767  * Fork - LGPL
30768  * <script type="text/javascript">
30769  */
30770  
30771 /**
30772  * @class Roo.form.Signature
30773  * @extends Roo.form.Field
30774  * Signature field.  
30775  * @constructor
30776  * 
30777  * @param {Object} config Configuration options
30778  */
30779
30780 Roo.form.Signature = function(config){
30781     Roo.form.Signature.superclass.constructor.call(this, config);
30782     
30783     this.addEvents({// not in used??
30784          /**
30785          * @event confirm
30786          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30787              * @param {Roo.form.Signature} combo This combo box
30788              */
30789         'confirm' : true,
30790         /**
30791          * @event reset
30792          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30793              * @param {Roo.form.ComboBox} combo This combo box
30794              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30795              */
30796         'reset' : true
30797     });
30798 };
30799
30800 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30801     /**
30802      * @cfg {Object} labels Label to use when rendering a form.
30803      * defaults to 
30804      * labels : { 
30805      *      clear : "Clear",
30806      *      confirm : "Confirm"
30807      *  }
30808      */
30809     labels : { 
30810         clear : "Clear",
30811         confirm : "Confirm"
30812     },
30813     /**
30814      * @cfg {Number} width The signature panel width (defaults to 300)
30815      */
30816     width: 300,
30817     /**
30818      * @cfg {Number} height The signature panel height (defaults to 100)
30819      */
30820     height : 100,
30821     /**
30822      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30823      */
30824     allowBlank : false,
30825     
30826     //private
30827     // {Object} signPanel The signature SVG panel element (defaults to {})
30828     signPanel : {},
30829     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30830     isMouseDown : false,
30831     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30832     isConfirmed : false,
30833     // {String} signatureTmp SVG mapping string (defaults to empty string)
30834     signatureTmp : '',
30835     
30836     
30837     defaultAutoCreate : { // modified by initCompnoent..
30838         tag: "input",
30839         type:"hidden"
30840     },
30841
30842     // private
30843     onRender : function(ct, position){
30844         
30845         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30846         
30847         this.wrap = this.el.wrap({
30848             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30849         });
30850         
30851         this.createToolbar(this);
30852         this.signPanel = this.wrap.createChild({
30853                 tag: 'div',
30854                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30855             }, this.el
30856         );
30857             
30858         this.svgID = Roo.id();
30859         this.svgEl = this.signPanel.createChild({
30860               xmlns : 'http://www.w3.org/2000/svg',
30861               tag : 'svg',
30862               id : this.svgID + "-svg",
30863               width: this.width,
30864               height: this.height,
30865               viewBox: '0 0 '+this.width+' '+this.height,
30866               cn : [
30867                 {
30868                     tag: "rect",
30869                     id: this.svgID + "-svg-r",
30870                     width: this.width,
30871                     height: this.height,
30872                     fill: "#ffa"
30873                 },
30874                 {
30875                     tag: "line",
30876                     id: this.svgID + "-svg-l",
30877                     x1: "0", // start
30878                     y1: (this.height*0.8), // start set the line in 80% of height
30879                     x2: this.width, // end
30880                     y2: (this.height*0.8), // end set the line in 80% of height
30881                     'stroke': "#666",
30882                     'stroke-width': "1",
30883                     'stroke-dasharray': "3",
30884                     'shape-rendering': "crispEdges",
30885                     'pointer-events': "none"
30886                 },
30887                 {
30888                     tag: "path",
30889                     id: this.svgID + "-svg-p",
30890                     'stroke': "navy",
30891                     'stroke-width': "3",
30892                     'fill': "none",
30893                     'pointer-events': 'none'
30894                 }
30895               ]
30896         });
30897         this.createSVG();
30898         this.svgBox = this.svgEl.dom.getScreenCTM();
30899     },
30900     createSVG : function(){ 
30901         var svg = this.signPanel;
30902         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30903         var t = this;
30904
30905         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30906         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30907         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30908         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30909         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30910         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30911         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30912         
30913     },
30914     isTouchEvent : function(e){
30915         return e.type.match(/^touch/);
30916     },
30917     getCoords : function (e) {
30918         var pt    = this.svgEl.dom.createSVGPoint();
30919         pt.x = e.clientX; 
30920         pt.y = e.clientY;
30921         if (this.isTouchEvent(e)) {
30922             pt.x =  e.targetTouches[0].clientX 
30923             pt.y = e.targetTouches[0].clientY;
30924         }
30925         var a = this.svgEl.dom.getScreenCTM();
30926         var b = a.inverse();
30927         var mx = pt.matrixTransform(b);
30928         return mx.x + ',' + mx.y;
30929     },
30930     //mouse event headler 
30931     down : function (e) {
30932         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30933         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30934         
30935         this.isMouseDown = true;
30936         
30937         e.preventDefault();
30938     },
30939     move : function (e) {
30940         if (this.isMouseDown) {
30941             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30942             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30943         }
30944         
30945         e.preventDefault();
30946     },
30947     up : function (e) {
30948         this.isMouseDown = false;
30949         var sp = this.signatureTmp.split(' ');
30950         
30951         if(sp.length > 1){
30952             if(!sp[sp.length-2].match(/^L/)){
30953                 sp.pop();
30954                 sp.pop();
30955                 sp.push("");
30956                 this.signatureTmp = sp.join(" ");
30957             }
30958         }
30959         if(this.getValue() != this.signatureTmp){
30960             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30961             this.isConfirmed = false;
30962         }
30963         e.preventDefault();
30964     },
30965     
30966     /**
30967      * Protected method that will not generally be called directly. It
30968      * is called when the editor creates its toolbar. Override this method if you need to
30969      * add custom toolbar buttons.
30970      * @param {HtmlEditor} editor
30971      */
30972     createToolbar : function(editor){
30973          function btn(id, toggle, handler){
30974             var xid = fid + '-'+ id ;
30975             return {
30976                 id : xid,
30977                 cmd : id,
30978                 cls : 'x-btn-icon x-edit-'+id,
30979                 enableToggle:toggle !== false,
30980                 scope: editor, // was editor...
30981                 handler:handler||editor.relayBtnCmd,
30982                 clickEvent:'mousedown',
30983                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
30984                 tabIndex:-1
30985             };
30986         }
30987         
30988         
30989         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
30990         this.tb = tb;
30991         this.tb.add(
30992            {
30993                 cls : ' x-signature-btn x-signature-'+id,
30994                 scope: editor, // was editor...
30995                 handler: this.reset,
30996                 clickEvent:'mousedown',
30997                 text: this.labels.clear
30998             },
30999             {
31000                  xtype : 'Fill',
31001                  xns: Roo.Toolbar
31002             }, 
31003             {
31004                 cls : '  x-signature-btn x-signature-'+id,
31005                 scope: editor, // was editor...
31006                 handler: this.confirmHandler,
31007                 clickEvent:'mousedown',
31008                 text: this.labels.confirm
31009             }
31010         );
31011     
31012     },
31013     //public
31014     /**
31015      * when user is clicked confirm then show this image.....
31016      * 
31017      * @return {String} Image Data URI
31018      */
31019     getImageDataURI : function(){
31020         var svg = this.svgEl.dom.parentNode.innerHTML;
31021         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31022         return src; 
31023     },
31024     /**
31025      * 
31026      * @return {Boolean} this.isConfirmed
31027      */
31028     getConfirmed : function(){
31029         return this.isConfirmed;
31030     },
31031     /**
31032      * 
31033      * @return {Number} this.width
31034      */
31035     getWidth : function(){
31036         return this.width;
31037     },
31038     /**
31039      * 
31040      * @return {Number} this.height
31041      */
31042     getHeight : function(){
31043         return this.height;
31044     },
31045     // private
31046     getSignature : function(){
31047         return this.signatureTmp;
31048     },
31049     // private
31050     reset : function(){
31051         this.signatureTmp = '';
31052         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31053         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31054         this.isConfirmed = false;
31055         Roo.form.Signature.superclass.reset.call(this);
31056     },
31057     setSignature : function(s){
31058         this.signatureTmp = s;
31059         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31060         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31061         this.setValue(s);
31062         this.isConfirmed = false;
31063         Roo.form.Signature.superclass.reset.call(this);
31064     }, 
31065     test : function(){
31066 //        Roo.log(this.signPanel.dom.contentWindow.up())
31067     },
31068     //private
31069     setConfirmed : function(){
31070         
31071         
31072         
31073 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31074     },
31075     // private
31076     confirmHandler : function(){
31077         if(!this.getSignature()){
31078             return;
31079         }
31080         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31081         this.setValue(this.getSignature());
31082         this.isConfirmed = true;
31083         
31084         Roo.log('in confirm clicked');
31085 //        
31086 //        var valid = true;
31087 //        this.items.each(function(f){
31088 //            if(!f.isValid(true)){
31089 //                valid = false;
31090 //                return false;
31091 //            }
31092 //        });
31093 //        for(var i = 0, len = this.buttons.length; i < len; i++){
31094 //            var btn = this.buttons[i];
31095 //            if(btn.formBind === true && btn.disabled === valid){
31096 //                btn.setDisabled(!valid);
31097 //            }
31098 //        }
31099         this.fireEvent('confirm', this);
31100     },
31101     // private
31102     // Subclasses should provide the validation implementation by overriding this
31103     validateValue : function(value){
31104         if(this.allowBlank){
31105             return true;
31106         }
31107         
31108         if(this.isConfirmed){
31109             return true;
31110         }
31111         return false;
31112     }
31113 });//<script type="text/javasscript">
31114  
31115
31116 /**
31117  * @class Roo.DDView
31118  * A DnD enabled version of Roo.View.
31119  * @param {Element/String} container The Element in which to create the View.
31120  * @param {String} tpl The template string used to create the markup for each element of the View
31121  * @param {Object} config The configuration properties. These include all the config options of
31122  * {@link Roo.View} plus some specific to this class.<br>
31123  * <p>
31124  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31125  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31126  * <p>
31127  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31128 .x-view-drag-insert-above {
31129         border-top:1px dotted #3366cc;
31130 }
31131 .x-view-drag-insert-below {
31132         border-bottom:1px dotted #3366cc;
31133 }
31134 </code></pre>
31135  * 
31136  */
31137  
31138 Roo.DDView = function(container, tpl, config) {
31139     Roo.DDView.superclass.constructor.apply(this, arguments);
31140     this.getEl().setStyle("outline", "0px none");
31141     this.getEl().unselectable();
31142     if (this.dragGroup) {
31143                 this.setDraggable(this.dragGroup.split(","));
31144     }
31145     if (this.dropGroup) {
31146                 this.setDroppable(this.dropGroup.split(","));
31147     }
31148     if (this.deletable) {
31149         this.setDeletable();
31150     }
31151     this.isDirtyFlag = false;
31152         this.addEvents({
31153                 "drop" : true
31154         });
31155 };
31156
31157 Roo.extend(Roo.DDView, Roo.View, {
31158 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31159 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31160 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31161 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31162
31163         isFormField: true,
31164
31165         reset: Roo.emptyFn,
31166         
31167         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31168
31169         validate: function() {
31170                 return true;
31171         },
31172         
31173         destroy: function() {
31174                 this.purgeListeners();
31175                 this.getEl.removeAllListeners();
31176                 this.getEl().remove();
31177                 if (this.dragZone) {
31178                         if (this.dragZone.destroy) {
31179                                 this.dragZone.destroy();
31180                         }
31181                 }
31182                 if (this.dropZone) {
31183                         if (this.dropZone.destroy) {
31184                                 this.dropZone.destroy();
31185                         }
31186                 }
31187         },
31188
31189 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31190         getName: function() {
31191                 return this.name;
31192         },
31193
31194 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31195         setValue: function(v) {
31196                 if (!this.store) {
31197                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31198                 }
31199                 var data = {};
31200                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31201                 this.store.proxy = new Roo.data.MemoryProxy(data);
31202                 this.store.load();
31203         },
31204
31205 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31206         getValue: function() {
31207                 var result = '(';
31208                 this.store.each(function(rec) {
31209                         result += rec.id + ',';
31210                 });
31211                 return result.substr(0, result.length - 1) + ')';
31212         },
31213         
31214         getIds: function() {
31215                 var i = 0, result = new Array(this.store.getCount());
31216                 this.store.each(function(rec) {
31217                         result[i++] = rec.id;
31218                 });
31219                 return result;
31220         },
31221         
31222         isDirty: function() {
31223                 return this.isDirtyFlag;
31224         },
31225
31226 /**
31227  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31228  *      whole Element becomes the target, and this causes the drop gesture to append.
31229  */
31230     getTargetFromEvent : function(e) {
31231                 var target = e.getTarget();
31232                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31233                 target = target.parentNode;
31234                 }
31235                 if (!target) {
31236                         target = this.el.dom.lastChild || this.el.dom;
31237                 }
31238                 return target;
31239     },
31240
31241 /**
31242  *      Create the drag data which consists of an object which has the property "ddel" as
31243  *      the drag proxy element. 
31244  */
31245     getDragData : function(e) {
31246         var target = this.findItemFromChild(e.getTarget());
31247                 if(target) {
31248                         this.handleSelection(e);
31249                         var selNodes = this.getSelectedNodes();
31250             var dragData = {
31251                 source: this,
31252                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31253                 nodes: selNodes,
31254                 records: []
31255                         };
31256                         var selectedIndices = this.getSelectedIndexes();
31257                         for (var i = 0; i < selectedIndices.length; i++) {
31258                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31259                         }
31260                         if (selNodes.length == 1) {
31261                                 dragData.ddel = target.cloneNode(true); // the div element
31262                         } else {
31263                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31264                                 div.className = 'multi-proxy';
31265                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31266                                         div.appendChild(selNodes[i].cloneNode(true));
31267                                 }
31268                                 dragData.ddel = div;
31269                         }
31270             //console.log(dragData)
31271             //console.log(dragData.ddel.innerHTML)
31272                         return dragData;
31273                 }
31274         //console.log('nodragData')
31275                 return false;
31276     },
31277     
31278 /**     Specify to which ddGroup items in this DDView may be dragged. */
31279     setDraggable: function(ddGroup) {
31280         if (ddGroup instanceof Array) {
31281                 Roo.each(ddGroup, this.setDraggable, this);
31282                 return;
31283         }
31284         if (this.dragZone) {
31285                 this.dragZone.addToGroup(ddGroup);
31286         } else {
31287                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31288                                 containerScroll: true,
31289                                 ddGroup: ddGroup 
31290
31291                         });
31292 //                      Draggability implies selection. DragZone's mousedown selects the element.
31293                         if (!this.multiSelect) { this.singleSelect = true; }
31294
31295 //                      Wire the DragZone's handlers up to methods in *this*
31296                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31297                 }
31298     },
31299
31300 /**     Specify from which ddGroup this DDView accepts drops. */
31301     setDroppable: function(ddGroup) {
31302         if (ddGroup instanceof Array) {
31303                 Roo.each(ddGroup, this.setDroppable, this);
31304                 return;
31305         }
31306         if (this.dropZone) {
31307                 this.dropZone.addToGroup(ddGroup);
31308         } else {
31309                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31310                                 containerScroll: true,
31311                                 ddGroup: ddGroup
31312                         });
31313
31314 //                      Wire the DropZone's handlers up to methods in *this*
31315                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31316                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31317                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31318                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31319                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31320                 }
31321     },
31322
31323 /**     Decide whether to drop above or below a View node. */
31324     getDropPoint : function(e, n, dd){
31325         if (n == this.el.dom) { return "above"; }
31326                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31327                 var c = t + (b - t) / 2;
31328                 var y = Roo.lib.Event.getPageY(e);
31329                 if(y <= c) {
31330                         return "above";
31331                 }else{
31332                         return "below";
31333                 }
31334     },
31335
31336     onNodeEnter : function(n, dd, e, data){
31337                 return false;
31338     },
31339     
31340     onNodeOver : function(n, dd, e, data){
31341                 var pt = this.getDropPoint(e, n, dd);
31342                 // set the insert point style on the target node
31343                 var dragElClass = this.dropNotAllowed;
31344                 if (pt) {
31345                         var targetElClass;
31346                         if (pt == "above"){
31347                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31348                                 targetElClass = "x-view-drag-insert-above";
31349                         } else {
31350                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31351                                 targetElClass = "x-view-drag-insert-below";
31352                         }
31353                         if (this.lastInsertClass != targetElClass){
31354                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31355                                 this.lastInsertClass = targetElClass;
31356                         }
31357                 }
31358                 return dragElClass;
31359         },
31360
31361     onNodeOut : function(n, dd, e, data){
31362                 this.removeDropIndicators(n);
31363     },
31364
31365     onNodeDrop : function(n, dd, e, data){
31366         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31367                 return false;
31368         }
31369         var pt = this.getDropPoint(e, n, dd);
31370                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
31371                 if (pt == "below") { insertAt++; }
31372                 for (var i = 0; i < data.records.length; i++) {
31373                         var r = data.records[i];
31374                         var dup = this.store.getById(r.id);
31375                         if (dup && (dd != this.dragZone)) {
31376                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
31377                         } else {
31378                                 if (data.copy) {
31379                                         this.store.insert(insertAt++, r.copy());
31380                                 } else {
31381                                         data.source.isDirtyFlag = true;
31382                                         r.store.remove(r);
31383                                         this.store.insert(insertAt++, r);
31384                                 }
31385                                 this.isDirtyFlag = true;
31386                         }
31387                 }
31388                 this.dragZone.cachedTarget = null;
31389                 return true;
31390     },
31391
31392     removeDropIndicators : function(n){
31393                 if(n){
31394                         Roo.fly(n).removeClass([
31395                                 "x-view-drag-insert-above",
31396                                 "x-view-drag-insert-below"]);
31397                         this.lastInsertClass = "_noclass";
31398                 }
31399     },
31400
31401 /**
31402  *      Utility method. Add a delete option to the DDView's context menu.
31403  *      @param {String} imageUrl The URL of the "delete" icon image.
31404  */
31405         setDeletable: function(imageUrl) {
31406                 if (!this.singleSelect && !this.multiSelect) {
31407                         this.singleSelect = true;
31408                 }
31409                 var c = this.getContextMenu();
31410                 this.contextMenu.on("itemclick", function(item) {
31411                         switch (item.id) {
31412                                 case "delete":
31413                                         this.remove(this.getSelectedIndexes());
31414                                         break;
31415                         }
31416                 }, this);
31417                 this.contextMenu.add({
31418                         icon: imageUrl,
31419                         id: "delete",
31420                         text: 'Delete'
31421                 });
31422         },
31423         
31424 /**     Return the context menu for this DDView. */
31425         getContextMenu: function() {
31426                 if (!this.contextMenu) {
31427 //                      Create the View's context menu
31428                         this.contextMenu = new Roo.menu.Menu({
31429                                 id: this.id + "-contextmenu"
31430                         });
31431                         this.el.on("contextmenu", this.showContextMenu, this);
31432                 }
31433                 return this.contextMenu;
31434         },
31435         
31436         disableContextMenu: function() {
31437                 if (this.contextMenu) {
31438                         this.el.un("contextmenu", this.showContextMenu, this);
31439                 }
31440         },
31441
31442         showContextMenu: function(e, item) {
31443         item = this.findItemFromChild(e.getTarget());
31444                 if (item) {
31445                         e.stopEvent();
31446                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
31447                         this.contextMenu.showAt(e.getXY());
31448             }
31449     },
31450
31451 /**
31452  *      Remove {@link Roo.data.Record}s at the specified indices.
31453  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
31454  */
31455     remove: function(selectedIndices) {
31456                 selectedIndices = [].concat(selectedIndices);
31457                 for (var i = 0; i < selectedIndices.length; i++) {
31458                         var rec = this.store.getAt(selectedIndices[i]);
31459                         this.store.remove(rec);
31460                 }
31461     },
31462
31463 /**
31464  *      Double click fires the event, but also, if this is draggable, and there is only one other
31465  *      related DropZone, it transfers the selected node.
31466  */
31467     onDblClick : function(e){
31468         var item = this.findItemFromChild(e.getTarget());
31469         if(item){
31470             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
31471                 return false;
31472             }
31473             if (this.dragGroup) {
31474                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
31475                     while (targets.indexOf(this.dropZone) > -1) {
31476                             targets.remove(this.dropZone);
31477                                 }
31478                     if (targets.length == 1) {
31479                                         this.dragZone.cachedTarget = null;
31480                         var el = Roo.get(targets[0].getEl());
31481                         var box = el.getBox(true);
31482                         targets[0].onNodeDrop(el.dom, {
31483                                 target: el.dom,
31484                                 xy: [box.x, box.y + box.height - 1]
31485                         }, null, this.getDragData(e));
31486                     }
31487                 }
31488         }
31489     },
31490     
31491     handleSelection: function(e) {
31492                 this.dragZone.cachedTarget = null;
31493         var item = this.findItemFromChild(e.getTarget());
31494         if (!item) {
31495                 this.clearSelections(true);
31496                 return;
31497         }
31498                 if (item && (this.multiSelect || this.singleSelect)){
31499                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
31500                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
31501                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
31502                                 this.unselect(item);
31503                         } else {
31504                                 this.select(item, this.multiSelect && e.ctrlKey);
31505                                 this.lastSelection = item;
31506                         }
31507                 }
31508     },
31509
31510     onItemClick : function(item, index, e){
31511                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
31512                         return false;
31513                 }
31514                 return true;
31515     },
31516
31517     unselect : function(nodeInfo, suppressEvent){
31518                 var node = this.getNode(nodeInfo);
31519                 if(node && this.isSelected(node)){
31520                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
31521                                 Roo.fly(node).removeClass(this.selectedClass);
31522                                 this.selections.remove(node);
31523                                 if(!suppressEvent){
31524                                         this.fireEvent("selectionchange", this, this.selections);
31525                                 }
31526                         }
31527                 }
31528     }
31529 });
31530 /*
31531  * Based on:
31532  * Ext JS Library 1.1.1
31533  * Copyright(c) 2006-2007, Ext JS, LLC.
31534  *
31535  * Originally Released Under LGPL - original licence link has changed is not relivant.
31536  *
31537  * Fork - LGPL
31538  * <script type="text/javascript">
31539  */
31540  
31541 /**
31542  * @class Roo.LayoutManager
31543  * @extends Roo.util.Observable
31544  * Base class for layout managers.
31545  */
31546 Roo.LayoutManager = function(container, config){
31547     Roo.LayoutManager.superclass.constructor.call(this);
31548     this.el = Roo.get(container);
31549     // ie scrollbar fix
31550     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31551         document.body.scroll = "no";
31552     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31553         this.el.position('relative');
31554     }
31555     this.id = this.el.id;
31556     this.el.addClass("x-layout-container");
31557     /** false to disable window resize monitoring @type Boolean */
31558     this.monitorWindowResize = true;
31559     this.regions = {};
31560     this.addEvents({
31561         /**
31562          * @event layout
31563          * Fires when a layout is performed. 
31564          * @param {Roo.LayoutManager} this
31565          */
31566         "layout" : true,
31567         /**
31568          * @event regionresized
31569          * Fires when the user resizes a region. 
31570          * @param {Roo.LayoutRegion} region The resized region
31571          * @param {Number} newSize The new size (width for east/west, height for north/south)
31572          */
31573         "regionresized" : true,
31574         /**
31575          * @event regioncollapsed
31576          * Fires when a region is collapsed. 
31577          * @param {Roo.LayoutRegion} region The collapsed region
31578          */
31579         "regioncollapsed" : true,
31580         /**
31581          * @event regionexpanded
31582          * Fires when a region is expanded.  
31583          * @param {Roo.LayoutRegion} region The expanded region
31584          */
31585         "regionexpanded" : true
31586     });
31587     this.updating = false;
31588     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31589 };
31590
31591 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
31592     /**
31593      * Returns true if this layout is currently being updated
31594      * @return {Boolean}
31595      */
31596     isUpdating : function(){
31597         return this.updating; 
31598     },
31599     
31600     /**
31601      * Suspend the LayoutManager from doing auto-layouts while
31602      * making multiple add or remove calls
31603      */
31604     beginUpdate : function(){
31605         this.updating = true;    
31606     },
31607     
31608     /**
31609      * Restore auto-layouts and optionally disable the manager from performing a layout
31610      * @param {Boolean} noLayout true to disable a layout update 
31611      */
31612     endUpdate : function(noLayout){
31613         this.updating = false;
31614         if(!noLayout){
31615             this.layout();
31616         }    
31617     },
31618     
31619     layout: function(){
31620         
31621     },
31622     
31623     onRegionResized : function(region, newSize){
31624         this.fireEvent("regionresized", region, newSize);
31625         this.layout();
31626     },
31627     
31628     onRegionCollapsed : function(region){
31629         this.fireEvent("regioncollapsed", region);
31630     },
31631     
31632     onRegionExpanded : function(region){
31633         this.fireEvent("regionexpanded", region);
31634     },
31635         
31636     /**
31637      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31638      * performs box-model adjustments.
31639      * @return {Object} The size as an object {width: (the width), height: (the height)}
31640      */
31641     getViewSize : function(){
31642         var size;
31643         if(this.el.dom != document.body){
31644             size = this.el.getSize();
31645         }else{
31646             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31647         }
31648         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31649         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31650         return size;
31651     },
31652     
31653     /**
31654      * Returns the Element this layout is bound to.
31655      * @return {Roo.Element}
31656      */
31657     getEl : function(){
31658         return this.el;
31659     },
31660     
31661     /**
31662      * Returns the specified region.
31663      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31664      * @return {Roo.LayoutRegion}
31665      */
31666     getRegion : function(target){
31667         return this.regions[target.toLowerCase()];
31668     },
31669     
31670     onWindowResize : function(){
31671         if(this.monitorWindowResize){
31672             this.layout();
31673         }
31674     }
31675 });/*
31676  * Based on:
31677  * Ext JS Library 1.1.1
31678  * Copyright(c) 2006-2007, Ext JS, LLC.
31679  *
31680  * Originally Released Under LGPL - original licence link has changed is not relivant.
31681  *
31682  * Fork - LGPL
31683  * <script type="text/javascript">
31684  */
31685 /**
31686  * @class Roo.BorderLayout
31687  * @extends Roo.LayoutManager
31688  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31689  * please see: <br><br>
31690  * <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>
31691  * <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>
31692  * Example:
31693  <pre><code>
31694  var layout = new Roo.BorderLayout(document.body, {
31695     north: {
31696         initialSize: 25,
31697         titlebar: false
31698     },
31699     west: {
31700         split:true,
31701         initialSize: 200,
31702         minSize: 175,
31703         maxSize: 400,
31704         titlebar: true,
31705         collapsible: true
31706     },
31707     east: {
31708         split:true,
31709         initialSize: 202,
31710         minSize: 175,
31711         maxSize: 400,
31712         titlebar: true,
31713         collapsible: true
31714     },
31715     south: {
31716         split:true,
31717         initialSize: 100,
31718         minSize: 100,
31719         maxSize: 200,
31720         titlebar: true,
31721         collapsible: true
31722     },
31723     center: {
31724         titlebar: true,
31725         autoScroll:true,
31726         resizeTabs: true,
31727         minTabWidth: 50,
31728         preferredTabWidth: 150
31729     }
31730 });
31731
31732 // shorthand
31733 var CP = Roo.ContentPanel;
31734
31735 layout.beginUpdate();
31736 layout.add("north", new CP("north", "North"));
31737 layout.add("south", new CP("south", {title: "South", closable: true}));
31738 layout.add("west", new CP("west", {title: "West"}));
31739 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
31740 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
31741 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
31742 layout.getRegion("center").showPanel("center1");
31743 layout.endUpdate();
31744 </code></pre>
31745
31746 <b>The container the layout is rendered into can be either the body element or any other element.
31747 If it is not the body element, the container needs to either be an absolute positioned element,
31748 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31749 the container size if it is not the body element.</b>
31750
31751 * @constructor
31752 * Create a new BorderLayout
31753 * @param {String/HTMLElement/Element} container The container this layout is bound to
31754 * @param {Object} config Configuration options
31755  */
31756 Roo.BorderLayout = function(container, config){
31757     config = config || {};
31758     Roo.BorderLayout.superclass.constructor.call(this, container, config);
31759     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
31760     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
31761         var target = this.factory.validRegions[i];
31762         if(config[target]){
31763             this.addRegion(target, config[target]);
31764         }
31765     }
31766 };
31767
31768 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31769     /**
31770      * Creates and adds a new region if it doesn't already exist.
31771      * @param {String} target The target region key (north, south, east, west or center).
31772      * @param {Object} config The regions config object
31773      * @return {BorderLayoutRegion} The new region
31774      */
31775     addRegion : function(target, config){
31776         if(!this.regions[target]){
31777             var r = this.factory.create(target, this, config);
31778             this.bindRegion(target, r);
31779         }
31780         return this.regions[target];
31781     },
31782
31783     // private (kinda)
31784     bindRegion : function(name, r){
31785         this.regions[name] = r;
31786         r.on("visibilitychange", this.layout, this);
31787         r.on("paneladded", this.layout, this);
31788         r.on("panelremoved", this.layout, this);
31789         r.on("invalidated", this.layout, this);
31790         r.on("resized", this.onRegionResized, this);
31791         r.on("collapsed", this.onRegionCollapsed, this);
31792         r.on("expanded", this.onRegionExpanded, this);
31793     },
31794
31795     /**
31796      * Performs a layout update.
31797      */
31798     layout : function(){
31799         if(this.updating) return;
31800         var size = this.getViewSize();
31801         var w = size.width;
31802         var h = size.height;
31803         var centerW = w;
31804         var centerH = h;
31805         var centerY = 0;
31806         var centerX = 0;
31807         //var x = 0, y = 0;
31808
31809         var rs = this.regions;
31810         var north = rs["north"];
31811         var south = rs["south"]; 
31812         var west = rs["west"];
31813         var east = rs["east"];
31814         var center = rs["center"];
31815         //if(this.hideOnLayout){ // not supported anymore
31816             //c.el.setStyle("display", "none");
31817         //}
31818         if(north && north.isVisible()){
31819             var b = north.getBox();
31820             var m = north.getMargins();
31821             b.width = w - (m.left+m.right);
31822             b.x = m.left;
31823             b.y = m.top;
31824             centerY = b.height + b.y + m.bottom;
31825             centerH -= centerY;
31826             north.updateBox(this.safeBox(b));
31827         }
31828         if(south && south.isVisible()){
31829             var b = south.getBox();
31830             var m = south.getMargins();
31831             b.width = w - (m.left+m.right);
31832             b.x = m.left;
31833             var totalHeight = (b.height + m.top + m.bottom);
31834             b.y = h - totalHeight + m.top;
31835             centerH -= totalHeight;
31836             south.updateBox(this.safeBox(b));
31837         }
31838         if(west && west.isVisible()){
31839             var b = west.getBox();
31840             var m = west.getMargins();
31841             b.height = centerH - (m.top+m.bottom);
31842             b.x = m.left;
31843             b.y = centerY + m.top;
31844             var totalWidth = (b.width + m.left + m.right);
31845             centerX += totalWidth;
31846             centerW -= totalWidth;
31847             west.updateBox(this.safeBox(b));
31848         }
31849         if(east && east.isVisible()){
31850             var b = east.getBox();
31851             var m = east.getMargins();
31852             b.height = centerH - (m.top+m.bottom);
31853             var totalWidth = (b.width + m.left + m.right);
31854             b.x = w - totalWidth + m.left;
31855             b.y = centerY + m.top;
31856             centerW -= totalWidth;
31857             east.updateBox(this.safeBox(b));
31858         }
31859         if(center){
31860             var m = center.getMargins();
31861             var centerBox = {
31862                 x: centerX + m.left,
31863                 y: centerY + m.top,
31864                 width: centerW - (m.left+m.right),
31865                 height: centerH - (m.top+m.bottom)
31866             };
31867             //if(this.hideOnLayout){
31868                 //center.el.setStyle("display", "block");
31869             //}
31870             center.updateBox(this.safeBox(centerBox));
31871         }
31872         this.el.repaint();
31873         this.fireEvent("layout", this);
31874     },
31875
31876     // private
31877     safeBox : function(box){
31878         box.width = Math.max(0, box.width);
31879         box.height = Math.max(0, box.height);
31880         return box;
31881     },
31882
31883     /**
31884      * Adds a ContentPanel (or subclass) to this layout.
31885      * @param {String} target The target region key (north, south, east, west or center).
31886      * @param {Roo.ContentPanel} panel The panel to add
31887      * @return {Roo.ContentPanel} The added panel
31888      */
31889     add : function(target, panel){
31890          
31891         target = target.toLowerCase();
31892         return this.regions[target].add(panel);
31893     },
31894
31895     /**
31896      * Remove a ContentPanel (or subclass) to this layout.
31897      * @param {String} target The target region key (north, south, east, west or center).
31898      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31899      * @return {Roo.ContentPanel} The removed panel
31900      */
31901     remove : function(target, panel){
31902         target = target.toLowerCase();
31903         return this.regions[target].remove(panel);
31904     },
31905
31906     /**
31907      * Searches all regions for a panel with the specified id
31908      * @param {String} panelId
31909      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31910      */
31911     findPanel : function(panelId){
31912         var rs = this.regions;
31913         for(var target in rs){
31914             if(typeof rs[target] != "function"){
31915                 var p = rs[target].getPanel(panelId);
31916                 if(p){
31917                     return p;
31918                 }
31919             }
31920         }
31921         return null;
31922     },
31923
31924     /**
31925      * Searches all regions for a panel with the specified id and activates (shows) it.
31926      * @param {String/ContentPanel} panelId The panels id or the panel itself
31927      * @return {Roo.ContentPanel} The shown panel or null
31928      */
31929     showPanel : function(panelId) {
31930       var rs = this.regions;
31931       for(var target in rs){
31932          var r = rs[target];
31933          if(typeof r != "function"){
31934             if(r.hasPanel(panelId)){
31935                return r.showPanel(panelId);
31936             }
31937          }
31938       }
31939       return null;
31940    },
31941
31942    /**
31943      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31944      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31945      */
31946     restoreState : function(provider){
31947         if(!provider){
31948             provider = Roo.state.Manager;
31949         }
31950         var sm = new Roo.LayoutStateManager();
31951         sm.init(this, provider);
31952     },
31953
31954     /**
31955      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31956      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31957      * a valid ContentPanel config object.  Example:
31958      * <pre><code>
31959 // Create the main layout
31960 var layout = new Roo.BorderLayout('main-ct', {
31961     west: {
31962         split:true,
31963         minSize: 175,
31964         titlebar: true
31965     },
31966     center: {
31967         title:'Components'
31968     }
31969 }, 'main-ct');
31970
31971 // Create and add multiple ContentPanels at once via configs
31972 layout.batchAdd({
31973    west: {
31974        id: 'source-files',
31975        autoCreate:true,
31976        title:'Ext Source Files',
31977        autoScroll:true,
31978        fitToFrame:true
31979    },
31980    center : {
31981        el: cview,
31982        autoScroll:true,
31983        fitToFrame:true,
31984        toolbar: tb,
31985        resizeEl:'cbody'
31986    }
31987 });
31988 </code></pre>
31989      * @param {Object} regions An object containing ContentPanel configs by region name
31990      */
31991     batchAdd : function(regions){
31992         this.beginUpdate();
31993         for(var rname in regions){
31994             var lr = this.regions[rname];
31995             if(lr){
31996                 this.addTypedPanels(lr, regions[rname]);
31997             }
31998         }
31999         this.endUpdate();
32000     },
32001
32002     // private
32003     addTypedPanels : function(lr, ps){
32004         if(typeof ps == 'string'){
32005             lr.add(new Roo.ContentPanel(ps));
32006         }
32007         else if(ps instanceof Array){
32008             for(var i =0, len = ps.length; i < len; i++){
32009                 this.addTypedPanels(lr, ps[i]);
32010             }
32011         }
32012         else if(!ps.events){ // raw config?
32013             var el = ps.el;
32014             delete ps.el; // prevent conflict
32015             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32016         }
32017         else {  // panel object assumed!
32018             lr.add(ps);
32019         }
32020     },
32021     /**
32022      * Adds a xtype elements to the layout.
32023      * <pre><code>
32024
32025 layout.addxtype({
32026        xtype : 'ContentPanel',
32027        region: 'west',
32028        items: [ .... ]
32029    }
32030 );
32031
32032 layout.addxtype({
32033         xtype : 'NestedLayoutPanel',
32034         region: 'west',
32035         layout: {
32036            center: { },
32037            west: { }   
32038         },
32039         items : [ ... list of content panels or nested layout panels.. ]
32040    }
32041 );
32042 </code></pre>
32043      * @param {Object} cfg Xtype definition of item to add.
32044      */
32045     addxtype : function(cfg)
32046     {
32047         // basically accepts a pannel...
32048         // can accept a layout region..!?!?
32049         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32050         
32051         if (!cfg.xtype.match(/Panel$/)) {
32052             return false;
32053         }
32054         var ret = false;
32055         
32056         if (typeof(cfg.region) == 'undefined') {
32057             Roo.log("Failed to add Panel, region was not set");
32058             Roo.log(cfg);
32059             return false;
32060         }
32061         var region = cfg.region;
32062         delete cfg.region;
32063         
32064           
32065         var xitems = [];
32066         if (cfg.items) {
32067             xitems = cfg.items;
32068             delete cfg.items;
32069         }
32070         var nb = false;
32071         
32072         switch(cfg.xtype) 
32073         {
32074             case 'ContentPanel':  // ContentPanel (el, cfg)
32075             case 'ScrollPanel':  // ContentPanel (el, cfg)
32076             case 'ViewPanel': 
32077                 if(cfg.autoCreate) {
32078                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32079                 } else {
32080                     var el = this.el.createChild();
32081                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32082                 }
32083                 
32084                 this.add(region, ret);
32085                 break;
32086             
32087             
32088             case 'TreePanel': // our new panel!
32089                 cfg.el = this.el.createChild();
32090                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32091                 this.add(region, ret);
32092                 break;
32093             
32094             case 'NestedLayoutPanel': 
32095                 // create a new Layout (which is  a Border Layout...
32096                 var el = this.el.createChild();
32097                 var clayout = cfg.layout;
32098                 delete cfg.layout;
32099                 clayout.items   = clayout.items  || [];
32100                 // replace this exitems with the clayout ones..
32101                 xitems = clayout.items;
32102                  
32103                 
32104                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32105                     cfg.background = false;
32106                 }
32107                 var layout = new Roo.BorderLayout(el, clayout);
32108                 
32109                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32110                 //console.log('adding nested layout panel '  + cfg.toSource());
32111                 this.add(region, ret);
32112                 nb = {}; /// find first...
32113                 break;
32114                 
32115             case 'GridPanel': 
32116             
32117                 // needs grid and region
32118                 
32119                 //var el = this.getRegion(region).el.createChild();
32120                 var el = this.el.createChild();
32121                 // create the grid first...
32122                 
32123                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32124                 delete cfg.grid;
32125                 if (region == 'center' && this.active ) {
32126                     cfg.background = false;
32127                 }
32128                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32129                 
32130                 this.add(region, ret);
32131                 if (cfg.background) {
32132                     ret.on('activate', function(gp) {
32133                         if (!gp.grid.rendered) {
32134                             gp.grid.render();
32135                         }
32136                     });
32137                 } else {
32138                     grid.render();
32139                 }
32140                 break;
32141            
32142            
32143            
32144                 
32145                 
32146                 
32147             default: 
32148                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32149                 return null;
32150              // GridPanel (grid, cfg)
32151             
32152         }
32153         this.beginUpdate();
32154         // add children..
32155         var region = '';
32156         var abn = {};
32157         Roo.each(xitems, function(i)  {
32158             region = nb && i.region ? i.region : false;
32159             
32160             var add = ret.addxtype(i);
32161            
32162             if (region) {
32163                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32164                 if (!i.background) {
32165                     abn[region] = nb[region] ;
32166                 }
32167             }
32168             
32169         });
32170         this.endUpdate();
32171
32172         // make the last non-background panel active..
32173         //if (nb) { Roo.log(abn); }
32174         if (nb) {
32175             
32176             for(var r in abn) {
32177                 region = this.getRegion(r);
32178                 if (region) {
32179                     // tried using nb[r], but it does not work..
32180                      
32181                     region.showPanel(abn[r]);
32182                    
32183                 }
32184             }
32185         }
32186         return ret;
32187         
32188     }
32189 });
32190
32191 /**
32192  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32193  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32194  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32195  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32196  * <pre><code>
32197 // shorthand
32198 var CP = Roo.ContentPanel;
32199
32200 var layout = Roo.BorderLayout.create({
32201     north: {
32202         initialSize: 25,
32203         titlebar: false,
32204         panels: [new CP("north", "North")]
32205     },
32206     west: {
32207         split:true,
32208         initialSize: 200,
32209         minSize: 175,
32210         maxSize: 400,
32211         titlebar: true,
32212         collapsible: true,
32213         panels: [new CP("west", {title: "West"})]
32214     },
32215     east: {
32216         split:true,
32217         initialSize: 202,
32218         minSize: 175,
32219         maxSize: 400,
32220         titlebar: true,
32221         collapsible: true,
32222         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32223     },
32224     south: {
32225         split:true,
32226         initialSize: 100,
32227         minSize: 100,
32228         maxSize: 200,
32229         titlebar: true,
32230         collapsible: true,
32231         panels: [new CP("south", {title: "South", closable: true})]
32232     },
32233     center: {
32234         titlebar: true,
32235         autoScroll:true,
32236         resizeTabs: true,
32237         minTabWidth: 50,
32238         preferredTabWidth: 150,
32239         panels: [
32240             new CP("center1", {title: "Close Me", closable: true}),
32241             new CP("center2", {title: "Center Panel", closable: false})
32242         ]
32243     }
32244 }, document.body);
32245
32246 layout.getRegion("center").showPanel("center1");
32247 </code></pre>
32248  * @param config
32249  * @param targetEl
32250  */
32251 Roo.BorderLayout.create = function(config, targetEl){
32252     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32253     layout.beginUpdate();
32254     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32255     for(var j = 0, jlen = regions.length; j < jlen; j++){
32256         var lr = regions[j];
32257         if(layout.regions[lr] && config[lr].panels){
32258             var r = layout.regions[lr];
32259             var ps = config[lr].panels;
32260             layout.addTypedPanels(r, ps);
32261         }
32262     }
32263     layout.endUpdate();
32264     return layout;
32265 };
32266
32267 // private
32268 Roo.BorderLayout.RegionFactory = {
32269     // private
32270     validRegions : ["north","south","east","west","center"],
32271
32272     // private
32273     create : function(target, mgr, config){
32274         target = target.toLowerCase();
32275         if(config.lightweight || config.basic){
32276             return new Roo.BasicLayoutRegion(mgr, config, target);
32277         }
32278         switch(target){
32279             case "north":
32280                 return new Roo.NorthLayoutRegion(mgr, config);
32281             case "south":
32282                 return new Roo.SouthLayoutRegion(mgr, config);
32283             case "east":
32284                 return new Roo.EastLayoutRegion(mgr, config);
32285             case "west":
32286                 return new Roo.WestLayoutRegion(mgr, config);
32287             case "center":
32288                 return new Roo.CenterLayoutRegion(mgr, config);
32289         }
32290         throw 'Layout region "'+target+'" not supported.';
32291     }
32292 };/*
32293  * Based on:
32294  * Ext JS Library 1.1.1
32295  * Copyright(c) 2006-2007, Ext JS, LLC.
32296  *
32297  * Originally Released Under LGPL - original licence link has changed is not relivant.
32298  *
32299  * Fork - LGPL
32300  * <script type="text/javascript">
32301  */
32302  
32303 /**
32304  * @class Roo.BasicLayoutRegion
32305  * @extends Roo.util.Observable
32306  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32307  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32308  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32309  */
32310 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32311     this.mgr = mgr;
32312     this.position  = pos;
32313     this.events = {
32314         /**
32315          * @scope Roo.BasicLayoutRegion
32316          */
32317         
32318         /**
32319          * @event beforeremove
32320          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32321          * @param {Roo.LayoutRegion} this
32322          * @param {Roo.ContentPanel} panel The panel
32323          * @param {Object} e The cancel event object
32324          */
32325         "beforeremove" : true,
32326         /**
32327          * @event invalidated
32328          * Fires when the layout for this region is changed.
32329          * @param {Roo.LayoutRegion} this
32330          */
32331         "invalidated" : true,
32332         /**
32333          * @event visibilitychange
32334          * Fires when this region is shown or hidden 
32335          * @param {Roo.LayoutRegion} this
32336          * @param {Boolean} visibility true or false
32337          */
32338         "visibilitychange" : true,
32339         /**
32340          * @event paneladded
32341          * Fires when a panel is added. 
32342          * @param {Roo.LayoutRegion} this
32343          * @param {Roo.ContentPanel} panel The panel
32344          */
32345         "paneladded" : true,
32346         /**
32347          * @event panelremoved
32348          * Fires when a panel is removed. 
32349          * @param {Roo.LayoutRegion} this
32350          * @param {Roo.ContentPanel} panel The panel
32351          */
32352         "panelremoved" : true,
32353         /**
32354          * @event collapsed
32355          * Fires when this region is collapsed.
32356          * @param {Roo.LayoutRegion} this
32357          */
32358         "collapsed" : true,
32359         /**
32360          * @event expanded
32361          * Fires when this region is expanded.
32362          * @param {Roo.LayoutRegion} this
32363          */
32364         "expanded" : true,
32365         /**
32366          * @event slideshow
32367          * Fires when this region is slid into view.
32368          * @param {Roo.LayoutRegion} this
32369          */
32370         "slideshow" : true,
32371         /**
32372          * @event slidehide
32373          * Fires when this region slides out of view. 
32374          * @param {Roo.LayoutRegion} this
32375          */
32376         "slidehide" : true,
32377         /**
32378          * @event panelactivated
32379          * Fires when a panel is activated. 
32380          * @param {Roo.LayoutRegion} this
32381          * @param {Roo.ContentPanel} panel The activated panel
32382          */
32383         "panelactivated" : true,
32384         /**
32385          * @event resized
32386          * Fires when the user resizes this region. 
32387          * @param {Roo.LayoutRegion} this
32388          * @param {Number} newSize The new size (width for east/west, height for north/south)
32389          */
32390         "resized" : true
32391     };
32392     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32393     this.panels = new Roo.util.MixedCollection();
32394     this.panels.getKey = this.getPanelId.createDelegate(this);
32395     this.box = null;
32396     this.activePanel = null;
32397     // ensure listeners are added...
32398     
32399     if (config.listeners || config.events) {
32400         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
32401             listeners : config.listeners || {},
32402             events : config.events || {}
32403         });
32404     }
32405     
32406     if(skipConfig !== true){
32407         this.applyConfig(config);
32408     }
32409 };
32410
32411 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
32412     getPanelId : function(p){
32413         return p.getId();
32414     },
32415     
32416     applyConfig : function(config){
32417         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32418         this.config = config;
32419         
32420     },
32421     
32422     /**
32423      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32424      * the width, for horizontal (north, south) the height.
32425      * @param {Number} newSize The new width or height
32426      */
32427     resizeTo : function(newSize){
32428         var el = this.el ? this.el :
32429                  (this.activePanel ? this.activePanel.getEl() : null);
32430         if(el){
32431             switch(this.position){
32432                 case "east":
32433                 case "west":
32434                     el.setWidth(newSize);
32435                     this.fireEvent("resized", this, newSize);
32436                 break;
32437                 case "north":
32438                 case "south":
32439                     el.setHeight(newSize);
32440                     this.fireEvent("resized", this, newSize);
32441                 break;                
32442             }
32443         }
32444     },
32445     
32446     getBox : function(){
32447         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32448     },
32449     
32450     getMargins : function(){
32451         return this.margins;
32452     },
32453     
32454     updateBox : function(box){
32455         this.box = box;
32456         var el = this.activePanel.getEl();
32457         el.dom.style.left = box.x + "px";
32458         el.dom.style.top = box.y + "px";
32459         this.activePanel.setSize(box.width, box.height);
32460     },
32461     
32462     /**
32463      * Returns the container element for this region.
32464      * @return {Roo.Element}
32465      */
32466     getEl : function(){
32467         return this.activePanel;
32468     },
32469     
32470     /**
32471      * Returns true if this region is currently visible.
32472      * @return {Boolean}
32473      */
32474     isVisible : function(){
32475         return this.activePanel ? true : false;
32476     },
32477     
32478     setActivePanel : function(panel){
32479         panel = this.getPanel(panel);
32480         if(this.activePanel && this.activePanel != panel){
32481             this.activePanel.setActiveState(false);
32482             this.activePanel.getEl().setLeftTop(-10000,-10000);
32483         }
32484         this.activePanel = panel;
32485         panel.setActiveState(true);
32486         if(this.box){
32487             panel.setSize(this.box.width, this.box.height);
32488         }
32489         this.fireEvent("panelactivated", this, panel);
32490         this.fireEvent("invalidated");
32491     },
32492     
32493     /**
32494      * Show the specified panel.
32495      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32496      * @return {Roo.ContentPanel} The shown panel or null
32497      */
32498     showPanel : function(panel){
32499         if(panel = this.getPanel(panel)){
32500             this.setActivePanel(panel);
32501         }
32502         return panel;
32503     },
32504     
32505     /**
32506      * Get the active panel for this region.
32507      * @return {Roo.ContentPanel} The active panel or null
32508      */
32509     getActivePanel : function(){
32510         return this.activePanel;
32511     },
32512     
32513     /**
32514      * Add the passed ContentPanel(s)
32515      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32516      * @return {Roo.ContentPanel} The panel added (if only one was added)
32517      */
32518     add : function(panel){
32519         if(arguments.length > 1){
32520             for(var i = 0, len = arguments.length; i < len; i++) {
32521                 this.add(arguments[i]);
32522             }
32523             return null;
32524         }
32525         if(this.hasPanel(panel)){
32526             this.showPanel(panel);
32527             return panel;
32528         }
32529         var el = panel.getEl();
32530         if(el.dom.parentNode != this.mgr.el.dom){
32531             this.mgr.el.dom.appendChild(el.dom);
32532         }
32533         if(panel.setRegion){
32534             panel.setRegion(this);
32535         }
32536         this.panels.add(panel);
32537         el.setStyle("position", "absolute");
32538         if(!panel.background){
32539             this.setActivePanel(panel);
32540             if(this.config.initialSize && this.panels.getCount()==1){
32541                 this.resizeTo(this.config.initialSize);
32542             }
32543         }
32544         this.fireEvent("paneladded", this, panel);
32545         return panel;
32546     },
32547     
32548     /**
32549      * Returns true if the panel is in this region.
32550      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32551      * @return {Boolean}
32552      */
32553     hasPanel : function(panel){
32554         if(typeof panel == "object"){ // must be panel obj
32555             panel = panel.getId();
32556         }
32557         return this.getPanel(panel) ? true : false;
32558     },
32559     
32560     /**
32561      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32562      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32563      * @param {Boolean} preservePanel Overrides the config preservePanel option
32564      * @return {Roo.ContentPanel} The panel that was removed
32565      */
32566     remove : function(panel, preservePanel){
32567         panel = this.getPanel(panel);
32568         if(!panel){
32569             return null;
32570         }
32571         var e = {};
32572         this.fireEvent("beforeremove", this, panel, e);
32573         if(e.cancel === true){
32574             return null;
32575         }
32576         var panelId = panel.getId();
32577         this.panels.removeKey(panelId);
32578         return panel;
32579     },
32580     
32581     /**
32582      * Returns the panel specified or null if it's not in this region.
32583      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32584      * @return {Roo.ContentPanel}
32585      */
32586     getPanel : function(id){
32587         if(typeof id == "object"){ // must be panel obj
32588             return id;
32589         }
32590         return this.panels.get(id);
32591     },
32592     
32593     /**
32594      * Returns this regions position (north/south/east/west/center).
32595      * @return {String} 
32596      */
32597     getPosition: function(){
32598         return this.position;    
32599     }
32600 });/*
32601  * Based on:
32602  * Ext JS Library 1.1.1
32603  * Copyright(c) 2006-2007, Ext JS, LLC.
32604  *
32605  * Originally Released Under LGPL - original licence link has changed is not relivant.
32606  *
32607  * Fork - LGPL
32608  * <script type="text/javascript">
32609  */
32610  
32611 /**
32612  * @class Roo.LayoutRegion
32613  * @extends Roo.BasicLayoutRegion
32614  * This class represents a region in a layout manager.
32615  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
32616  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
32617  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
32618  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32619  * @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})
32620  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
32621  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
32622  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32623  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32624  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32625  * @cfg {String}    title           The title for the region (overrides panel titles)
32626  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32627  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32628  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32629  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32630  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32631  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32632  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32633  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32634  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32635  * @cfg {Boolean}   showPin         True to show a pin button
32636  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32637  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32638  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32639  * @cfg {Number}    width           For East/West panels
32640  * @cfg {Number}    height          For North/South panels
32641  * @cfg {Boolean}   split           To show the splitter
32642  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32643  */
32644 Roo.LayoutRegion = function(mgr, config, pos){
32645     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
32646     var dh = Roo.DomHelper;
32647     /** This region's container element 
32648     * @type Roo.Element */
32649     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
32650     /** This region's title element 
32651     * @type Roo.Element */
32652
32653     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
32654         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32655         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
32656     ]}, true);
32657     this.titleEl.enableDisplayMode();
32658     /** This region's title text element 
32659     * @type HTMLElement */
32660     this.titleTextEl = this.titleEl.dom.firstChild;
32661     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32662     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
32663     this.closeBtn.enableDisplayMode();
32664     this.closeBtn.on("click", this.closeClicked, this);
32665     this.closeBtn.hide();
32666
32667     this.createBody(config);
32668     this.visible = true;
32669     this.collapsed = false;
32670
32671     if(config.hideWhenEmpty){
32672         this.hide();
32673         this.on("paneladded", this.validateVisibility, this);
32674         this.on("panelremoved", this.validateVisibility, this);
32675     }
32676     this.applyConfig(config);
32677 };
32678
32679 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
32680
32681     createBody : function(){
32682         /** This region's body element 
32683         * @type Roo.Element */
32684         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
32685     },
32686
32687     applyConfig : function(c){
32688         if(c.collapsible && this.position != "center" && !this.collapsedEl){
32689             var dh = Roo.DomHelper;
32690             if(c.titlebar !== false){
32691                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
32692                 this.collapseBtn.on("click", this.collapse, this);
32693                 this.collapseBtn.enableDisplayMode();
32694
32695                 if(c.showPin === true || this.showPin){
32696                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
32697                     this.stickBtn.enableDisplayMode();
32698                     this.stickBtn.on("click", this.expand, this);
32699                     this.stickBtn.hide();
32700                 }
32701             }
32702             /** This region's collapsed element
32703             * @type Roo.Element */
32704             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32705                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32706             ]}, true);
32707             if(c.floatable !== false){
32708                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32709                this.collapsedEl.on("click", this.collapseClick, this);
32710             }
32711
32712             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32713                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32714                    id: "message", unselectable: "on", style:{"float":"left"}});
32715                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32716              }
32717             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32718             this.expandBtn.on("click", this.expand, this);
32719         }
32720         if(this.collapseBtn){
32721             this.collapseBtn.setVisible(c.collapsible == true);
32722         }
32723         this.cmargins = c.cmargins || this.cmargins ||
32724                          (this.position == "west" || this.position == "east" ?
32725                              {top: 0, left: 2, right:2, bottom: 0} :
32726                              {top: 2, left: 0, right:0, bottom: 2});
32727         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32728         this.bottomTabs = c.tabPosition != "top";
32729         this.autoScroll = c.autoScroll || false;
32730         if(this.autoScroll){
32731             this.bodyEl.setStyle("overflow", "auto");
32732         }else{
32733             this.bodyEl.setStyle("overflow", "hidden");
32734         }
32735         //if(c.titlebar !== false){
32736             if((!c.titlebar && !c.title) || c.titlebar === false){
32737                 this.titleEl.hide();
32738             }else{
32739                 this.titleEl.show();
32740                 if(c.title){
32741                     this.titleTextEl.innerHTML = c.title;
32742                 }
32743             }
32744         //}
32745         this.duration = c.duration || .30;
32746         this.slideDuration = c.slideDuration || .45;
32747         this.config = c;
32748         if(c.collapsed){
32749             this.collapse(true);
32750         }
32751         if(c.hidden){
32752             this.hide();
32753         }
32754     },
32755     /**
32756      * Returns true if this region is currently visible.
32757      * @return {Boolean}
32758      */
32759     isVisible : function(){
32760         return this.visible;
32761     },
32762
32763     /**
32764      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32765      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32766      */
32767     setCollapsedTitle : function(title){
32768         title = title || "&#160;";
32769         if(this.collapsedTitleTextEl){
32770             this.collapsedTitleTextEl.innerHTML = title;
32771         }
32772     },
32773
32774     getBox : function(){
32775         var b;
32776         if(!this.collapsed){
32777             b = this.el.getBox(false, true);
32778         }else{
32779             b = this.collapsedEl.getBox(false, true);
32780         }
32781         return b;
32782     },
32783
32784     getMargins : function(){
32785         return this.collapsed ? this.cmargins : this.margins;
32786     },
32787
32788     highlight : function(){
32789         this.el.addClass("x-layout-panel-dragover");
32790     },
32791
32792     unhighlight : function(){
32793         this.el.removeClass("x-layout-panel-dragover");
32794     },
32795
32796     updateBox : function(box){
32797         this.box = box;
32798         if(!this.collapsed){
32799             this.el.dom.style.left = box.x + "px";
32800             this.el.dom.style.top = box.y + "px";
32801             this.updateBody(box.width, box.height);
32802         }else{
32803             this.collapsedEl.dom.style.left = box.x + "px";
32804             this.collapsedEl.dom.style.top = box.y + "px";
32805             this.collapsedEl.setSize(box.width, box.height);
32806         }
32807         if(this.tabs){
32808             this.tabs.autoSizeTabs();
32809         }
32810     },
32811
32812     updateBody : function(w, h){
32813         if(w !== null){
32814             this.el.setWidth(w);
32815             w -= this.el.getBorderWidth("rl");
32816             if(this.config.adjustments){
32817                 w += this.config.adjustments[0];
32818             }
32819         }
32820         if(h !== null){
32821             this.el.setHeight(h);
32822             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32823             h -= this.el.getBorderWidth("tb");
32824             if(this.config.adjustments){
32825                 h += this.config.adjustments[1];
32826             }
32827             this.bodyEl.setHeight(h);
32828             if(this.tabs){
32829                 h = this.tabs.syncHeight(h);
32830             }
32831         }
32832         if(this.panelSize){
32833             w = w !== null ? w : this.panelSize.width;
32834             h = h !== null ? h : this.panelSize.height;
32835         }
32836         if(this.activePanel){
32837             var el = this.activePanel.getEl();
32838             w = w !== null ? w : el.getWidth();
32839             h = h !== null ? h : el.getHeight();
32840             this.panelSize = {width: w, height: h};
32841             this.activePanel.setSize(w, h);
32842         }
32843         if(Roo.isIE && this.tabs){
32844             this.tabs.el.repaint();
32845         }
32846     },
32847
32848     /**
32849      * Returns the container element for this region.
32850      * @return {Roo.Element}
32851      */
32852     getEl : function(){
32853         return this.el;
32854     },
32855
32856     /**
32857      * Hides this region.
32858      */
32859     hide : function(){
32860         if(!this.collapsed){
32861             this.el.dom.style.left = "-2000px";
32862             this.el.hide();
32863         }else{
32864             this.collapsedEl.dom.style.left = "-2000px";
32865             this.collapsedEl.hide();
32866         }
32867         this.visible = false;
32868         this.fireEvent("visibilitychange", this, false);
32869     },
32870
32871     /**
32872      * Shows this region if it was previously hidden.
32873      */
32874     show : function(){
32875         if(!this.collapsed){
32876             this.el.show();
32877         }else{
32878             this.collapsedEl.show();
32879         }
32880         this.visible = true;
32881         this.fireEvent("visibilitychange", this, true);
32882     },
32883
32884     closeClicked : function(){
32885         if(this.activePanel){
32886             this.remove(this.activePanel);
32887         }
32888     },
32889
32890     collapseClick : function(e){
32891         if(this.isSlid){
32892            e.stopPropagation();
32893            this.slideIn();
32894         }else{
32895            e.stopPropagation();
32896            this.slideOut();
32897         }
32898     },
32899
32900     /**
32901      * Collapses this region.
32902      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32903      */
32904     collapse : function(skipAnim){
32905         if(this.collapsed) return;
32906         this.collapsed = true;
32907         if(this.split){
32908             this.split.el.hide();
32909         }
32910         if(this.config.animate && skipAnim !== true){
32911             this.fireEvent("invalidated", this);
32912             this.animateCollapse();
32913         }else{
32914             this.el.setLocation(-20000,-20000);
32915             this.el.hide();
32916             this.collapsedEl.show();
32917             this.fireEvent("collapsed", this);
32918             this.fireEvent("invalidated", this);
32919         }
32920     },
32921
32922     animateCollapse : function(){
32923         // overridden
32924     },
32925
32926     /**
32927      * Expands this region if it was previously collapsed.
32928      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32929      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32930      */
32931     expand : function(e, skipAnim){
32932         if(e) e.stopPropagation();
32933         if(!this.collapsed || this.el.hasActiveFx()) return;
32934         if(this.isSlid){
32935             this.afterSlideIn();
32936             skipAnim = true;
32937         }
32938         this.collapsed = false;
32939         if(this.config.animate && skipAnim !== true){
32940             this.animateExpand();
32941         }else{
32942             this.el.show();
32943             if(this.split){
32944                 this.split.el.show();
32945             }
32946             this.collapsedEl.setLocation(-2000,-2000);
32947             this.collapsedEl.hide();
32948             this.fireEvent("invalidated", this);
32949             this.fireEvent("expanded", this);
32950         }
32951     },
32952
32953     animateExpand : function(){
32954         // overridden
32955     },
32956
32957     initTabs : function()
32958     {
32959         this.bodyEl.setStyle("overflow", "hidden");
32960         var ts = new Roo.TabPanel(
32961                 this.bodyEl.dom,
32962                 {
32963                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
32964                     disableTooltips: this.config.disableTabTips,
32965                     toolbar : this.config.toolbar
32966                 }
32967         );
32968         if(this.config.hideTabs){
32969             ts.stripWrap.setDisplayed(false);
32970         }
32971         this.tabs = ts;
32972         ts.resizeTabs = this.config.resizeTabs === true;
32973         ts.minTabWidth = this.config.minTabWidth || 40;
32974         ts.maxTabWidth = this.config.maxTabWidth || 250;
32975         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32976         ts.monitorResize = false;
32977         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32978         ts.bodyEl.addClass('x-layout-tabs-body');
32979         this.panels.each(this.initPanelAsTab, this);
32980     },
32981
32982     initPanelAsTab : function(panel){
32983         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
32984                     this.config.closeOnTab && panel.isClosable());
32985         if(panel.tabTip !== undefined){
32986             ti.setTooltip(panel.tabTip);
32987         }
32988         ti.on("activate", function(){
32989               this.setActivePanel(panel);
32990         }, this);
32991         if(this.config.closeOnTab){
32992             ti.on("beforeclose", function(t, e){
32993                 e.cancel = true;
32994                 this.remove(panel);
32995             }, this);
32996         }
32997         return ti;
32998     },
32999
33000     updatePanelTitle : function(panel, title){
33001         if(this.activePanel == panel){
33002             this.updateTitle(title);
33003         }
33004         if(this.tabs){
33005             var ti = this.tabs.getTab(panel.getEl().id);
33006             ti.setText(title);
33007             if(panel.tabTip !== undefined){
33008                 ti.setTooltip(panel.tabTip);
33009             }
33010         }
33011     },
33012
33013     updateTitle : function(title){
33014         if(this.titleTextEl && !this.config.title){
33015             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33016         }
33017     },
33018
33019     setActivePanel : function(panel){
33020         panel = this.getPanel(panel);
33021         if(this.activePanel && this.activePanel != panel){
33022             this.activePanel.setActiveState(false);
33023         }
33024         this.activePanel = panel;
33025         panel.setActiveState(true);
33026         if(this.panelSize){
33027             panel.setSize(this.panelSize.width, this.panelSize.height);
33028         }
33029         if(this.closeBtn){
33030             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33031         }
33032         this.updateTitle(panel.getTitle());
33033         if(this.tabs){
33034             this.fireEvent("invalidated", this);
33035         }
33036         this.fireEvent("panelactivated", this, panel);
33037     },
33038
33039     /**
33040      * Shows the specified panel.
33041      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33042      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33043      */
33044     showPanel : function(panel){
33045         if(panel = this.getPanel(panel)){
33046             if(this.tabs){
33047                 var tab = this.tabs.getTab(panel.getEl().id);
33048                 if(tab.isHidden()){
33049                     this.tabs.unhideTab(tab.id);
33050                 }
33051                 tab.activate();
33052             }else{
33053                 this.setActivePanel(panel);
33054             }
33055         }
33056         return panel;
33057     },
33058
33059     /**
33060      * Get the active panel for this region.
33061      * @return {Roo.ContentPanel} The active panel or null
33062      */
33063     getActivePanel : function(){
33064         return this.activePanel;
33065     },
33066
33067     validateVisibility : function(){
33068         if(this.panels.getCount() < 1){
33069             this.updateTitle("&#160;");
33070             this.closeBtn.hide();
33071             this.hide();
33072         }else{
33073             if(!this.isVisible()){
33074                 this.show();
33075             }
33076         }
33077     },
33078
33079     /**
33080      * Adds the passed ContentPanel(s) to this region.
33081      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33082      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33083      */
33084     add : function(panel){
33085         if(arguments.length > 1){
33086             for(var i = 0, len = arguments.length; i < len; i++) {
33087                 this.add(arguments[i]);
33088             }
33089             return null;
33090         }
33091         if(this.hasPanel(panel)){
33092             this.showPanel(panel);
33093             return panel;
33094         }
33095         panel.setRegion(this);
33096         this.panels.add(panel);
33097         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33098             this.bodyEl.dom.appendChild(panel.getEl().dom);
33099             if(panel.background !== true){
33100                 this.setActivePanel(panel);
33101             }
33102             this.fireEvent("paneladded", this, panel);
33103             return panel;
33104         }
33105         if(!this.tabs){
33106             this.initTabs();
33107         }else{
33108             this.initPanelAsTab(panel);
33109         }
33110         if(panel.background !== true){
33111             this.tabs.activate(panel.getEl().id);
33112         }
33113         this.fireEvent("paneladded", this, panel);
33114         return panel;
33115     },
33116
33117     /**
33118      * Hides the tab for the specified panel.
33119      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33120      */
33121     hidePanel : function(panel){
33122         if(this.tabs && (panel = this.getPanel(panel))){
33123             this.tabs.hideTab(panel.getEl().id);
33124         }
33125     },
33126
33127     /**
33128      * Unhides the tab for a previously hidden panel.
33129      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33130      */
33131     unhidePanel : function(panel){
33132         if(this.tabs && (panel = this.getPanel(panel))){
33133             this.tabs.unhideTab(panel.getEl().id);
33134         }
33135     },
33136
33137     clearPanels : function(){
33138         while(this.panels.getCount() > 0){
33139              this.remove(this.panels.first());
33140         }
33141     },
33142
33143     /**
33144      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33145      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33146      * @param {Boolean} preservePanel Overrides the config preservePanel option
33147      * @return {Roo.ContentPanel} The panel that was removed
33148      */
33149     remove : function(panel, preservePanel){
33150         panel = this.getPanel(panel);
33151         if(!panel){
33152             return null;
33153         }
33154         var e = {};
33155         this.fireEvent("beforeremove", this, panel, e);
33156         if(e.cancel === true){
33157             return null;
33158         }
33159         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33160         var panelId = panel.getId();
33161         this.panels.removeKey(panelId);
33162         if(preservePanel){
33163             document.body.appendChild(panel.getEl().dom);
33164         }
33165         if(this.tabs){
33166             this.tabs.removeTab(panel.getEl().id);
33167         }else if (!preservePanel){
33168             this.bodyEl.dom.removeChild(panel.getEl().dom);
33169         }
33170         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33171             var p = this.panels.first();
33172             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33173             tempEl.appendChild(p.getEl().dom);
33174             this.bodyEl.update("");
33175             this.bodyEl.dom.appendChild(p.getEl().dom);
33176             tempEl = null;
33177             this.updateTitle(p.getTitle());
33178             this.tabs = null;
33179             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33180             this.setActivePanel(p);
33181         }
33182         panel.setRegion(null);
33183         if(this.activePanel == panel){
33184             this.activePanel = null;
33185         }
33186         if(this.config.autoDestroy !== false && preservePanel !== true){
33187             try{panel.destroy();}catch(e){}
33188         }
33189         this.fireEvent("panelremoved", this, panel);
33190         return panel;
33191     },
33192
33193     /**
33194      * Returns the TabPanel component used by this region
33195      * @return {Roo.TabPanel}
33196      */
33197     getTabs : function(){
33198         return this.tabs;
33199     },
33200
33201     createTool : function(parentEl, className){
33202         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33203             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33204         btn.addClassOnOver("x-layout-tools-button-over");
33205         return btn;
33206     }
33207 });/*
33208  * Based on:
33209  * Ext JS Library 1.1.1
33210  * Copyright(c) 2006-2007, Ext JS, LLC.
33211  *
33212  * Originally Released Under LGPL - original licence link has changed is not relivant.
33213  *
33214  * Fork - LGPL
33215  * <script type="text/javascript">
33216  */
33217  
33218
33219
33220 /**
33221  * @class Roo.SplitLayoutRegion
33222  * @extends Roo.LayoutRegion
33223  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33224  */
33225 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33226     this.cursor = cursor;
33227     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33228 };
33229
33230 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33231     splitTip : "Drag to resize.",
33232     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33233     useSplitTips : false,
33234
33235     applyConfig : function(config){
33236         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33237         if(config.split){
33238             if(!this.split){
33239                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33240                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33241                 /** The SplitBar for this region 
33242                 * @type Roo.SplitBar */
33243                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33244                 this.split.on("moved", this.onSplitMove, this);
33245                 this.split.useShim = config.useShim === true;
33246                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33247                 if(this.useSplitTips){
33248                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33249                 }
33250                 if(config.collapsible){
33251                     this.split.el.on("dblclick", this.collapse,  this);
33252                 }
33253             }
33254             if(typeof config.minSize != "undefined"){
33255                 this.split.minSize = config.minSize;
33256             }
33257             if(typeof config.maxSize != "undefined"){
33258                 this.split.maxSize = config.maxSize;
33259             }
33260             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33261                 this.hideSplitter();
33262             }
33263         }
33264     },
33265
33266     getHMaxSize : function(){
33267          var cmax = this.config.maxSize || 10000;
33268          var center = this.mgr.getRegion("center");
33269          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33270     },
33271
33272     getVMaxSize : function(){
33273          var cmax = this.config.maxSize || 10000;
33274          var center = this.mgr.getRegion("center");
33275          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33276     },
33277
33278     onSplitMove : function(split, newSize){
33279         this.fireEvent("resized", this, newSize);
33280     },
33281     
33282     /** 
33283      * Returns the {@link Roo.SplitBar} for this region.
33284      * @return {Roo.SplitBar}
33285      */
33286     getSplitBar : function(){
33287         return this.split;
33288     },
33289     
33290     hide : function(){
33291         this.hideSplitter();
33292         Roo.SplitLayoutRegion.superclass.hide.call(this);
33293     },
33294
33295     hideSplitter : function(){
33296         if(this.split){
33297             this.split.el.setLocation(-2000,-2000);
33298             this.split.el.hide();
33299         }
33300     },
33301
33302     show : function(){
33303         if(this.split){
33304             this.split.el.show();
33305         }
33306         Roo.SplitLayoutRegion.superclass.show.call(this);
33307     },
33308     
33309     beforeSlide: function(){
33310         if(Roo.isGecko){// firefox overflow auto bug workaround
33311             this.bodyEl.clip();
33312             if(this.tabs) this.tabs.bodyEl.clip();
33313             if(this.activePanel){
33314                 this.activePanel.getEl().clip();
33315                 
33316                 if(this.activePanel.beforeSlide){
33317                     this.activePanel.beforeSlide();
33318                 }
33319             }
33320         }
33321     },
33322     
33323     afterSlide : function(){
33324         if(Roo.isGecko){// firefox overflow auto bug workaround
33325             this.bodyEl.unclip();
33326             if(this.tabs) this.tabs.bodyEl.unclip();
33327             if(this.activePanel){
33328                 this.activePanel.getEl().unclip();
33329                 if(this.activePanel.afterSlide){
33330                     this.activePanel.afterSlide();
33331                 }
33332             }
33333         }
33334     },
33335
33336     initAutoHide : function(){
33337         if(this.autoHide !== false){
33338             if(!this.autoHideHd){
33339                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33340                 this.autoHideHd = {
33341                     "mouseout": function(e){
33342                         if(!e.within(this.el, true)){
33343                             st.delay(500);
33344                         }
33345                     },
33346                     "mouseover" : function(e){
33347                         st.cancel();
33348                     },
33349                     scope : this
33350                 };
33351             }
33352             this.el.on(this.autoHideHd);
33353         }
33354     },
33355
33356     clearAutoHide : function(){
33357         if(this.autoHide !== false){
33358             this.el.un("mouseout", this.autoHideHd.mouseout);
33359             this.el.un("mouseover", this.autoHideHd.mouseover);
33360         }
33361     },
33362
33363     clearMonitor : function(){
33364         Roo.get(document).un("click", this.slideInIf, this);
33365     },
33366
33367     // these names are backwards but not changed for compat
33368     slideOut : function(){
33369         if(this.isSlid || this.el.hasActiveFx()){
33370             return;
33371         }
33372         this.isSlid = true;
33373         if(this.collapseBtn){
33374             this.collapseBtn.hide();
33375         }
33376         this.closeBtnState = this.closeBtn.getStyle('display');
33377         this.closeBtn.hide();
33378         if(this.stickBtn){
33379             this.stickBtn.show();
33380         }
33381         this.el.show();
33382         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33383         this.beforeSlide();
33384         this.el.setStyle("z-index", 10001);
33385         this.el.slideIn(this.getSlideAnchor(), {
33386             callback: function(){
33387                 this.afterSlide();
33388                 this.initAutoHide();
33389                 Roo.get(document).on("click", this.slideInIf, this);
33390                 this.fireEvent("slideshow", this);
33391             },
33392             scope: this,
33393             block: true
33394         });
33395     },
33396
33397     afterSlideIn : function(){
33398         this.clearAutoHide();
33399         this.isSlid = false;
33400         this.clearMonitor();
33401         this.el.setStyle("z-index", "");
33402         if(this.collapseBtn){
33403             this.collapseBtn.show();
33404         }
33405         this.closeBtn.setStyle('display', this.closeBtnState);
33406         if(this.stickBtn){
33407             this.stickBtn.hide();
33408         }
33409         this.fireEvent("slidehide", this);
33410     },
33411
33412     slideIn : function(cb){
33413         if(!this.isSlid || this.el.hasActiveFx()){
33414             Roo.callback(cb);
33415             return;
33416         }
33417         this.isSlid = false;
33418         this.beforeSlide();
33419         this.el.slideOut(this.getSlideAnchor(), {
33420             callback: function(){
33421                 this.el.setLeftTop(-10000, -10000);
33422                 this.afterSlide();
33423                 this.afterSlideIn();
33424                 Roo.callback(cb);
33425             },
33426             scope: this,
33427             block: true
33428         });
33429     },
33430     
33431     slideInIf : function(e){
33432         if(!e.within(this.el)){
33433             this.slideIn();
33434         }
33435     },
33436
33437     animateCollapse : function(){
33438         this.beforeSlide();
33439         this.el.setStyle("z-index", 20000);
33440         var anchor = this.getSlideAnchor();
33441         this.el.slideOut(anchor, {
33442             callback : function(){
33443                 this.el.setStyle("z-index", "");
33444                 this.collapsedEl.slideIn(anchor, {duration:.3});
33445                 this.afterSlide();
33446                 this.el.setLocation(-10000,-10000);
33447                 this.el.hide();
33448                 this.fireEvent("collapsed", this);
33449             },
33450             scope: this,
33451             block: true
33452         });
33453     },
33454
33455     animateExpand : function(){
33456         this.beforeSlide();
33457         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33458         this.el.setStyle("z-index", 20000);
33459         this.collapsedEl.hide({
33460             duration:.1
33461         });
33462         this.el.slideIn(this.getSlideAnchor(), {
33463             callback : function(){
33464                 this.el.setStyle("z-index", "");
33465                 this.afterSlide();
33466                 if(this.split){
33467                     this.split.el.show();
33468                 }
33469                 this.fireEvent("invalidated", this);
33470                 this.fireEvent("expanded", this);
33471             },
33472             scope: this,
33473             block: true
33474         });
33475     },
33476
33477     anchors : {
33478         "west" : "left",
33479         "east" : "right",
33480         "north" : "top",
33481         "south" : "bottom"
33482     },
33483
33484     sanchors : {
33485         "west" : "l",
33486         "east" : "r",
33487         "north" : "t",
33488         "south" : "b"
33489     },
33490
33491     canchors : {
33492         "west" : "tl-tr",
33493         "east" : "tr-tl",
33494         "north" : "tl-bl",
33495         "south" : "bl-tl"
33496     },
33497
33498     getAnchor : function(){
33499         return this.anchors[this.position];
33500     },
33501
33502     getCollapseAnchor : function(){
33503         return this.canchors[this.position];
33504     },
33505
33506     getSlideAnchor : function(){
33507         return this.sanchors[this.position];
33508     },
33509
33510     getAlignAdj : function(){
33511         var cm = this.cmargins;
33512         switch(this.position){
33513             case "west":
33514                 return [0, 0];
33515             break;
33516             case "east":
33517                 return [0, 0];
33518             break;
33519             case "north":
33520                 return [0, 0];
33521             break;
33522             case "south":
33523                 return [0, 0];
33524             break;
33525         }
33526     },
33527
33528     getExpandAdj : function(){
33529         var c = this.collapsedEl, cm = this.cmargins;
33530         switch(this.position){
33531             case "west":
33532                 return [-(cm.right+c.getWidth()+cm.left), 0];
33533             break;
33534             case "east":
33535                 return [cm.right+c.getWidth()+cm.left, 0];
33536             break;
33537             case "north":
33538                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33539             break;
33540             case "south":
33541                 return [0, cm.top+cm.bottom+c.getHeight()];
33542             break;
33543         }
33544     }
33545 });/*
33546  * Based on:
33547  * Ext JS Library 1.1.1
33548  * Copyright(c) 2006-2007, Ext JS, LLC.
33549  *
33550  * Originally Released Under LGPL - original licence link has changed is not relivant.
33551  *
33552  * Fork - LGPL
33553  * <script type="text/javascript">
33554  */
33555 /*
33556  * These classes are private internal classes
33557  */
33558 Roo.CenterLayoutRegion = function(mgr, config){
33559     Roo.LayoutRegion.call(this, mgr, config, "center");
33560     this.visible = true;
33561     this.minWidth = config.minWidth || 20;
33562     this.minHeight = config.minHeight || 20;
33563 };
33564
33565 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
33566     hide : function(){
33567         // center panel can't be hidden
33568     },
33569     
33570     show : function(){
33571         // center panel can't be hidden
33572     },
33573     
33574     getMinWidth: function(){
33575         return this.minWidth;
33576     },
33577     
33578     getMinHeight: function(){
33579         return this.minHeight;
33580     }
33581 });
33582
33583
33584 Roo.NorthLayoutRegion = function(mgr, config){
33585     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
33586     if(this.split){
33587         this.split.placement = Roo.SplitBar.TOP;
33588         this.split.orientation = Roo.SplitBar.VERTICAL;
33589         this.split.el.addClass("x-layout-split-v");
33590     }
33591     var size = config.initialSize || config.height;
33592     if(typeof size != "undefined"){
33593         this.el.setHeight(size);
33594     }
33595 };
33596 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
33597     orientation: Roo.SplitBar.VERTICAL,
33598     getBox : function(){
33599         if(this.collapsed){
33600             return this.collapsedEl.getBox();
33601         }
33602         var box = this.el.getBox();
33603         if(this.split){
33604             box.height += this.split.el.getHeight();
33605         }
33606         return box;
33607     },
33608     
33609     updateBox : function(box){
33610         if(this.split && !this.collapsed){
33611             box.height -= this.split.el.getHeight();
33612             this.split.el.setLeft(box.x);
33613             this.split.el.setTop(box.y+box.height);
33614             this.split.el.setWidth(box.width);
33615         }
33616         if(this.collapsed){
33617             this.updateBody(box.width, null);
33618         }
33619         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33620     }
33621 });
33622
33623 Roo.SouthLayoutRegion = function(mgr, config){
33624     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
33625     if(this.split){
33626         this.split.placement = Roo.SplitBar.BOTTOM;
33627         this.split.orientation = Roo.SplitBar.VERTICAL;
33628         this.split.el.addClass("x-layout-split-v");
33629     }
33630     var size = config.initialSize || config.height;
33631     if(typeof size != "undefined"){
33632         this.el.setHeight(size);
33633     }
33634 };
33635 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
33636     orientation: Roo.SplitBar.VERTICAL,
33637     getBox : function(){
33638         if(this.collapsed){
33639             return this.collapsedEl.getBox();
33640         }
33641         var box = this.el.getBox();
33642         if(this.split){
33643             var sh = this.split.el.getHeight();
33644             box.height += sh;
33645             box.y -= sh;
33646         }
33647         return box;
33648     },
33649     
33650     updateBox : function(box){
33651         if(this.split && !this.collapsed){
33652             var sh = this.split.el.getHeight();
33653             box.height -= sh;
33654             box.y += sh;
33655             this.split.el.setLeft(box.x);
33656             this.split.el.setTop(box.y-sh);
33657             this.split.el.setWidth(box.width);
33658         }
33659         if(this.collapsed){
33660             this.updateBody(box.width, null);
33661         }
33662         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33663     }
33664 });
33665
33666 Roo.EastLayoutRegion = function(mgr, config){
33667     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
33668     if(this.split){
33669         this.split.placement = Roo.SplitBar.RIGHT;
33670         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33671         this.split.el.addClass("x-layout-split-h");
33672     }
33673     var size = config.initialSize || config.width;
33674     if(typeof size != "undefined"){
33675         this.el.setWidth(size);
33676     }
33677 };
33678 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
33679     orientation: Roo.SplitBar.HORIZONTAL,
33680     getBox : function(){
33681         if(this.collapsed){
33682             return this.collapsedEl.getBox();
33683         }
33684         var box = this.el.getBox();
33685         if(this.split){
33686             var sw = this.split.el.getWidth();
33687             box.width += sw;
33688             box.x -= sw;
33689         }
33690         return box;
33691     },
33692
33693     updateBox : function(box){
33694         if(this.split && !this.collapsed){
33695             var sw = this.split.el.getWidth();
33696             box.width -= sw;
33697             this.split.el.setLeft(box.x);
33698             this.split.el.setTop(box.y);
33699             this.split.el.setHeight(box.height);
33700             box.x += sw;
33701         }
33702         if(this.collapsed){
33703             this.updateBody(null, box.height);
33704         }
33705         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33706     }
33707 });
33708
33709 Roo.WestLayoutRegion = function(mgr, config){
33710     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
33711     if(this.split){
33712         this.split.placement = Roo.SplitBar.LEFT;
33713         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33714         this.split.el.addClass("x-layout-split-h");
33715     }
33716     var size = config.initialSize || config.width;
33717     if(typeof size != "undefined"){
33718         this.el.setWidth(size);
33719     }
33720 };
33721 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
33722     orientation: Roo.SplitBar.HORIZONTAL,
33723     getBox : function(){
33724         if(this.collapsed){
33725             return this.collapsedEl.getBox();
33726         }
33727         var box = this.el.getBox();
33728         if(this.split){
33729             box.width += this.split.el.getWidth();
33730         }
33731         return box;
33732     },
33733     
33734     updateBox : function(box){
33735         if(this.split && !this.collapsed){
33736             var sw = this.split.el.getWidth();
33737             box.width -= sw;
33738             this.split.el.setLeft(box.x+box.width);
33739             this.split.el.setTop(box.y);
33740             this.split.el.setHeight(box.height);
33741         }
33742         if(this.collapsed){
33743             this.updateBody(null, box.height);
33744         }
33745         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33746     }
33747 });
33748 /*
33749  * Based on:
33750  * Ext JS Library 1.1.1
33751  * Copyright(c) 2006-2007, Ext JS, LLC.
33752  *
33753  * Originally Released Under LGPL - original licence link has changed is not relivant.
33754  *
33755  * Fork - LGPL
33756  * <script type="text/javascript">
33757  */
33758  
33759  
33760 /*
33761  * Private internal class for reading and applying state
33762  */
33763 Roo.LayoutStateManager = function(layout){
33764      // default empty state
33765      this.state = {
33766         north: {},
33767         south: {},
33768         east: {},
33769         west: {}       
33770     };
33771 };
33772
33773 Roo.LayoutStateManager.prototype = {
33774     init : function(layout, provider){
33775         this.provider = provider;
33776         var state = provider.get(layout.id+"-layout-state");
33777         if(state){
33778             var wasUpdating = layout.isUpdating();
33779             if(!wasUpdating){
33780                 layout.beginUpdate();
33781             }
33782             for(var key in state){
33783                 if(typeof state[key] != "function"){
33784                     var rstate = state[key];
33785                     var r = layout.getRegion(key);
33786                     if(r && rstate){
33787                         if(rstate.size){
33788                             r.resizeTo(rstate.size);
33789                         }
33790                         if(rstate.collapsed == true){
33791                             r.collapse(true);
33792                         }else{
33793                             r.expand(null, true);
33794                         }
33795                     }
33796                 }
33797             }
33798             if(!wasUpdating){
33799                 layout.endUpdate();
33800             }
33801             this.state = state; 
33802         }
33803         this.layout = layout;
33804         layout.on("regionresized", this.onRegionResized, this);
33805         layout.on("regioncollapsed", this.onRegionCollapsed, this);
33806         layout.on("regionexpanded", this.onRegionExpanded, this);
33807     },
33808     
33809     storeState : function(){
33810         this.provider.set(this.layout.id+"-layout-state", this.state);
33811     },
33812     
33813     onRegionResized : function(region, newSize){
33814         this.state[region.getPosition()].size = newSize;
33815         this.storeState();
33816     },
33817     
33818     onRegionCollapsed : function(region){
33819         this.state[region.getPosition()].collapsed = true;
33820         this.storeState();
33821     },
33822     
33823     onRegionExpanded : function(region){
33824         this.state[region.getPosition()].collapsed = false;
33825         this.storeState();
33826     }
33827 };/*
33828  * Based on:
33829  * Ext JS Library 1.1.1
33830  * Copyright(c) 2006-2007, Ext JS, LLC.
33831  *
33832  * Originally Released Under LGPL - original licence link has changed is not relivant.
33833  *
33834  * Fork - LGPL
33835  * <script type="text/javascript">
33836  */
33837 /**
33838  * @class Roo.ContentPanel
33839  * @extends Roo.util.Observable
33840  * A basic ContentPanel element.
33841  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33842  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33843  * @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
33844  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33845  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33846  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33847  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33848  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33849  * @cfg {String} title          The title for this panel
33850  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33851  * @cfg {String} url            Calls {@link #setUrl} with this value
33852  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33853  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33854  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33855  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33856
33857  * @constructor
33858  * Create a new ContentPanel.
33859  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33860  * @param {String/Object} config A string to set only the title or a config object
33861  * @param {String} content (optional) Set the HTML content for this panel
33862  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33863  */
33864 Roo.ContentPanel = function(el, config, content){
33865     
33866      
33867     /*
33868     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
33869         config = el;
33870         el = Roo.id();
33871     }
33872     if (config && config.parentLayout) { 
33873         el = config.parentLayout.el.createChild(); 
33874     }
33875     */
33876     if(el.autoCreate){ // xtype is available if this is called from factory
33877         config = el;
33878         el = Roo.id();
33879     }
33880     this.el = Roo.get(el);
33881     if(!this.el && config && config.autoCreate){
33882         if(typeof config.autoCreate == "object"){
33883             if(!config.autoCreate.id){
33884                 config.autoCreate.id = config.id||el;
33885             }
33886             this.el = Roo.DomHelper.append(document.body,
33887                         config.autoCreate, true);
33888         }else{
33889             this.el = Roo.DomHelper.append(document.body,
33890                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
33891         }
33892     }
33893     this.closable = false;
33894     this.loaded = false;
33895     this.active = false;
33896     if(typeof config == "string"){
33897         this.title = config;
33898     }else{
33899         Roo.apply(this, config);
33900     }
33901     
33902     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33903         this.wrapEl = this.el.wrap();
33904         this.toolbar.container = this.el.insertSibling(false, 'before');
33905         this.toolbar = new Roo.Toolbar(this.toolbar);
33906     }
33907     
33908     // xtype created footer. - not sure if will work as we normally have to render first..
33909     if (this.footer && !this.footer.el && this.footer.xtype) {
33910         if (!this.wrapEl) {
33911             this.wrapEl = this.el.wrap();
33912         }
33913     
33914         this.footer.container = this.wrapEl.createChild();
33915          
33916         this.footer = Roo.factory(this.footer, Roo);
33917         
33918     }
33919     
33920     if(this.resizeEl){
33921         this.resizeEl = Roo.get(this.resizeEl, true);
33922     }else{
33923         this.resizeEl = this.el;
33924     }
33925     // handle view.xtype
33926     
33927  
33928     
33929     
33930     this.addEvents({
33931         /**
33932          * @event activate
33933          * Fires when this panel is activated. 
33934          * @param {Roo.ContentPanel} this
33935          */
33936         "activate" : true,
33937         /**
33938          * @event deactivate
33939          * Fires when this panel is activated. 
33940          * @param {Roo.ContentPanel} this
33941          */
33942         "deactivate" : true,
33943
33944         /**
33945          * @event resize
33946          * Fires when this panel is resized if fitToFrame is true.
33947          * @param {Roo.ContentPanel} this
33948          * @param {Number} width The width after any component adjustments
33949          * @param {Number} height The height after any component adjustments
33950          */
33951         "resize" : true,
33952         
33953          /**
33954          * @event render
33955          * Fires when this tab is created
33956          * @param {Roo.ContentPanel} this
33957          */
33958         "render" : true
33959         
33960         
33961         
33962     });
33963     
33964
33965     
33966     
33967     if(this.autoScroll){
33968         this.resizeEl.setStyle("overflow", "auto");
33969     } else {
33970         // fix randome scrolling
33971         this.el.on('scroll', function() {
33972             Roo.log('fix random scolling');
33973             this.scrollTo('top',0); 
33974         });
33975     }
33976     content = content || this.content;
33977     if(content){
33978         this.setContent(content);
33979     }
33980     if(config && config.url){
33981         this.setUrl(this.url, this.params, this.loadOnce);
33982     }
33983     
33984     
33985     
33986     Roo.ContentPanel.superclass.constructor.call(this);
33987     
33988     if (this.view && typeof(this.view.xtype) != 'undefined') {
33989         this.view.el = this.el.appendChild(document.createElement("div"));
33990         this.view = Roo.factory(this.view); 
33991         this.view.render  &&  this.view.render(false, '');  
33992     }
33993     
33994     
33995     this.fireEvent('render', this);
33996 };
33997
33998 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
33999     tabTip:'',
34000     setRegion : function(region){
34001         this.region = region;
34002         if(region){
34003            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34004         }else{
34005            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34006         } 
34007     },
34008     
34009     /**
34010      * Returns the toolbar for this Panel if one was configured. 
34011      * @return {Roo.Toolbar} 
34012      */
34013     getToolbar : function(){
34014         return this.toolbar;
34015     },
34016     
34017     setActiveState : function(active){
34018         this.active = active;
34019         if(!active){
34020             this.fireEvent("deactivate", this);
34021         }else{
34022             this.fireEvent("activate", this);
34023         }
34024     },
34025     /**
34026      * Updates this panel's element
34027      * @param {String} content The new content
34028      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34029     */
34030     setContent : function(content, loadScripts){
34031         this.el.update(content, loadScripts);
34032     },
34033
34034     ignoreResize : function(w, h){
34035         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34036             return true;
34037         }else{
34038             this.lastSize = {width: w, height: h};
34039             return false;
34040         }
34041     },
34042     /**
34043      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34044      * @return {Roo.UpdateManager} The UpdateManager
34045      */
34046     getUpdateManager : function(){
34047         return this.el.getUpdateManager();
34048     },
34049      /**
34050      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34051      * @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:
34052 <pre><code>
34053 panel.load({
34054     url: "your-url.php",
34055     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34056     callback: yourFunction,
34057     scope: yourObject, //(optional scope)
34058     discardUrl: false,
34059     nocache: false,
34060     text: "Loading...",
34061     timeout: 30,
34062     scripts: false
34063 });
34064 </code></pre>
34065      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34066      * 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.
34067      * @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}
34068      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34069      * @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.
34070      * @return {Roo.ContentPanel} this
34071      */
34072     load : function(){
34073         var um = this.el.getUpdateManager();
34074         um.update.apply(um, arguments);
34075         return this;
34076     },
34077
34078
34079     /**
34080      * 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.
34081      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34082      * @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)
34083      * @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)
34084      * @return {Roo.UpdateManager} The UpdateManager
34085      */
34086     setUrl : function(url, params, loadOnce){
34087         if(this.refreshDelegate){
34088             this.removeListener("activate", this.refreshDelegate);
34089         }
34090         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34091         this.on("activate", this.refreshDelegate);
34092         return this.el.getUpdateManager();
34093     },
34094     
34095     _handleRefresh : function(url, params, loadOnce){
34096         if(!loadOnce || !this.loaded){
34097             var updater = this.el.getUpdateManager();
34098             updater.update(url, params, this._setLoaded.createDelegate(this));
34099         }
34100     },
34101     
34102     _setLoaded : function(){
34103         this.loaded = true;
34104     }, 
34105     
34106     /**
34107      * Returns this panel's id
34108      * @return {String} 
34109      */
34110     getId : function(){
34111         return this.el.id;
34112     },
34113     
34114     /** 
34115      * Returns this panel's element - used by regiosn to add.
34116      * @return {Roo.Element} 
34117      */
34118     getEl : function(){
34119         return this.wrapEl || this.el;
34120     },
34121     
34122     adjustForComponents : function(width, height)
34123     {
34124         //Roo.log('adjustForComponents ');
34125         if(this.resizeEl != this.el){
34126             width -= this.el.getFrameWidth('lr');
34127             height -= this.el.getFrameWidth('tb');
34128         }
34129         if(this.toolbar){
34130             var te = this.toolbar.getEl();
34131             height -= te.getHeight();
34132             te.setWidth(width);
34133         }
34134         if(this.footer){
34135             var te = this.footer.getEl();
34136             Roo.log("footer:" + te.getHeight());
34137             
34138             height -= te.getHeight();
34139             te.setWidth(width);
34140         }
34141         
34142         
34143         if(this.adjustments){
34144             width += this.adjustments[0];
34145             height += this.adjustments[1];
34146         }
34147         return {"width": width, "height": height};
34148     },
34149     
34150     setSize : function(width, height){
34151         if(this.fitToFrame && !this.ignoreResize(width, height)){
34152             if(this.fitContainer && this.resizeEl != this.el){
34153                 this.el.setSize(width, height);
34154             }
34155             var size = this.adjustForComponents(width, height);
34156             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34157             this.fireEvent('resize', this, size.width, size.height);
34158         }
34159     },
34160     
34161     /**
34162      * Returns this panel's title
34163      * @return {String} 
34164      */
34165     getTitle : function(){
34166         return this.title;
34167     },
34168     
34169     /**
34170      * Set this panel's title
34171      * @param {String} title
34172      */
34173     setTitle : function(title){
34174         this.title = title;
34175         if(this.region){
34176             this.region.updatePanelTitle(this, title);
34177         }
34178     },
34179     
34180     /**
34181      * Returns true is this panel was configured to be closable
34182      * @return {Boolean} 
34183      */
34184     isClosable : function(){
34185         return this.closable;
34186     },
34187     
34188     beforeSlide : function(){
34189         this.el.clip();
34190         this.resizeEl.clip();
34191     },
34192     
34193     afterSlide : function(){
34194         this.el.unclip();
34195         this.resizeEl.unclip();
34196     },
34197     
34198     /**
34199      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34200      *   Will fail silently if the {@link #setUrl} method has not been called.
34201      *   This does not activate the panel, just updates its content.
34202      */
34203     refresh : function(){
34204         if(this.refreshDelegate){
34205            this.loaded = false;
34206            this.refreshDelegate();
34207         }
34208     },
34209     
34210     /**
34211      * Destroys this panel
34212      */
34213     destroy : function(){
34214         this.el.removeAllListeners();
34215         var tempEl = document.createElement("span");
34216         tempEl.appendChild(this.el.dom);
34217         tempEl.innerHTML = "";
34218         this.el.remove();
34219         this.el = null;
34220     },
34221     
34222     /**
34223      * form - if the content panel contains a form - this is a reference to it.
34224      * @type {Roo.form.Form}
34225      */
34226     form : false,
34227     /**
34228      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34229      *    This contains a reference to it.
34230      * @type {Roo.View}
34231      */
34232     view : false,
34233     
34234       /**
34235      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34236      * <pre><code>
34237
34238 layout.addxtype({
34239        xtype : 'Form',
34240        items: [ .... ]
34241    }
34242 );
34243
34244 </code></pre>
34245      * @param {Object} cfg Xtype definition of item to add.
34246      */
34247     
34248     addxtype : function(cfg) {
34249         // add form..
34250         if (cfg.xtype.match(/^Form$/)) {
34251             
34252             var el;
34253             //if (this.footer) {
34254             //    el = this.footer.container.insertSibling(false, 'before');
34255             //} else {
34256                 el = this.el.createChild();
34257             //}
34258
34259             this.form = new  Roo.form.Form(cfg);
34260             
34261             
34262             if ( this.form.allItems.length) this.form.render(el.dom);
34263             return this.form;
34264         }
34265         // should only have one of theses..
34266         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34267             // views.. should not be just added - used named prop 'view''
34268             
34269             cfg.el = this.el.appendChild(document.createElement("div"));
34270             // factory?
34271             
34272             var ret = new Roo.factory(cfg);
34273              
34274              ret.render && ret.render(false, ''); // render blank..
34275             this.view = ret;
34276             return ret;
34277         }
34278         return false;
34279     }
34280 });
34281
34282 /**
34283  * @class Roo.GridPanel
34284  * @extends Roo.ContentPanel
34285  * @constructor
34286  * Create a new GridPanel.
34287  * @param {Roo.grid.Grid} grid The grid for this panel
34288  * @param {String/Object} config A string to set only the panel's title, or a config object
34289  */
34290 Roo.GridPanel = function(grid, config){
34291     
34292   
34293     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34294         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34295         
34296     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34297     
34298     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34299     
34300     if(this.toolbar){
34301         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34302     }
34303     // xtype created footer. - not sure if will work as we normally have to render first..
34304     if (this.footer && !this.footer.el && this.footer.xtype) {
34305         
34306         this.footer.container = this.grid.getView().getFooterPanel(true);
34307         this.footer.dataSource = this.grid.dataSource;
34308         this.footer = Roo.factory(this.footer, Roo);
34309         
34310     }
34311     
34312     grid.monitorWindowResize = false; // turn off autosizing
34313     grid.autoHeight = false;
34314     grid.autoWidth = false;
34315     this.grid = grid;
34316     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34317 };
34318
34319 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34320     getId : function(){
34321         return this.grid.id;
34322     },
34323     
34324     /**
34325      * Returns the grid for this panel
34326      * @return {Roo.grid.Grid} 
34327      */
34328     getGrid : function(){
34329         return this.grid;    
34330     },
34331     
34332     setSize : function(width, height){
34333         if(!this.ignoreResize(width, height)){
34334             var grid = this.grid;
34335             var size = this.adjustForComponents(width, height);
34336             grid.getGridEl().setSize(size.width, size.height);
34337             grid.autoSize();
34338         }
34339     },
34340     
34341     beforeSlide : function(){
34342         this.grid.getView().scroller.clip();
34343     },
34344     
34345     afterSlide : function(){
34346         this.grid.getView().scroller.unclip();
34347     },
34348     
34349     destroy : function(){
34350         this.grid.destroy();
34351         delete this.grid;
34352         Roo.GridPanel.superclass.destroy.call(this); 
34353     }
34354 });
34355
34356
34357 /**
34358  * @class Roo.NestedLayoutPanel
34359  * @extends Roo.ContentPanel
34360  * @constructor
34361  * Create a new NestedLayoutPanel.
34362  * 
34363  * 
34364  * @param {Roo.BorderLayout} layout The layout for this panel
34365  * @param {String/Object} config A string to set only the title or a config object
34366  */
34367 Roo.NestedLayoutPanel = function(layout, config)
34368 {
34369     // construct with only one argument..
34370     /* FIXME - implement nicer consturctors
34371     if (layout.layout) {
34372         config = layout;
34373         layout = config.layout;
34374         delete config.layout;
34375     }
34376     if (layout.xtype && !layout.getEl) {
34377         // then layout needs constructing..
34378         layout = Roo.factory(layout, Roo);
34379     }
34380     */
34381     
34382     
34383     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
34384     
34385     layout.monitorWindowResize = false; // turn off autosizing
34386     this.layout = layout;
34387     this.layout.getEl().addClass("x-layout-nested-layout");
34388     
34389     
34390     
34391     
34392 };
34393
34394 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
34395
34396     setSize : function(width, height){
34397         if(!this.ignoreResize(width, height)){
34398             var size = this.adjustForComponents(width, height);
34399             var el = this.layout.getEl();
34400             el.setSize(size.width, size.height);
34401             var touch = el.dom.offsetWidth;
34402             this.layout.layout();
34403             // ie requires a double layout on the first pass
34404             if(Roo.isIE && !this.initialized){
34405                 this.initialized = true;
34406                 this.layout.layout();
34407             }
34408         }
34409     },
34410     
34411     // activate all subpanels if not currently active..
34412     
34413     setActiveState : function(active){
34414         this.active = active;
34415         if(!active){
34416             this.fireEvent("deactivate", this);
34417             return;
34418         }
34419         
34420         this.fireEvent("activate", this);
34421         // not sure if this should happen before or after..
34422         if (!this.layout) {
34423             return; // should not happen..
34424         }
34425         var reg = false;
34426         for (var r in this.layout.regions) {
34427             reg = this.layout.getRegion(r);
34428             if (reg.getActivePanel()) {
34429                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34430                 reg.setActivePanel(reg.getActivePanel());
34431                 continue;
34432             }
34433             if (!reg.panels.length) {
34434                 continue;
34435             }
34436             reg.showPanel(reg.getPanel(0));
34437         }
34438         
34439         
34440         
34441         
34442     },
34443     
34444     /**
34445      * Returns the nested BorderLayout for this panel
34446      * @return {Roo.BorderLayout} 
34447      */
34448     getLayout : function(){
34449         return this.layout;
34450     },
34451     
34452      /**
34453      * Adds a xtype elements to the layout of the nested panel
34454      * <pre><code>
34455
34456 panel.addxtype({
34457        xtype : 'ContentPanel',
34458        region: 'west',
34459        items: [ .... ]
34460    }
34461 );
34462
34463 panel.addxtype({
34464         xtype : 'NestedLayoutPanel',
34465         region: 'west',
34466         layout: {
34467            center: { },
34468            west: { }   
34469         },
34470         items : [ ... list of content panels or nested layout panels.. ]
34471    }
34472 );
34473 </code></pre>
34474      * @param {Object} cfg Xtype definition of item to add.
34475      */
34476     addxtype : function(cfg) {
34477         return this.layout.addxtype(cfg);
34478     
34479     }
34480 });
34481
34482 Roo.ScrollPanel = function(el, config, content){
34483     config = config || {};
34484     config.fitToFrame = true;
34485     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
34486     
34487     this.el.dom.style.overflow = "hidden";
34488     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
34489     this.el.removeClass("x-layout-inactive-content");
34490     this.el.on("mousewheel", this.onWheel, this);
34491
34492     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
34493     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
34494     up.unselectable(); down.unselectable();
34495     up.on("click", this.scrollUp, this);
34496     down.on("click", this.scrollDown, this);
34497     up.addClassOnOver("x-scroller-btn-over");
34498     down.addClassOnOver("x-scroller-btn-over");
34499     up.addClassOnClick("x-scroller-btn-click");
34500     down.addClassOnClick("x-scroller-btn-click");
34501     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
34502
34503     this.resizeEl = this.el;
34504     this.el = wrap; this.up = up; this.down = down;
34505 };
34506
34507 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
34508     increment : 100,
34509     wheelIncrement : 5,
34510     scrollUp : function(){
34511         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
34512     },
34513
34514     scrollDown : function(){
34515         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
34516     },
34517
34518     afterScroll : function(){
34519         var el = this.resizeEl;
34520         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
34521         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
34522         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
34523     },
34524
34525     setSize : function(){
34526         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
34527         this.afterScroll();
34528     },
34529
34530     onWheel : function(e){
34531         var d = e.getWheelDelta();
34532         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
34533         this.afterScroll();
34534         e.stopEvent();
34535     },
34536
34537     setContent : function(content, loadScripts){
34538         this.resizeEl.update(content, loadScripts);
34539     }
34540
34541 });
34542
34543
34544
34545
34546
34547
34548
34549
34550
34551 /**
34552  * @class Roo.TreePanel
34553  * @extends Roo.ContentPanel
34554  * @constructor
34555  * Create a new TreePanel. - defaults to fit/scoll contents.
34556  * @param {String/Object} config A string to set only the panel's title, or a config object
34557  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
34558  */
34559 Roo.TreePanel = function(config){
34560     var el = config.el;
34561     var tree = config.tree;
34562     delete config.tree; 
34563     delete config.el; // hopefull!
34564     
34565     // wrapper for IE7 strict & safari scroll issue
34566     
34567     var treeEl = el.createChild();
34568     config.resizeEl = treeEl;
34569     
34570     
34571     
34572     Roo.TreePanel.superclass.constructor.call(this, el, config);
34573  
34574  
34575     this.tree = new Roo.tree.TreePanel(treeEl , tree);
34576     //console.log(tree);
34577     this.on('activate', function()
34578     {
34579         if (this.tree.rendered) {
34580             return;
34581         }
34582         //console.log('render tree');
34583         this.tree.render();
34584     });
34585     // this should not be needed.. - it's actually the 'el' that resizes?
34586     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
34587     
34588     //this.on('resize',  function (cp, w, h) {
34589     //        this.tree.innerCt.setWidth(w);
34590     //        this.tree.innerCt.setHeight(h);
34591     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
34592     //});
34593
34594         
34595     
34596 };
34597
34598 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
34599     fitToFrame : true,
34600     autoScroll : true
34601 });
34602
34603
34604
34605
34606
34607
34608
34609
34610
34611
34612
34613 /*
34614  * Based on:
34615  * Ext JS Library 1.1.1
34616  * Copyright(c) 2006-2007, Ext JS, LLC.
34617  *
34618  * Originally Released Under LGPL - original licence link has changed is not relivant.
34619  *
34620  * Fork - LGPL
34621  * <script type="text/javascript">
34622  */
34623  
34624
34625 /**
34626  * @class Roo.ReaderLayout
34627  * @extends Roo.BorderLayout
34628  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
34629  * center region containing two nested regions (a top one for a list view and one for item preview below),
34630  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
34631  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
34632  * expedites the setup of the overall layout and regions for this common application style.
34633  * Example:
34634  <pre><code>
34635 var reader = new Roo.ReaderLayout();
34636 var CP = Roo.ContentPanel;  // shortcut for adding
34637
34638 reader.beginUpdate();
34639 reader.add("north", new CP("north", "North"));
34640 reader.add("west", new CP("west", {title: "West"}));
34641 reader.add("east", new CP("east", {title: "East"}));
34642
34643 reader.regions.listView.add(new CP("listView", "List"));
34644 reader.regions.preview.add(new CP("preview", "Preview"));
34645 reader.endUpdate();
34646 </code></pre>
34647 * @constructor
34648 * Create a new ReaderLayout
34649 * @param {Object} config Configuration options
34650 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
34651 * document.body if omitted)
34652 */
34653 Roo.ReaderLayout = function(config, renderTo){
34654     var c = config || {size:{}};
34655     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
34656         north: c.north !== false ? Roo.apply({
34657             split:false,
34658             initialSize: 32,
34659             titlebar: false
34660         }, c.north) : false,
34661         west: c.west !== false ? Roo.apply({
34662             split:true,
34663             initialSize: 200,
34664             minSize: 175,
34665             maxSize: 400,
34666             titlebar: true,
34667             collapsible: true,
34668             animate: true,
34669             margins:{left:5,right:0,bottom:5,top:5},
34670             cmargins:{left:5,right:5,bottom:5,top:5}
34671         }, c.west) : false,
34672         east: c.east !== false ? Roo.apply({
34673             split:true,
34674             initialSize: 200,
34675             minSize: 175,
34676             maxSize: 400,
34677             titlebar: true,
34678             collapsible: true,
34679             animate: true,
34680             margins:{left:0,right:5,bottom:5,top:5},
34681             cmargins:{left:5,right:5,bottom:5,top:5}
34682         }, c.east) : false,
34683         center: Roo.apply({
34684             tabPosition: 'top',
34685             autoScroll:false,
34686             closeOnTab: true,
34687             titlebar:false,
34688             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
34689         }, c.center)
34690     });
34691
34692     this.el.addClass('x-reader');
34693
34694     this.beginUpdate();
34695
34696     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
34697         south: c.preview !== false ? Roo.apply({
34698             split:true,
34699             initialSize: 200,
34700             minSize: 100,
34701             autoScroll:true,
34702             collapsible:true,
34703             titlebar: true,
34704             cmargins:{top:5,left:0, right:0, bottom:0}
34705         }, c.preview) : false,
34706         center: Roo.apply({
34707             autoScroll:false,
34708             titlebar:false,
34709             minHeight:200
34710         }, c.listView)
34711     });
34712     this.add('center', new Roo.NestedLayoutPanel(inner,
34713             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
34714
34715     this.endUpdate();
34716
34717     this.regions.preview = inner.getRegion('south');
34718     this.regions.listView = inner.getRegion('center');
34719 };
34720
34721 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
34722  * Based on:
34723  * Ext JS Library 1.1.1
34724  * Copyright(c) 2006-2007, Ext JS, LLC.
34725  *
34726  * Originally Released Under LGPL - original licence link has changed is not relivant.
34727  *
34728  * Fork - LGPL
34729  * <script type="text/javascript">
34730  */
34731  
34732 /**
34733  * @class Roo.grid.Grid
34734  * @extends Roo.util.Observable
34735  * This class represents the primary interface of a component based grid control.
34736  * <br><br>Usage:<pre><code>
34737  var grid = new Roo.grid.Grid("my-container-id", {
34738      ds: myDataStore,
34739      cm: myColModel,
34740      selModel: mySelectionModel,
34741      autoSizeColumns: true,
34742      monitorWindowResize: false,
34743      trackMouseOver: true
34744  });
34745  // set any options
34746  grid.render();
34747  * </code></pre>
34748  * <b>Common Problems:</b><br/>
34749  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
34750  * element will correct this<br/>
34751  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
34752  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
34753  * are unpredictable.<br/>
34754  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
34755  * grid to calculate dimensions/offsets.<br/>
34756   * @constructor
34757  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
34758  * The container MUST have some type of size defined for the grid to fill. The container will be
34759  * automatically set to position relative if it isn't already.
34760  * @param {Object} config A config object that sets properties on this grid.
34761  */
34762 Roo.grid.Grid = function(container, config){
34763         // initialize the container
34764         this.container = Roo.get(container);
34765         this.container.update("");
34766         this.container.setStyle("overflow", "hidden");
34767     this.container.addClass('x-grid-container');
34768
34769     this.id = this.container.id;
34770
34771     Roo.apply(this, config);
34772     // check and correct shorthanded configs
34773     if(this.ds){
34774         this.dataSource = this.ds;
34775         delete this.ds;
34776     }
34777     if(this.cm){
34778         this.colModel = this.cm;
34779         delete this.cm;
34780     }
34781     if(this.sm){
34782         this.selModel = this.sm;
34783         delete this.sm;
34784     }
34785
34786     if (this.selModel) {
34787         this.selModel = Roo.factory(this.selModel, Roo.grid);
34788         this.sm = this.selModel;
34789         this.sm.xmodule = this.xmodule || false;
34790     }
34791     if (typeof(this.colModel.config) == 'undefined') {
34792         this.colModel = new Roo.grid.ColumnModel(this.colModel);
34793         this.cm = this.colModel;
34794         this.cm.xmodule = this.xmodule || false;
34795     }
34796     if (this.dataSource) {
34797         this.dataSource= Roo.factory(this.dataSource, Roo.data);
34798         this.ds = this.dataSource;
34799         this.ds.xmodule = this.xmodule || false;
34800          
34801     }
34802     
34803     
34804     
34805     if(this.width){
34806         this.container.setWidth(this.width);
34807     }
34808
34809     if(this.height){
34810         this.container.setHeight(this.height);
34811     }
34812     /** @private */
34813         this.addEvents({
34814         // raw events
34815         /**
34816          * @event click
34817          * The raw click event for the entire grid.
34818          * @param {Roo.EventObject} e
34819          */
34820         "click" : true,
34821         /**
34822          * @event dblclick
34823          * The raw dblclick event for the entire grid.
34824          * @param {Roo.EventObject} e
34825          */
34826         "dblclick" : true,
34827         /**
34828          * @event contextmenu
34829          * The raw contextmenu event for the entire grid.
34830          * @param {Roo.EventObject} e
34831          */
34832         "contextmenu" : true,
34833         /**
34834          * @event mousedown
34835          * The raw mousedown event for the entire grid.
34836          * @param {Roo.EventObject} e
34837          */
34838         "mousedown" : true,
34839         /**
34840          * @event mouseup
34841          * The raw mouseup event for the entire grid.
34842          * @param {Roo.EventObject} e
34843          */
34844         "mouseup" : true,
34845         /**
34846          * @event mouseover
34847          * The raw mouseover event for the entire grid.
34848          * @param {Roo.EventObject} e
34849          */
34850         "mouseover" : true,
34851         /**
34852          * @event mouseout
34853          * The raw mouseout event for the entire grid.
34854          * @param {Roo.EventObject} e
34855          */
34856         "mouseout" : true,
34857         /**
34858          * @event keypress
34859          * The raw keypress event for the entire grid.
34860          * @param {Roo.EventObject} e
34861          */
34862         "keypress" : true,
34863         /**
34864          * @event keydown
34865          * The raw keydown event for the entire grid.
34866          * @param {Roo.EventObject} e
34867          */
34868         "keydown" : true,
34869
34870         // custom events
34871
34872         /**
34873          * @event cellclick
34874          * Fires when a cell is clicked
34875          * @param {Grid} this
34876          * @param {Number} rowIndex
34877          * @param {Number} columnIndex
34878          * @param {Roo.EventObject} e
34879          */
34880         "cellclick" : true,
34881         /**
34882          * @event celldblclick
34883          * Fires when a cell is double clicked
34884          * @param {Grid} this
34885          * @param {Number} rowIndex
34886          * @param {Number} columnIndex
34887          * @param {Roo.EventObject} e
34888          */
34889         "celldblclick" : true,
34890         /**
34891          * @event rowclick
34892          * Fires when a row is clicked
34893          * @param {Grid} this
34894          * @param {Number} rowIndex
34895          * @param {Roo.EventObject} e
34896          */
34897         "rowclick" : true,
34898         /**
34899          * @event rowdblclick
34900          * Fires when a row is double clicked
34901          * @param {Grid} this
34902          * @param {Number} rowIndex
34903          * @param {Roo.EventObject} e
34904          */
34905         "rowdblclick" : true,
34906         /**
34907          * @event headerclick
34908          * Fires when a header is clicked
34909          * @param {Grid} this
34910          * @param {Number} columnIndex
34911          * @param {Roo.EventObject} e
34912          */
34913         "headerclick" : true,
34914         /**
34915          * @event headerdblclick
34916          * Fires when a header cell is double clicked
34917          * @param {Grid} this
34918          * @param {Number} columnIndex
34919          * @param {Roo.EventObject} e
34920          */
34921         "headerdblclick" : true,
34922         /**
34923          * @event rowcontextmenu
34924          * Fires when a row is right clicked
34925          * @param {Grid} this
34926          * @param {Number} rowIndex
34927          * @param {Roo.EventObject} e
34928          */
34929         "rowcontextmenu" : true,
34930         /**
34931          * @event cellcontextmenu
34932          * Fires when a cell is right clicked
34933          * @param {Grid} this
34934          * @param {Number} rowIndex
34935          * @param {Number} cellIndex
34936          * @param {Roo.EventObject} e
34937          */
34938          "cellcontextmenu" : true,
34939         /**
34940          * @event headercontextmenu
34941          * Fires when a header is right clicked
34942          * @param {Grid} this
34943          * @param {Number} columnIndex
34944          * @param {Roo.EventObject} e
34945          */
34946         "headercontextmenu" : true,
34947         /**
34948          * @event bodyscroll
34949          * Fires when the body element is scrolled
34950          * @param {Number} scrollLeft
34951          * @param {Number} scrollTop
34952          */
34953         "bodyscroll" : true,
34954         /**
34955          * @event columnresize
34956          * Fires when the user resizes a column
34957          * @param {Number} columnIndex
34958          * @param {Number} newSize
34959          */
34960         "columnresize" : true,
34961         /**
34962          * @event columnmove
34963          * Fires when the user moves a column
34964          * @param {Number} oldIndex
34965          * @param {Number} newIndex
34966          */
34967         "columnmove" : true,
34968         /**
34969          * @event startdrag
34970          * Fires when row(s) start being dragged
34971          * @param {Grid} this
34972          * @param {Roo.GridDD} dd The drag drop object
34973          * @param {event} e The raw browser event
34974          */
34975         "startdrag" : true,
34976         /**
34977          * @event enddrag
34978          * Fires when a drag operation is complete
34979          * @param {Grid} this
34980          * @param {Roo.GridDD} dd The drag drop object
34981          * @param {event} e The raw browser event
34982          */
34983         "enddrag" : true,
34984         /**
34985          * @event dragdrop
34986          * Fires when dragged row(s) are dropped on a valid DD target
34987          * @param {Grid} this
34988          * @param {Roo.GridDD} dd The drag drop object
34989          * @param {String} targetId The target drag drop object
34990          * @param {event} e The raw browser event
34991          */
34992         "dragdrop" : true,
34993         /**
34994          * @event dragover
34995          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
34996          * @param {Grid} this
34997          * @param {Roo.GridDD} dd The drag drop object
34998          * @param {String} targetId The target drag drop object
34999          * @param {event} e The raw browser event
35000          */
35001         "dragover" : true,
35002         /**
35003          * @event dragenter
35004          *  Fires when the dragged row(s) first cross another DD target while being dragged
35005          * @param {Grid} this
35006          * @param {Roo.GridDD} dd The drag drop object
35007          * @param {String} targetId The target drag drop object
35008          * @param {event} e The raw browser event
35009          */
35010         "dragenter" : true,
35011         /**
35012          * @event dragout
35013          * Fires when the dragged row(s) leave another DD target while being dragged
35014          * @param {Grid} this
35015          * @param {Roo.GridDD} dd The drag drop object
35016          * @param {String} targetId The target drag drop object
35017          * @param {event} e The raw browser event
35018          */
35019         "dragout" : true,
35020         /**
35021          * @event rowclass
35022          * Fires when a row is rendered, so you can change add a style to it.
35023          * @param {GridView} gridview   The grid view
35024          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35025          */
35026         'rowclass' : true,
35027
35028         /**
35029          * @event render
35030          * Fires when the grid is rendered
35031          * @param {Grid} grid
35032          */
35033         'render' : true
35034     });
35035
35036     Roo.grid.Grid.superclass.constructor.call(this);
35037 };
35038 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35039     
35040     /**
35041      * @cfg {String} ddGroup - drag drop group.
35042      */
35043
35044     /**
35045      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35046      */
35047     minColumnWidth : 25,
35048
35049     /**
35050      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35051      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35052      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35053      */
35054     autoSizeColumns : false,
35055
35056     /**
35057      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35058      */
35059     autoSizeHeaders : true,
35060
35061     /**
35062      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35063      */
35064     monitorWindowResize : true,
35065
35066     /**
35067      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35068      * rows measured to get a columns size. Default is 0 (all rows).
35069      */
35070     maxRowsToMeasure : 0,
35071
35072     /**
35073      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35074      */
35075     trackMouseOver : true,
35076
35077     /**
35078     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35079     */
35080     
35081     /**
35082     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35083     */
35084     enableDragDrop : false,
35085     
35086     /**
35087     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35088     */
35089     enableColumnMove : true,
35090     
35091     /**
35092     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35093     */
35094     enableColumnHide : true,
35095     
35096     /**
35097     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35098     */
35099     enableRowHeightSync : false,
35100     
35101     /**
35102     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35103     */
35104     stripeRows : true,
35105     
35106     /**
35107     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35108     */
35109     autoHeight : false,
35110
35111     /**
35112      * @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.
35113      */
35114     autoExpandColumn : false,
35115
35116     /**
35117     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35118     * Default is 50.
35119     */
35120     autoExpandMin : 50,
35121
35122     /**
35123     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35124     */
35125     autoExpandMax : 1000,
35126
35127     /**
35128     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35129     */
35130     view : null,
35131
35132     /**
35133     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35134     */
35135     loadMask : false,
35136     /**
35137     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35138     */
35139     dropTarget: false,
35140     
35141    
35142     
35143     // private
35144     rendered : false,
35145
35146     /**
35147     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35148     * of a fixed width. Default is false.
35149     */
35150     /**
35151     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35152     */
35153     /**
35154      * Called once after all setup has been completed and the grid is ready to be rendered.
35155      * @return {Roo.grid.Grid} this
35156      */
35157     render : function()
35158     {
35159         var c = this.container;
35160         // try to detect autoHeight/width mode
35161         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35162             this.autoHeight = true;
35163         }
35164         var view = this.getView();
35165         view.init(this);
35166
35167         c.on("click", this.onClick, this);
35168         c.on("dblclick", this.onDblClick, this);
35169         c.on("contextmenu", this.onContextMenu, this);
35170         c.on("keydown", this.onKeyDown, this);
35171
35172         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35173
35174         this.getSelectionModel().init(this);
35175
35176         view.render();
35177
35178         if(this.loadMask){
35179             this.loadMask = new Roo.LoadMask(this.container,
35180                     Roo.apply({store:this.dataSource}, this.loadMask));
35181         }
35182         
35183         
35184         if (this.toolbar && this.toolbar.xtype) {
35185             this.toolbar.container = this.getView().getHeaderPanel(true);
35186             this.toolbar = new Roo.Toolbar(this.toolbar);
35187         }
35188         if (this.footer && this.footer.xtype) {
35189             this.footer.dataSource = this.getDataSource();
35190             this.footer.container = this.getView().getFooterPanel(true);
35191             this.footer = Roo.factory(this.footer, Roo);
35192         }
35193         if (this.dropTarget && this.dropTarget.xtype) {
35194             delete this.dropTarget.xtype;
35195             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35196         }
35197         
35198         
35199         this.rendered = true;
35200         this.fireEvent('render', this);
35201         return this;
35202     },
35203
35204         /**
35205          * Reconfigures the grid to use a different Store and Column Model.
35206          * The View will be bound to the new objects and refreshed.
35207          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35208          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35209          */
35210     reconfigure : function(dataSource, colModel){
35211         if(this.loadMask){
35212             this.loadMask.destroy();
35213             this.loadMask = new Roo.LoadMask(this.container,
35214                     Roo.apply({store:dataSource}, this.loadMask));
35215         }
35216         this.view.bind(dataSource, colModel);
35217         this.dataSource = dataSource;
35218         this.colModel = colModel;
35219         this.view.refresh(true);
35220     },
35221
35222     // private
35223     onKeyDown : function(e){
35224         this.fireEvent("keydown", e);
35225     },
35226
35227     /**
35228      * Destroy this grid.
35229      * @param {Boolean} removeEl True to remove the element
35230      */
35231     destroy : function(removeEl, keepListeners){
35232         if(this.loadMask){
35233             this.loadMask.destroy();
35234         }
35235         var c = this.container;
35236         c.removeAllListeners();
35237         this.view.destroy();
35238         this.colModel.purgeListeners();
35239         if(!keepListeners){
35240             this.purgeListeners();
35241         }
35242         c.update("");
35243         if(removeEl === true){
35244             c.remove();
35245         }
35246     },
35247
35248     // private
35249     processEvent : function(name, e){
35250         this.fireEvent(name, e);
35251         var t = e.getTarget();
35252         var v = this.view;
35253         var header = v.findHeaderIndex(t);
35254         if(header !== false){
35255             this.fireEvent("header" + name, this, header, e);
35256         }else{
35257             var row = v.findRowIndex(t);
35258             var cell = v.findCellIndex(t);
35259             if(row !== false){
35260                 this.fireEvent("row" + name, this, row, e);
35261                 if(cell !== false){
35262                     this.fireEvent("cell" + name, this, row, cell, e);
35263                 }
35264             }
35265         }
35266     },
35267
35268     // private
35269     onClick : function(e){
35270         this.processEvent("click", e);
35271     },
35272
35273     // private
35274     onContextMenu : function(e, t){
35275         this.processEvent("contextmenu", e);
35276     },
35277
35278     // private
35279     onDblClick : function(e){
35280         this.processEvent("dblclick", e);
35281     },
35282
35283     // private
35284     walkCells : function(row, col, step, fn, scope){
35285         var cm = this.colModel, clen = cm.getColumnCount();
35286         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35287         if(step < 0){
35288             if(col < 0){
35289                 row--;
35290                 first = false;
35291             }
35292             while(row >= 0){
35293                 if(!first){
35294                     col = clen-1;
35295                 }
35296                 first = false;
35297                 while(col >= 0){
35298                     if(fn.call(scope || this, row, col, cm) === true){
35299                         return [row, col];
35300                     }
35301                     col--;
35302                 }
35303                 row--;
35304             }
35305         } else {
35306             if(col >= clen){
35307                 row++;
35308                 first = false;
35309             }
35310             while(row < rlen){
35311                 if(!first){
35312                     col = 0;
35313                 }
35314                 first = false;
35315                 while(col < clen){
35316                     if(fn.call(scope || this, row, col, cm) === true){
35317                         return [row, col];
35318                     }
35319                     col++;
35320                 }
35321                 row++;
35322             }
35323         }
35324         return null;
35325     },
35326
35327     // private
35328     getSelections : function(){
35329         return this.selModel.getSelections();
35330     },
35331
35332     /**
35333      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
35334      * but if manual update is required this method will initiate it.
35335      */
35336     autoSize : function(){
35337         if(this.rendered){
35338             this.view.layout();
35339             if(this.view.adjustForScroll){
35340                 this.view.adjustForScroll();
35341             }
35342         }
35343     },
35344
35345     /**
35346      * Returns the grid's underlying element.
35347      * @return {Element} The element
35348      */
35349     getGridEl : function(){
35350         return this.container;
35351     },
35352
35353     // private for compatibility, overridden by editor grid
35354     stopEditing : function(){},
35355
35356     /**
35357      * Returns the grid's SelectionModel.
35358      * @return {SelectionModel}
35359      */
35360     getSelectionModel : function(){
35361         if(!this.selModel){
35362             this.selModel = new Roo.grid.RowSelectionModel();
35363         }
35364         return this.selModel;
35365     },
35366
35367     /**
35368      * Returns the grid's DataSource.
35369      * @return {DataSource}
35370      */
35371     getDataSource : function(){
35372         return this.dataSource;
35373     },
35374
35375     /**
35376      * Returns the grid's ColumnModel.
35377      * @return {ColumnModel}
35378      */
35379     getColumnModel : function(){
35380         return this.colModel;
35381     },
35382
35383     /**
35384      * Returns the grid's GridView object.
35385      * @return {GridView}
35386      */
35387     getView : function(){
35388         if(!this.view){
35389             this.view = new Roo.grid.GridView(this.viewConfig);
35390         }
35391         return this.view;
35392     },
35393     /**
35394      * Called to get grid's drag proxy text, by default returns this.ddText.
35395      * @return {String}
35396      */
35397     getDragDropText : function(){
35398         var count = this.selModel.getCount();
35399         return String.format(this.ddText, count, count == 1 ? '' : 's');
35400     }
35401 });
35402 /**
35403  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
35404  * %0 is replaced with the number of selected rows.
35405  * @type String
35406  */
35407 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
35408  * Based on:
35409  * Ext JS Library 1.1.1
35410  * Copyright(c) 2006-2007, Ext JS, LLC.
35411  *
35412  * Originally Released Under LGPL - original licence link has changed is not relivant.
35413  *
35414  * Fork - LGPL
35415  * <script type="text/javascript">
35416  */
35417  
35418 Roo.grid.AbstractGridView = function(){
35419         this.grid = null;
35420         
35421         this.events = {
35422             "beforerowremoved" : true,
35423             "beforerowsinserted" : true,
35424             "beforerefresh" : true,
35425             "rowremoved" : true,
35426             "rowsinserted" : true,
35427             "rowupdated" : true,
35428             "refresh" : true
35429         };
35430     Roo.grid.AbstractGridView.superclass.constructor.call(this);
35431 };
35432
35433 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
35434     rowClass : "x-grid-row",
35435     cellClass : "x-grid-cell",
35436     tdClass : "x-grid-td",
35437     hdClass : "x-grid-hd",
35438     splitClass : "x-grid-hd-split",
35439     
35440         init: function(grid){
35441         this.grid = grid;
35442                 var cid = this.grid.getGridEl().id;
35443         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
35444         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
35445         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
35446         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
35447         },
35448         
35449         getColumnRenderers : function(){
35450         var renderers = [];
35451         var cm = this.grid.colModel;
35452         var colCount = cm.getColumnCount();
35453         for(var i = 0; i < colCount; i++){
35454             renderers[i] = cm.getRenderer(i);
35455         }
35456         return renderers;
35457     },
35458     
35459     getColumnIds : function(){
35460         var ids = [];
35461         var cm = this.grid.colModel;
35462         var colCount = cm.getColumnCount();
35463         for(var i = 0; i < colCount; i++){
35464             ids[i] = cm.getColumnId(i);
35465         }
35466         return ids;
35467     },
35468     
35469     getDataIndexes : function(){
35470         if(!this.indexMap){
35471             this.indexMap = this.buildIndexMap();
35472         }
35473         return this.indexMap.colToData;
35474     },
35475     
35476     getColumnIndexByDataIndex : function(dataIndex){
35477         if(!this.indexMap){
35478             this.indexMap = this.buildIndexMap();
35479         }
35480         return this.indexMap.dataToCol[dataIndex];
35481     },
35482     
35483     /**
35484      * Set a css style for a column dynamically. 
35485      * @param {Number} colIndex The index of the column
35486      * @param {String} name The css property name
35487      * @param {String} value The css value
35488      */
35489     setCSSStyle : function(colIndex, name, value){
35490         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
35491         Roo.util.CSS.updateRule(selector, name, value);
35492     },
35493     
35494     generateRules : function(cm){
35495         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
35496         Roo.util.CSS.removeStyleSheet(rulesId);
35497         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35498             var cid = cm.getColumnId(i);
35499             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
35500                          this.tdSelector, cid, " {\n}\n",
35501                          this.hdSelector, cid, " {\n}\n",
35502                          this.splitSelector, cid, " {\n}\n");
35503         }
35504         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35505     }
35506 });/*
35507  * Based on:
35508  * Ext JS Library 1.1.1
35509  * Copyright(c) 2006-2007, Ext JS, LLC.
35510  *
35511  * Originally Released Under LGPL - original licence link has changed is not relivant.
35512  *
35513  * Fork - LGPL
35514  * <script type="text/javascript">
35515  */
35516
35517 // private
35518 // This is a support class used internally by the Grid components
35519 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
35520     this.grid = grid;
35521     this.view = grid.getView();
35522     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
35523     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
35524     if(hd2){
35525         this.setHandleElId(Roo.id(hd));
35526         this.setOuterHandleElId(Roo.id(hd2));
35527     }
35528     this.scroll = false;
35529 };
35530 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
35531     maxDragWidth: 120,
35532     getDragData : function(e){
35533         var t = Roo.lib.Event.getTarget(e);
35534         var h = this.view.findHeaderCell(t);
35535         if(h){
35536             return {ddel: h.firstChild, header:h};
35537         }
35538         return false;
35539     },
35540
35541     onInitDrag : function(e){
35542         this.view.headersDisabled = true;
35543         var clone = this.dragData.ddel.cloneNode(true);
35544         clone.id = Roo.id();
35545         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
35546         this.proxy.update(clone);
35547         return true;
35548     },
35549
35550     afterValidDrop : function(){
35551         var v = this.view;
35552         setTimeout(function(){
35553             v.headersDisabled = false;
35554         }, 50);
35555     },
35556
35557     afterInvalidDrop : function(){
35558         var v = this.view;
35559         setTimeout(function(){
35560             v.headersDisabled = false;
35561         }, 50);
35562     }
35563 });
35564 /*
35565  * Based on:
35566  * Ext JS Library 1.1.1
35567  * Copyright(c) 2006-2007, Ext JS, LLC.
35568  *
35569  * Originally Released Under LGPL - original licence link has changed is not relivant.
35570  *
35571  * Fork - LGPL
35572  * <script type="text/javascript">
35573  */
35574 // private
35575 // This is a support class used internally by the Grid components
35576 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
35577     this.grid = grid;
35578     this.view = grid.getView();
35579     // split the proxies so they don't interfere with mouse events
35580     this.proxyTop = Roo.DomHelper.append(document.body, {
35581         cls:"col-move-top", html:"&#160;"
35582     }, true);
35583     this.proxyBottom = Roo.DomHelper.append(document.body, {
35584         cls:"col-move-bottom", html:"&#160;"
35585     }, true);
35586     this.proxyTop.hide = this.proxyBottom.hide = function(){
35587         this.setLeftTop(-100,-100);
35588         this.setStyle("visibility", "hidden");
35589     };
35590     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
35591     // temporarily disabled
35592     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
35593     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
35594 };
35595 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
35596     proxyOffsets : [-4, -9],
35597     fly: Roo.Element.fly,
35598
35599     getTargetFromEvent : function(e){
35600         var t = Roo.lib.Event.getTarget(e);
35601         var cindex = this.view.findCellIndex(t);
35602         if(cindex !== false){
35603             return this.view.getHeaderCell(cindex);
35604         }
35605         return null;
35606     },
35607
35608     nextVisible : function(h){
35609         var v = this.view, cm = this.grid.colModel;
35610         h = h.nextSibling;
35611         while(h){
35612             if(!cm.isHidden(v.getCellIndex(h))){
35613                 return h;
35614             }
35615             h = h.nextSibling;
35616         }
35617         return null;
35618     },
35619
35620     prevVisible : function(h){
35621         var v = this.view, cm = this.grid.colModel;
35622         h = h.prevSibling;
35623         while(h){
35624             if(!cm.isHidden(v.getCellIndex(h))){
35625                 return h;
35626             }
35627             h = h.prevSibling;
35628         }
35629         return null;
35630     },
35631
35632     positionIndicator : function(h, n, e){
35633         var x = Roo.lib.Event.getPageX(e);
35634         var r = Roo.lib.Dom.getRegion(n.firstChild);
35635         var px, pt, py = r.top + this.proxyOffsets[1];
35636         if((r.right - x) <= (r.right-r.left)/2){
35637             px = r.right+this.view.borderWidth;
35638             pt = "after";
35639         }else{
35640             px = r.left;
35641             pt = "before";
35642         }
35643         var oldIndex = this.view.getCellIndex(h);
35644         var newIndex = this.view.getCellIndex(n);
35645
35646         if(this.grid.colModel.isFixed(newIndex)){
35647             return false;
35648         }
35649
35650         var locked = this.grid.colModel.isLocked(newIndex);
35651
35652         if(pt == "after"){
35653             newIndex++;
35654         }
35655         if(oldIndex < newIndex){
35656             newIndex--;
35657         }
35658         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
35659             return false;
35660         }
35661         px +=  this.proxyOffsets[0];
35662         this.proxyTop.setLeftTop(px, py);
35663         this.proxyTop.show();
35664         if(!this.bottomOffset){
35665             this.bottomOffset = this.view.mainHd.getHeight();
35666         }
35667         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
35668         this.proxyBottom.show();
35669         return pt;
35670     },
35671
35672     onNodeEnter : function(n, dd, e, data){
35673         if(data.header != n){
35674             this.positionIndicator(data.header, n, e);
35675         }
35676     },
35677
35678     onNodeOver : function(n, dd, e, data){
35679         var result = false;
35680         if(data.header != n){
35681             result = this.positionIndicator(data.header, n, e);
35682         }
35683         if(!result){
35684             this.proxyTop.hide();
35685             this.proxyBottom.hide();
35686         }
35687         return result ? this.dropAllowed : this.dropNotAllowed;
35688     },
35689
35690     onNodeOut : function(n, dd, e, data){
35691         this.proxyTop.hide();
35692         this.proxyBottom.hide();
35693     },
35694
35695     onNodeDrop : function(n, dd, e, data){
35696         var h = data.header;
35697         if(h != n){
35698             var cm = this.grid.colModel;
35699             var x = Roo.lib.Event.getPageX(e);
35700             var r = Roo.lib.Dom.getRegion(n.firstChild);
35701             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
35702             var oldIndex = this.view.getCellIndex(h);
35703             var newIndex = this.view.getCellIndex(n);
35704             var locked = cm.isLocked(newIndex);
35705             if(pt == "after"){
35706                 newIndex++;
35707             }
35708             if(oldIndex < newIndex){
35709                 newIndex--;
35710             }
35711             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
35712                 return false;
35713             }
35714             cm.setLocked(oldIndex, locked, true);
35715             cm.moveColumn(oldIndex, newIndex);
35716             this.grid.fireEvent("columnmove", oldIndex, newIndex);
35717             return true;
35718         }
35719         return false;
35720     }
35721 });
35722 /*
35723  * Based on:
35724  * Ext JS Library 1.1.1
35725  * Copyright(c) 2006-2007, Ext JS, LLC.
35726  *
35727  * Originally Released Under LGPL - original licence link has changed is not relivant.
35728  *
35729  * Fork - LGPL
35730  * <script type="text/javascript">
35731  */
35732   
35733 /**
35734  * @class Roo.grid.GridView
35735  * @extends Roo.util.Observable
35736  *
35737  * @constructor
35738  * @param {Object} config
35739  */
35740 Roo.grid.GridView = function(config){
35741     Roo.grid.GridView.superclass.constructor.call(this);
35742     this.el = null;
35743
35744     Roo.apply(this, config);
35745 };
35746
35747 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
35748
35749     unselectable :  'unselectable="on"',
35750     unselectableCls :  'x-unselectable',
35751     
35752     
35753     rowClass : "x-grid-row",
35754
35755     cellClass : "x-grid-col",
35756
35757     tdClass : "x-grid-td",
35758
35759     hdClass : "x-grid-hd",
35760
35761     splitClass : "x-grid-split",
35762
35763     sortClasses : ["sort-asc", "sort-desc"],
35764
35765     enableMoveAnim : false,
35766
35767     hlColor: "C3DAF9",
35768
35769     dh : Roo.DomHelper,
35770
35771     fly : Roo.Element.fly,
35772
35773     css : Roo.util.CSS,
35774
35775     borderWidth: 1,
35776
35777     splitOffset: 3,
35778
35779     scrollIncrement : 22,
35780
35781     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
35782
35783     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
35784
35785     bind : function(ds, cm){
35786         if(this.ds){
35787             this.ds.un("load", this.onLoad, this);
35788             this.ds.un("datachanged", this.onDataChange, this);
35789             this.ds.un("add", this.onAdd, this);
35790             this.ds.un("remove", this.onRemove, this);
35791             this.ds.un("update", this.onUpdate, this);
35792             this.ds.un("clear", this.onClear, this);
35793         }
35794         if(ds){
35795             ds.on("load", this.onLoad, this);
35796             ds.on("datachanged", this.onDataChange, this);
35797             ds.on("add", this.onAdd, this);
35798             ds.on("remove", this.onRemove, this);
35799             ds.on("update", this.onUpdate, this);
35800             ds.on("clear", this.onClear, this);
35801         }
35802         this.ds = ds;
35803
35804         if(this.cm){
35805             this.cm.un("widthchange", this.onColWidthChange, this);
35806             this.cm.un("headerchange", this.onHeaderChange, this);
35807             this.cm.un("hiddenchange", this.onHiddenChange, this);
35808             this.cm.un("columnmoved", this.onColumnMove, this);
35809             this.cm.un("columnlockchange", this.onColumnLock, this);
35810         }
35811         if(cm){
35812             this.generateRules(cm);
35813             cm.on("widthchange", this.onColWidthChange, this);
35814             cm.on("headerchange", this.onHeaderChange, this);
35815             cm.on("hiddenchange", this.onHiddenChange, this);
35816             cm.on("columnmoved", this.onColumnMove, this);
35817             cm.on("columnlockchange", this.onColumnLock, this);
35818         }
35819         this.cm = cm;
35820     },
35821
35822     init: function(grid){
35823         Roo.grid.GridView.superclass.init.call(this, grid);
35824
35825         this.bind(grid.dataSource, grid.colModel);
35826
35827         grid.on("headerclick", this.handleHeaderClick, this);
35828
35829         if(grid.trackMouseOver){
35830             grid.on("mouseover", this.onRowOver, this);
35831             grid.on("mouseout", this.onRowOut, this);
35832         }
35833         grid.cancelTextSelection = function(){};
35834         this.gridId = grid.id;
35835
35836         var tpls = this.templates || {};
35837
35838         if(!tpls.master){
35839             tpls.master = new Roo.Template(
35840                '<div class="x-grid" hidefocus="true">',
35841                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
35842                   '<div class="x-grid-topbar"></div>',
35843                   '<div class="x-grid-scroller"><div></div></div>',
35844                   '<div class="x-grid-locked">',
35845                       '<div class="x-grid-header">{lockedHeader}</div>',
35846                       '<div class="x-grid-body">{lockedBody}</div>',
35847                   "</div>",
35848                   '<div class="x-grid-viewport">',
35849                       '<div class="x-grid-header">{header}</div>',
35850                       '<div class="x-grid-body">{body}</div>',
35851                   "</div>",
35852                   '<div class="x-grid-bottombar"></div>',
35853                  
35854                   '<div class="x-grid-resize-proxy">&#160;</div>',
35855                "</div>"
35856             );
35857             tpls.master.disableformats = true;
35858         }
35859
35860         if(!tpls.header){
35861             tpls.header = new Roo.Template(
35862                '<table border="0" cellspacing="0" cellpadding="0">',
35863                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
35864                "</table>{splits}"
35865             );
35866             tpls.header.disableformats = true;
35867         }
35868         tpls.header.compile();
35869
35870         if(!tpls.hcell){
35871             tpls.hcell = new Roo.Template(
35872                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
35873                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
35874                 "</div></td>"
35875              );
35876              tpls.hcell.disableFormats = true;
35877         }
35878         tpls.hcell.compile();
35879
35880         if(!tpls.hsplit){
35881             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
35882                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
35883             tpls.hsplit.disableFormats = true;
35884         }
35885         tpls.hsplit.compile();
35886
35887         if(!tpls.body){
35888             tpls.body = new Roo.Template(
35889                '<table border="0" cellspacing="0" cellpadding="0">',
35890                "<tbody>{rows}</tbody>",
35891                "</table>"
35892             );
35893             tpls.body.disableFormats = true;
35894         }
35895         tpls.body.compile();
35896
35897         if(!tpls.row){
35898             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
35899             tpls.row.disableFormats = true;
35900         }
35901         tpls.row.compile();
35902
35903         if(!tpls.cell){
35904             tpls.cell = new Roo.Template(
35905                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
35906                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
35907                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
35908                 "</td>"
35909             );
35910             tpls.cell.disableFormats = true;
35911         }
35912         tpls.cell.compile();
35913
35914         this.templates = tpls;
35915     },
35916
35917     // remap these for backwards compat
35918     onColWidthChange : function(){
35919         this.updateColumns.apply(this, arguments);
35920     },
35921     onHeaderChange : function(){
35922         this.updateHeaders.apply(this, arguments);
35923     }, 
35924     onHiddenChange : function(){
35925         this.handleHiddenChange.apply(this, arguments);
35926     },
35927     onColumnMove : function(){
35928         this.handleColumnMove.apply(this, arguments);
35929     },
35930     onColumnLock : function(){
35931         this.handleLockChange.apply(this, arguments);
35932     },
35933
35934     onDataChange : function(){
35935         this.refresh();
35936         this.updateHeaderSortState();
35937     },
35938
35939     onClear : function(){
35940         this.refresh();
35941     },
35942
35943     onUpdate : function(ds, record){
35944         this.refreshRow(record);
35945     },
35946
35947     refreshRow : function(record){
35948         var ds = this.ds, index;
35949         if(typeof record == 'number'){
35950             index = record;
35951             record = ds.getAt(index);
35952         }else{
35953             index = ds.indexOf(record);
35954         }
35955         this.insertRows(ds, index, index, true);
35956         this.onRemove(ds, record, index+1, true);
35957         this.syncRowHeights(index, index);
35958         this.layout();
35959         this.fireEvent("rowupdated", this, index, record);
35960     },
35961
35962     onAdd : function(ds, records, index){
35963         this.insertRows(ds, index, index + (records.length-1));
35964     },
35965
35966     onRemove : function(ds, record, index, isUpdate){
35967         if(isUpdate !== true){
35968             this.fireEvent("beforerowremoved", this, index, record);
35969         }
35970         var bt = this.getBodyTable(), lt = this.getLockedTable();
35971         if(bt.rows[index]){
35972             bt.firstChild.removeChild(bt.rows[index]);
35973         }
35974         if(lt.rows[index]){
35975             lt.firstChild.removeChild(lt.rows[index]);
35976         }
35977         if(isUpdate !== true){
35978             this.stripeRows(index);
35979             this.syncRowHeights(index, index);
35980             this.layout();
35981             this.fireEvent("rowremoved", this, index, record);
35982         }
35983     },
35984
35985     onLoad : function(){
35986         this.scrollToTop();
35987     },
35988
35989     /**
35990      * Scrolls the grid to the top
35991      */
35992     scrollToTop : function(){
35993         if(this.scroller){
35994             this.scroller.dom.scrollTop = 0;
35995             this.syncScroll();
35996         }
35997     },
35998
35999     /**
36000      * Gets a panel in the header of the grid that can be used for toolbars etc.
36001      * After modifying the contents of this panel a call to grid.autoSize() may be
36002      * required to register any changes in size.
36003      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36004      * @return Roo.Element
36005      */
36006     getHeaderPanel : function(doShow){
36007         if(doShow){
36008             this.headerPanel.show();
36009         }
36010         return this.headerPanel;
36011     },
36012
36013     /**
36014      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36015      * After modifying the contents of this panel a call to grid.autoSize() may be
36016      * required to register any changes in size.
36017      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36018      * @return Roo.Element
36019      */
36020     getFooterPanel : function(doShow){
36021         if(doShow){
36022             this.footerPanel.show();
36023         }
36024         return this.footerPanel;
36025     },
36026
36027     initElements : function(){
36028         var E = Roo.Element;
36029         var el = this.grid.getGridEl().dom.firstChild;
36030         var cs = el.childNodes;
36031
36032         this.el = new E(el);
36033         
36034          this.focusEl = new E(el.firstChild);
36035         this.focusEl.swallowEvent("click", true);
36036         
36037         this.headerPanel = new E(cs[1]);
36038         this.headerPanel.enableDisplayMode("block");
36039
36040         this.scroller = new E(cs[2]);
36041         this.scrollSizer = new E(this.scroller.dom.firstChild);
36042
36043         this.lockedWrap = new E(cs[3]);
36044         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36045         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36046
36047         this.mainWrap = new E(cs[4]);
36048         this.mainHd = new E(this.mainWrap.dom.firstChild);
36049         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36050
36051         this.footerPanel = new E(cs[5]);
36052         this.footerPanel.enableDisplayMode("block");
36053
36054         this.resizeProxy = new E(cs[6]);
36055
36056         this.headerSelector = String.format(
36057            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36058            this.lockedHd.id, this.mainHd.id
36059         );
36060
36061         this.splitterSelector = String.format(
36062            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36063            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36064         );
36065     },
36066     idToCssName : function(s)
36067     {
36068         return s.replace(/[^a-z0-9]+/ig, '-');
36069     },
36070
36071     getHeaderCell : function(index){
36072         return Roo.DomQuery.select(this.headerSelector)[index];
36073     },
36074
36075     getHeaderCellMeasure : function(index){
36076         return this.getHeaderCell(index).firstChild;
36077     },
36078
36079     getHeaderCellText : function(index){
36080         return this.getHeaderCell(index).firstChild.firstChild;
36081     },
36082
36083     getLockedTable : function(){
36084         return this.lockedBody.dom.firstChild;
36085     },
36086
36087     getBodyTable : function(){
36088         return this.mainBody.dom.firstChild;
36089     },
36090
36091     getLockedRow : function(index){
36092         return this.getLockedTable().rows[index];
36093     },
36094
36095     getRow : function(index){
36096         return this.getBodyTable().rows[index];
36097     },
36098
36099     getRowComposite : function(index){
36100         if(!this.rowEl){
36101             this.rowEl = new Roo.CompositeElementLite();
36102         }
36103         var els = [], lrow, mrow;
36104         if(lrow = this.getLockedRow(index)){
36105             els.push(lrow);
36106         }
36107         if(mrow = this.getRow(index)){
36108             els.push(mrow);
36109         }
36110         this.rowEl.elements = els;
36111         return this.rowEl;
36112     },
36113     /**
36114      * Gets the 'td' of the cell
36115      * 
36116      * @param {Integer} rowIndex row to select
36117      * @param {Integer} colIndex column to select
36118      * 
36119      * @return {Object} 
36120      */
36121     getCell : function(rowIndex, colIndex){
36122         var locked = this.cm.getLockedCount();
36123         var source;
36124         if(colIndex < locked){
36125             source = this.lockedBody.dom.firstChild;
36126         }else{
36127             source = this.mainBody.dom.firstChild;
36128             colIndex -= locked;
36129         }
36130         return source.rows[rowIndex].childNodes[colIndex];
36131     },
36132
36133     getCellText : function(rowIndex, colIndex){
36134         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36135     },
36136
36137     getCellBox : function(cell){
36138         var b = this.fly(cell).getBox();
36139         if(Roo.isOpera){ // opera fails to report the Y
36140             b.y = cell.offsetTop + this.mainBody.getY();
36141         }
36142         return b;
36143     },
36144
36145     getCellIndex : function(cell){
36146         var id = String(cell.className).match(this.cellRE);
36147         if(id){
36148             return parseInt(id[1], 10);
36149         }
36150         return 0;
36151     },
36152
36153     findHeaderIndex : function(n){
36154         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36155         return r ? this.getCellIndex(r) : false;
36156     },
36157
36158     findHeaderCell : function(n){
36159         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36160         return r ? r : false;
36161     },
36162
36163     findRowIndex : function(n){
36164         if(!n){
36165             return false;
36166         }
36167         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36168         return r ? r.rowIndex : false;
36169     },
36170
36171     findCellIndex : function(node){
36172         var stop = this.el.dom;
36173         while(node && node != stop){
36174             if(this.findRE.test(node.className)){
36175                 return this.getCellIndex(node);
36176             }
36177             node = node.parentNode;
36178         }
36179         return false;
36180     },
36181
36182     getColumnId : function(index){
36183         return this.cm.getColumnId(index);
36184     },
36185
36186     getSplitters : function()
36187     {
36188         if(this.splitterSelector){
36189            return Roo.DomQuery.select(this.splitterSelector);
36190         }else{
36191             return null;
36192       }
36193     },
36194
36195     getSplitter : function(index){
36196         return this.getSplitters()[index];
36197     },
36198
36199     onRowOver : function(e, t){
36200         var row;
36201         if((row = this.findRowIndex(t)) !== false){
36202             this.getRowComposite(row).addClass("x-grid-row-over");
36203         }
36204     },
36205
36206     onRowOut : function(e, t){
36207         var row;
36208         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36209             this.getRowComposite(row).removeClass("x-grid-row-over");
36210         }
36211     },
36212
36213     renderHeaders : function(){
36214         var cm = this.cm;
36215         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36216         var cb = [], lb = [], sb = [], lsb = [], p = {};
36217         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36218             p.cellId = "x-grid-hd-0-" + i;
36219             p.splitId = "x-grid-csplit-0-" + i;
36220             p.id = cm.getColumnId(i);
36221             p.title = cm.getColumnTooltip(i) || "";
36222             p.value = cm.getColumnHeader(i) || "";
36223             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36224             if(!cm.isLocked(i)){
36225                 cb[cb.length] = ct.apply(p);
36226                 sb[sb.length] = st.apply(p);
36227             }else{
36228                 lb[lb.length] = ct.apply(p);
36229                 lsb[lsb.length] = st.apply(p);
36230             }
36231         }
36232         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36233                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36234     },
36235
36236     updateHeaders : function(){
36237         var html = this.renderHeaders();
36238         this.lockedHd.update(html[0]);
36239         this.mainHd.update(html[1]);
36240     },
36241
36242     /**
36243      * Focuses the specified row.
36244      * @param {Number} row The row index
36245      */
36246     focusRow : function(row)
36247     {
36248         //Roo.log('GridView.focusRow');
36249         var x = this.scroller.dom.scrollLeft;
36250         this.focusCell(row, 0, false);
36251         this.scroller.dom.scrollLeft = x;
36252     },
36253
36254     /**
36255      * Focuses the specified cell.
36256      * @param {Number} row The row index
36257      * @param {Number} col The column index
36258      * @param {Boolean} hscroll false to disable horizontal scrolling
36259      */
36260     focusCell : function(row, col, hscroll)
36261     {
36262         //Roo.log('GridView.focusCell');
36263         var el = this.ensureVisible(row, col, hscroll);
36264         this.focusEl.alignTo(el, "tl-tl");
36265         if(Roo.isGecko){
36266             this.focusEl.focus();
36267         }else{
36268             this.focusEl.focus.defer(1, this.focusEl);
36269         }
36270     },
36271
36272     /**
36273      * Scrolls the specified cell into view
36274      * @param {Number} row The row index
36275      * @param {Number} col The column index
36276      * @param {Boolean} hscroll false to disable horizontal scrolling
36277      */
36278     ensureVisible : function(row, col, hscroll)
36279     {
36280         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36281         //return null; //disable for testing.
36282         if(typeof row != "number"){
36283             row = row.rowIndex;
36284         }
36285         if(row < 0 && row >= this.ds.getCount()){
36286             return  null;
36287         }
36288         col = (col !== undefined ? col : 0);
36289         var cm = this.grid.colModel;
36290         while(cm.isHidden(col)){
36291             col++;
36292         }
36293
36294         var el = this.getCell(row, col);
36295         if(!el){
36296             return null;
36297         }
36298         var c = this.scroller.dom;
36299
36300         var ctop = parseInt(el.offsetTop, 10);
36301         var cleft = parseInt(el.offsetLeft, 10);
36302         var cbot = ctop + el.offsetHeight;
36303         var cright = cleft + el.offsetWidth;
36304         
36305         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36306         var stop = parseInt(c.scrollTop, 10);
36307         var sleft = parseInt(c.scrollLeft, 10);
36308         var sbot = stop + ch;
36309         var sright = sleft + c.clientWidth;
36310         /*
36311         Roo.log('GridView.ensureVisible:' +
36312                 ' ctop:' + ctop +
36313                 ' c.clientHeight:' + c.clientHeight +
36314                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36315                 ' stop:' + stop +
36316                 ' cbot:' + cbot +
36317                 ' sbot:' + sbot +
36318                 ' ch:' + ch  
36319                 );
36320         */
36321         if(ctop < stop){
36322              c.scrollTop = ctop;
36323             //Roo.log("set scrolltop to ctop DISABLE?");
36324         }else if(cbot > sbot){
36325             //Roo.log("set scrolltop to cbot-ch");
36326             c.scrollTop = cbot-ch;
36327         }
36328         
36329         if(hscroll !== false){
36330             if(cleft < sleft){
36331                 c.scrollLeft = cleft;
36332             }else if(cright > sright){
36333                 c.scrollLeft = cright-c.clientWidth;
36334             }
36335         }
36336          
36337         return el;
36338     },
36339
36340     updateColumns : function(){
36341         this.grid.stopEditing();
36342         var cm = this.grid.colModel, colIds = this.getColumnIds();
36343         //var totalWidth = cm.getTotalWidth();
36344         var pos = 0;
36345         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36346             //if(cm.isHidden(i)) continue;
36347             var w = cm.getColumnWidth(i);
36348             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36349             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36350         }
36351         this.updateSplitters();
36352     },
36353
36354     generateRules : function(cm){
36355         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
36356         Roo.util.CSS.removeStyleSheet(rulesId);
36357         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36358             var cid = cm.getColumnId(i);
36359             var align = '';
36360             if(cm.config[i].align){
36361                 align = 'text-align:'+cm.config[i].align+';';
36362             }
36363             var hidden = '';
36364             if(cm.isHidden(i)){
36365                 hidden = 'display:none;';
36366             }
36367             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
36368             ruleBuf.push(
36369                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
36370                     this.hdSelector, cid, " {\n", align, width, "}\n",
36371                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
36372                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
36373         }
36374         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36375     },
36376
36377     updateSplitters : function(){
36378         var cm = this.cm, s = this.getSplitters();
36379         if(s){ // splitters not created yet
36380             var pos = 0, locked = true;
36381             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36382                 if(cm.isHidden(i)) continue;
36383                 var w = cm.getColumnWidth(i); // make sure it's a number
36384                 if(!cm.isLocked(i) && locked){
36385                     pos = 0;
36386                     locked = false;
36387                 }
36388                 pos += w;
36389                 s[i].style.left = (pos-this.splitOffset) + "px";
36390             }
36391         }
36392     },
36393
36394     handleHiddenChange : function(colModel, colIndex, hidden){
36395         if(hidden){
36396             this.hideColumn(colIndex);
36397         }else{
36398             this.unhideColumn(colIndex);
36399         }
36400     },
36401
36402     hideColumn : function(colIndex){
36403         var cid = this.getColumnId(colIndex);
36404         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
36405         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
36406         if(Roo.isSafari){
36407             this.updateHeaders();
36408         }
36409         this.updateSplitters();
36410         this.layout();
36411     },
36412
36413     unhideColumn : function(colIndex){
36414         var cid = this.getColumnId(colIndex);
36415         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
36416         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
36417
36418         if(Roo.isSafari){
36419             this.updateHeaders();
36420         }
36421         this.updateSplitters();
36422         this.layout();
36423     },
36424
36425     insertRows : function(dm, firstRow, lastRow, isUpdate){
36426         if(firstRow == 0 && lastRow == dm.getCount()-1){
36427             this.refresh();
36428         }else{
36429             if(!isUpdate){
36430                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
36431             }
36432             var s = this.getScrollState();
36433             var markup = this.renderRows(firstRow, lastRow);
36434             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
36435             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
36436             this.restoreScroll(s);
36437             if(!isUpdate){
36438                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
36439                 this.syncRowHeights(firstRow, lastRow);
36440                 this.stripeRows(firstRow);
36441                 this.layout();
36442             }
36443         }
36444     },
36445
36446     bufferRows : function(markup, target, index){
36447         var before = null, trows = target.rows, tbody = target.tBodies[0];
36448         if(index < trows.length){
36449             before = trows[index];
36450         }
36451         var b = document.createElement("div");
36452         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
36453         var rows = b.firstChild.rows;
36454         for(var i = 0, len = rows.length; i < len; i++){
36455             if(before){
36456                 tbody.insertBefore(rows[0], before);
36457             }else{
36458                 tbody.appendChild(rows[0]);
36459             }
36460         }
36461         b.innerHTML = "";
36462         b = null;
36463     },
36464
36465     deleteRows : function(dm, firstRow, lastRow){
36466         if(dm.getRowCount()<1){
36467             this.fireEvent("beforerefresh", this);
36468             this.mainBody.update("");
36469             this.lockedBody.update("");
36470             this.fireEvent("refresh", this);
36471         }else{
36472             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
36473             var bt = this.getBodyTable();
36474             var tbody = bt.firstChild;
36475             var rows = bt.rows;
36476             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
36477                 tbody.removeChild(rows[firstRow]);
36478             }
36479             this.stripeRows(firstRow);
36480             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
36481         }
36482     },
36483
36484     updateRows : function(dataSource, firstRow, lastRow){
36485         var s = this.getScrollState();
36486         this.refresh();
36487         this.restoreScroll(s);
36488     },
36489
36490     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
36491         if(!noRefresh){
36492            this.refresh();
36493         }
36494         this.updateHeaderSortState();
36495     },
36496
36497     getScrollState : function(){
36498         
36499         var sb = this.scroller.dom;
36500         return {left: sb.scrollLeft, top: sb.scrollTop};
36501     },
36502
36503     stripeRows : function(startRow){
36504         if(!this.grid.stripeRows || this.ds.getCount() < 1){
36505             return;
36506         }
36507         startRow = startRow || 0;
36508         var rows = this.getBodyTable().rows;
36509         var lrows = this.getLockedTable().rows;
36510         var cls = ' x-grid-row-alt ';
36511         for(var i = startRow, len = rows.length; i < len; i++){
36512             var row = rows[i], lrow = lrows[i];
36513             var isAlt = ((i+1) % 2 == 0);
36514             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
36515             if(isAlt == hasAlt){
36516                 continue;
36517             }
36518             if(isAlt){
36519                 row.className += " x-grid-row-alt";
36520             }else{
36521                 row.className = row.className.replace("x-grid-row-alt", "");
36522             }
36523             if(lrow){
36524                 lrow.className = row.className;
36525             }
36526         }
36527     },
36528
36529     restoreScroll : function(state){
36530         //Roo.log('GridView.restoreScroll');
36531         var sb = this.scroller.dom;
36532         sb.scrollLeft = state.left;
36533         sb.scrollTop = state.top;
36534         this.syncScroll();
36535     },
36536
36537     syncScroll : function(){
36538         //Roo.log('GridView.syncScroll');
36539         var sb = this.scroller.dom;
36540         var sh = this.mainHd.dom;
36541         var bs = this.mainBody.dom;
36542         var lv = this.lockedBody.dom;
36543         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
36544         lv.scrollTop = bs.scrollTop = sb.scrollTop;
36545     },
36546
36547     handleScroll : function(e){
36548         this.syncScroll();
36549         var sb = this.scroller.dom;
36550         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
36551         e.stopEvent();
36552     },
36553
36554     handleWheel : function(e){
36555         var d = e.getWheelDelta();
36556         this.scroller.dom.scrollTop -= d*22;
36557         // set this here to prevent jumpy scrolling on large tables
36558         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
36559         e.stopEvent();
36560     },
36561
36562     renderRows : function(startRow, endRow){
36563         // pull in all the crap needed to render rows
36564         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
36565         var colCount = cm.getColumnCount();
36566
36567         if(ds.getCount() < 1){
36568             return ["", ""];
36569         }
36570
36571         // build a map for all the columns
36572         var cs = [];
36573         for(var i = 0; i < colCount; i++){
36574             var name = cm.getDataIndex(i);
36575             cs[i] = {
36576                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
36577                 renderer : cm.getRenderer(i),
36578                 id : cm.getColumnId(i),
36579                 locked : cm.isLocked(i)
36580             };
36581         }
36582
36583         startRow = startRow || 0;
36584         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
36585
36586         // records to render
36587         var rs = ds.getRange(startRow, endRow);
36588
36589         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
36590     },
36591
36592     // As much as I hate to duplicate code, this was branched because FireFox really hates
36593     // [].join("") on strings. The performance difference was substantial enough to
36594     // branch this function
36595     doRender : Roo.isGecko ?
36596             function(cs, rs, ds, startRow, colCount, stripe){
36597                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36598                 // buffers
36599                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36600                 
36601                 var hasListener = this.grid.hasListener('rowclass');
36602                 var rowcfg = {};
36603                 for(var j = 0, len = rs.length; j < len; j++){
36604                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
36605                     for(var i = 0; i < colCount; i++){
36606                         c = cs[i];
36607                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36608                         p.id = c.id;
36609                         p.css = p.attr = "";
36610                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36611                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36612                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36613                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36614                         }
36615                         var markup = ct.apply(p);
36616                         if(!c.locked){
36617                             cb+= markup;
36618                         }else{
36619                             lcb+= markup;
36620                         }
36621                     }
36622                     var alt = [];
36623                     if(stripe && ((rowIndex+1) % 2 == 0)){
36624                         alt.push("x-grid-row-alt")
36625                     }
36626                     if(r.dirty){
36627                         alt.push(  " x-grid-dirty-row");
36628                     }
36629                     rp.cells = lcb;
36630                     if(this.getRowClass){
36631                         alt.push(this.getRowClass(r, rowIndex));
36632                     }
36633                     if (hasListener) {
36634                         rowcfg = {
36635                              
36636                             record: r,
36637                             rowIndex : rowIndex,
36638                             rowClass : ''
36639                         }
36640                         this.grid.fireEvent('rowclass', this, rowcfg);
36641                         alt.push(rowcfg.rowClass);
36642                     }
36643                     rp.alt = alt.join(" ");
36644                     lbuf+= rt.apply(rp);
36645                     rp.cells = cb;
36646                     buf+=  rt.apply(rp);
36647                 }
36648                 return [lbuf, buf];
36649             } :
36650             function(cs, rs, ds, startRow, colCount, stripe){
36651                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36652                 // buffers
36653                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36654                 var hasListener = this.grid.hasListener('rowclass');
36655  
36656                 var rowcfg = {};
36657                 for(var j = 0, len = rs.length; j < len; j++){
36658                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
36659                     for(var i = 0; i < colCount; i++){
36660                         c = cs[i];
36661                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36662                         p.id = c.id;
36663                         p.css = p.attr = "";
36664                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36665                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36666                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36667                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36668                         }
36669                         
36670                         var markup = ct.apply(p);
36671                         if(!c.locked){
36672                             cb[cb.length] = markup;
36673                         }else{
36674                             lcb[lcb.length] = markup;
36675                         }
36676                     }
36677                     var alt = [];
36678                     if(stripe && ((rowIndex+1) % 2 == 0)){
36679                         alt.push( "x-grid-row-alt");
36680                     }
36681                     if(r.dirty){
36682                         alt.push(" x-grid-dirty-row");
36683                     }
36684                     rp.cells = lcb;
36685                     if(this.getRowClass){
36686                         alt.push( this.getRowClass(r, rowIndex));
36687                     }
36688                     if (hasListener) {
36689                         rowcfg = {
36690                              
36691                             record: r,
36692                             rowIndex : rowIndex,
36693                             rowClass : ''
36694                         }
36695                         this.grid.fireEvent('rowclass', this, rowcfg);
36696                         alt.push(rowcfg.rowClass);
36697                     }
36698                     rp.alt = alt.join(" ");
36699                     rp.cells = lcb.join("");
36700                     lbuf[lbuf.length] = rt.apply(rp);
36701                     rp.cells = cb.join("");
36702                     buf[buf.length] =  rt.apply(rp);
36703                 }
36704                 return [lbuf.join(""), buf.join("")];
36705             },
36706
36707     renderBody : function(){
36708         var markup = this.renderRows();
36709         var bt = this.templates.body;
36710         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
36711     },
36712
36713     /**
36714      * Refreshes the grid
36715      * @param {Boolean} headersToo
36716      */
36717     refresh : function(headersToo){
36718         this.fireEvent("beforerefresh", this);
36719         this.grid.stopEditing();
36720         var result = this.renderBody();
36721         this.lockedBody.update(result[0]);
36722         this.mainBody.update(result[1]);
36723         if(headersToo === true){
36724             this.updateHeaders();
36725             this.updateColumns();
36726             this.updateSplitters();
36727             this.updateHeaderSortState();
36728         }
36729         this.syncRowHeights();
36730         this.layout();
36731         this.fireEvent("refresh", this);
36732     },
36733
36734     handleColumnMove : function(cm, oldIndex, newIndex){
36735         this.indexMap = null;
36736         var s = this.getScrollState();
36737         this.refresh(true);
36738         this.restoreScroll(s);
36739         this.afterMove(newIndex);
36740     },
36741
36742     afterMove : function(colIndex){
36743         if(this.enableMoveAnim && Roo.enableFx){
36744             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
36745         }
36746         // if multisort - fix sortOrder, and reload..
36747         if (this.grid.dataSource.multiSort) {
36748             // the we can call sort again..
36749             var dm = this.grid.dataSource;
36750             var cm = this.grid.colModel;
36751             var so = [];
36752             for(var i = 0; i < cm.config.length; i++ ) {
36753                 
36754                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
36755                     continue; // dont' bother, it's not in sort list or being set.
36756                 }
36757                 
36758                 so.push(cm.config[i].dataIndex);
36759             };
36760             dm.sortOrder = so;
36761             dm.load(dm.lastOptions);
36762             
36763             
36764         }
36765         
36766     },
36767
36768     updateCell : function(dm, rowIndex, dataIndex){
36769         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
36770         if(typeof colIndex == "undefined"){ // not present in grid
36771             return;
36772         }
36773         var cm = this.grid.colModel;
36774         var cell = this.getCell(rowIndex, colIndex);
36775         var cellText = this.getCellText(rowIndex, colIndex);
36776
36777         var p = {
36778             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
36779             id : cm.getColumnId(colIndex),
36780             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
36781         };
36782         var renderer = cm.getRenderer(colIndex);
36783         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
36784         if(typeof val == "undefined" || val === "") val = "&#160;";
36785         cellText.innerHTML = val;
36786         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
36787         this.syncRowHeights(rowIndex, rowIndex);
36788     },
36789
36790     calcColumnWidth : function(colIndex, maxRowsToMeasure){
36791         var maxWidth = 0;
36792         if(this.grid.autoSizeHeaders){
36793             var h = this.getHeaderCellMeasure(colIndex);
36794             maxWidth = Math.max(maxWidth, h.scrollWidth);
36795         }
36796         var tb, index;
36797         if(this.cm.isLocked(colIndex)){
36798             tb = this.getLockedTable();
36799             index = colIndex;
36800         }else{
36801             tb = this.getBodyTable();
36802             index = colIndex - this.cm.getLockedCount();
36803         }
36804         if(tb && tb.rows){
36805             var rows = tb.rows;
36806             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
36807             for(var i = 0; i < stopIndex; i++){
36808                 var cell = rows[i].childNodes[index].firstChild;
36809                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
36810             }
36811         }
36812         return maxWidth + /*margin for error in IE*/ 5;
36813     },
36814     /**
36815      * Autofit a column to its content.
36816      * @param {Number} colIndex
36817      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
36818      */
36819      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
36820          if(this.cm.isHidden(colIndex)){
36821              return; // can't calc a hidden column
36822          }
36823         if(forceMinSize){
36824             var cid = this.cm.getColumnId(colIndex);
36825             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
36826            if(this.grid.autoSizeHeaders){
36827                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
36828            }
36829         }
36830         var newWidth = this.calcColumnWidth(colIndex);
36831         this.cm.setColumnWidth(colIndex,
36832             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
36833         if(!suppressEvent){
36834             this.grid.fireEvent("columnresize", colIndex, newWidth);
36835         }
36836     },
36837
36838     /**
36839      * Autofits all columns to their content and then expands to fit any extra space in the grid
36840      */
36841      autoSizeColumns : function(){
36842         var cm = this.grid.colModel;
36843         var colCount = cm.getColumnCount();
36844         for(var i = 0; i < colCount; i++){
36845             this.autoSizeColumn(i, true, true);
36846         }
36847         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
36848             this.fitColumns();
36849         }else{
36850             this.updateColumns();
36851             this.layout();
36852         }
36853     },
36854
36855     /**
36856      * Autofits all columns to the grid's width proportionate with their current size
36857      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
36858      */
36859     fitColumns : function(reserveScrollSpace){
36860         var cm = this.grid.colModel;
36861         var colCount = cm.getColumnCount();
36862         var cols = [];
36863         var width = 0;
36864         var i, w;
36865         for (i = 0; i < colCount; i++){
36866             if(!cm.isHidden(i) && !cm.isFixed(i)){
36867                 w = cm.getColumnWidth(i);
36868                 cols.push(i);
36869                 cols.push(w);
36870                 width += w;
36871             }
36872         }
36873         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
36874         if(reserveScrollSpace){
36875             avail -= 17;
36876         }
36877         var frac = (avail - cm.getTotalWidth())/width;
36878         while (cols.length){
36879             w = cols.pop();
36880             i = cols.pop();
36881             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
36882         }
36883         this.updateColumns();
36884         this.layout();
36885     },
36886
36887     onRowSelect : function(rowIndex){
36888         var row = this.getRowComposite(rowIndex);
36889         row.addClass("x-grid-row-selected");
36890     },
36891
36892     onRowDeselect : function(rowIndex){
36893         var row = this.getRowComposite(rowIndex);
36894         row.removeClass("x-grid-row-selected");
36895     },
36896
36897     onCellSelect : function(row, col){
36898         var cell = this.getCell(row, col);
36899         if(cell){
36900             Roo.fly(cell).addClass("x-grid-cell-selected");
36901         }
36902     },
36903
36904     onCellDeselect : function(row, col){
36905         var cell = this.getCell(row, col);
36906         if(cell){
36907             Roo.fly(cell).removeClass("x-grid-cell-selected");
36908         }
36909     },
36910
36911     updateHeaderSortState : function(){
36912         
36913         // sort state can be single { field: xxx, direction : yyy}
36914         // or   { xxx=>ASC , yyy : DESC ..... }
36915         
36916         var mstate = {};
36917         if (!this.ds.multiSort) { 
36918             var state = this.ds.getSortState();
36919             if(!state){
36920                 return;
36921             }
36922             mstate[state.field] = state.direction;
36923             // FIXME... - this is not used here.. but might be elsewhere..
36924             this.sortState = state;
36925             
36926         } else {
36927             mstate = this.ds.sortToggle;
36928         }
36929         //remove existing sort classes..
36930         
36931         var sc = this.sortClasses;
36932         var hds = this.el.select(this.headerSelector).removeClass(sc);
36933         
36934         for(var f in mstate) {
36935         
36936             var sortColumn = this.cm.findColumnIndex(f);
36937             
36938             if(sortColumn != -1){
36939                 var sortDir = mstate[f];        
36940                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36941             }
36942         }
36943         
36944          
36945         
36946     },
36947
36948
36949     handleHeaderClick : function(g, index){
36950         if(this.headersDisabled){
36951             return;
36952         }
36953         var dm = g.dataSource, cm = g.colModel;
36954         if(!cm.isSortable(index)){
36955             return;
36956         }
36957         g.stopEditing();
36958         
36959         if (dm.multiSort) {
36960             // update the sortOrder
36961             var so = [];
36962             for(var i = 0; i < cm.config.length; i++ ) {
36963                 
36964                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36965                     continue; // dont' bother, it's not in sort list or being set.
36966                 }
36967                 
36968                 so.push(cm.config[i].dataIndex);
36969             };
36970             dm.sortOrder = so;
36971         }
36972         
36973         
36974         dm.sort(cm.getDataIndex(index));
36975     },
36976
36977
36978     destroy : function(){
36979         if(this.colMenu){
36980             this.colMenu.removeAll();
36981             Roo.menu.MenuMgr.unregister(this.colMenu);
36982             this.colMenu.getEl().remove();
36983             delete this.colMenu;
36984         }
36985         if(this.hmenu){
36986             this.hmenu.removeAll();
36987             Roo.menu.MenuMgr.unregister(this.hmenu);
36988             this.hmenu.getEl().remove();
36989             delete this.hmenu;
36990         }
36991         if(this.grid.enableColumnMove){
36992             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36993             if(dds){
36994                 for(var dd in dds){
36995                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36996                         var elid = dds[dd].dragElId;
36997                         dds[dd].unreg();
36998                         Roo.get(elid).remove();
36999                     } else if(dds[dd].config.isTarget){
37000                         dds[dd].proxyTop.remove();
37001                         dds[dd].proxyBottom.remove();
37002                         dds[dd].unreg();
37003                     }
37004                     if(Roo.dd.DDM.locationCache[dd]){
37005                         delete Roo.dd.DDM.locationCache[dd];
37006                     }
37007                 }
37008                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37009             }
37010         }
37011         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37012         this.bind(null, null);
37013         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37014     },
37015
37016     handleLockChange : function(){
37017         this.refresh(true);
37018     },
37019
37020     onDenyColumnLock : function(){
37021
37022     },
37023
37024     onDenyColumnHide : function(){
37025
37026     },
37027
37028     handleHdMenuClick : function(item){
37029         var index = this.hdCtxIndex;
37030         var cm = this.cm, ds = this.ds;
37031         switch(item.id){
37032             case "asc":
37033                 ds.sort(cm.getDataIndex(index), "ASC");
37034                 break;
37035             case "desc":
37036                 ds.sort(cm.getDataIndex(index), "DESC");
37037                 break;
37038             case "lock":
37039                 var lc = cm.getLockedCount();
37040                 if(cm.getColumnCount(true) <= lc+1){
37041                     this.onDenyColumnLock();
37042                     return;
37043                 }
37044                 if(lc != index){
37045                     cm.setLocked(index, true, true);
37046                     cm.moveColumn(index, lc);
37047                     this.grid.fireEvent("columnmove", index, lc);
37048                 }else{
37049                     cm.setLocked(index, true);
37050                 }
37051             break;
37052             case "unlock":
37053                 var lc = cm.getLockedCount();
37054                 if((lc-1) != index){
37055                     cm.setLocked(index, false, true);
37056                     cm.moveColumn(index, lc-1);
37057                     this.grid.fireEvent("columnmove", index, lc-1);
37058                 }else{
37059                     cm.setLocked(index, false);
37060                 }
37061             break;
37062             default:
37063                 index = cm.getIndexById(item.id.substr(4));
37064                 if(index != -1){
37065                     if(item.checked && cm.getColumnCount(true) <= 1){
37066                         this.onDenyColumnHide();
37067                         return false;
37068                     }
37069                     cm.setHidden(index, item.checked);
37070                 }
37071         }
37072         return true;
37073     },
37074
37075     beforeColMenuShow : function(){
37076         var cm = this.cm,  colCount = cm.getColumnCount();
37077         this.colMenu.removeAll();
37078         for(var i = 0; i < colCount; i++){
37079             this.colMenu.add(new Roo.menu.CheckItem({
37080                 id: "col-"+cm.getColumnId(i),
37081                 text: cm.getColumnHeader(i),
37082                 checked: !cm.isHidden(i),
37083                 hideOnClick:false
37084             }));
37085         }
37086     },
37087
37088     handleHdCtx : function(g, index, e){
37089         e.stopEvent();
37090         var hd = this.getHeaderCell(index);
37091         this.hdCtxIndex = index;
37092         var ms = this.hmenu.items, cm = this.cm;
37093         ms.get("asc").setDisabled(!cm.isSortable(index));
37094         ms.get("desc").setDisabled(!cm.isSortable(index));
37095         if(this.grid.enableColLock !== false){
37096             ms.get("lock").setDisabled(cm.isLocked(index));
37097             ms.get("unlock").setDisabled(!cm.isLocked(index));
37098         }
37099         this.hmenu.show(hd, "tl-bl");
37100     },
37101
37102     handleHdOver : function(e){
37103         var hd = this.findHeaderCell(e.getTarget());
37104         if(hd && !this.headersDisabled){
37105             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37106                this.fly(hd).addClass("x-grid-hd-over");
37107             }
37108         }
37109     },
37110
37111     handleHdOut : function(e){
37112         var hd = this.findHeaderCell(e.getTarget());
37113         if(hd){
37114             this.fly(hd).removeClass("x-grid-hd-over");
37115         }
37116     },
37117
37118     handleSplitDblClick : function(e, t){
37119         var i = this.getCellIndex(t);
37120         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37121             this.autoSizeColumn(i, true);
37122             this.layout();
37123         }
37124     },
37125
37126     render : function(){
37127
37128         var cm = this.cm;
37129         var colCount = cm.getColumnCount();
37130
37131         if(this.grid.monitorWindowResize === true){
37132             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37133         }
37134         var header = this.renderHeaders();
37135         var body = this.templates.body.apply({rows:""});
37136         var html = this.templates.master.apply({
37137             lockedBody: body,
37138             body: body,
37139             lockedHeader: header[0],
37140             header: header[1]
37141         });
37142
37143         //this.updateColumns();
37144
37145         this.grid.getGridEl().dom.innerHTML = html;
37146
37147         this.initElements();
37148         
37149         // a kludge to fix the random scolling effect in webkit
37150         this.el.on("scroll", function() {
37151             this.el.dom.scrollTop=0; // hopefully not recursive..
37152         },this);
37153
37154         this.scroller.on("scroll", this.handleScroll, this);
37155         this.lockedBody.on("mousewheel", this.handleWheel, this);
37156         this.mainBody.on("mousewheel", this.handleWheel, this);
37157
37158         this.mainHd.on("mouseover", this.handleHdOver, this);
37159         this.mainHd.on("mouseout", this.handleHdOut, this);
37160         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37161                 {delegate: "."+this.splitClass});
37162
37163         this.lockedHd.on("mouseover", this.handleHdOver, this);
37164         this.lockedHd.on("mouseout", this.handleHdOut, this);
37165         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37166                 {delegate: "."+this.splitClass});
37167
37168         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37169             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37170         }
37171
37172         this.updateSplitters();
37173
37174         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37175             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37176             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37177         }
37178
37179         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37180             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37181             this.hmenu.add(
37182                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37183                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37184             );
37185             if(this.grid.enableColLock !== false){
37186                 this.hmenu.add('-',
37187                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37188                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37189                 );
37190             }
37191             if(this.grid.enableColumnHide !== false){
37192
37193                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37194                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37195                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37196
37197                 this.hmenu.add('-',
37198                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37199                 );
37200             }
37201             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37202
37203             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37204         }
37205
37206         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37207             this.dd = new Roo.grid.GridDragZone(this.grid, {
37208                 ddGroup : this.grid.ddGroup || 'GridDD'
37209             });
37210             
37211         }
37212
37213         /*
37214         for(var i = 0; i < colCount; i++){
37215             if(cm.isHidden(i)){
37216                 this.hideColumn(i);
37217             }
37218             if(cm.config[i].align){
37219                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37220                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37221             }
37222         }*/
37223         
37224         this.updateHeaderSortState();
37225
37226         this.beforeInitialResize();
37227         this.layout(true);
37228
37229         // two part rendering gives faster view to the user
37230         this.renderPhase2.defer(1, this);
37231     },
37232
37233     renderPhase2 : function(){
37234         // render the rows now
37235         this.refresh();
37236         if(this.grid.autoSizeColumns){
37237             this.autoSizeColumns();
37238         }
37239     },
37240
37241     beforeInitialResize : function(){
37242
37243     },
37244
37245     onColumnSplitterMoved : function(i, w){
37246         this.userResized = true;
37247         var cm = this.grid.colModel;
37248         cm.setColumnWidth(i, w, true);
37249         var cid = cm.getColumnId(i);
37250         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37251         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37252         this.updateSplitters();
37253         this.layout();
37254         this.grid.fireEvent("columnresize", i, w);
37255     },
37256
37257     syncRowHeights : function(startIndex, endIndex){
37258         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37259             startIndex = startIndex || 0;
37260             var mrows = this.getBodyTable().rows;
37261             var lrows = this.getLockedTable().rows;
37262             var len = mrows.length-1;
37263             endIndex = Math.min(endIndex || len, len);
37264             for(var i = startIndex; i <= endIndex; i++){
37265                 var m = mrows[i], l = lrows[i];
37266                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37267                 m.style.height = l.style.height = h + "px";
37268             }
37269         }
37270     },
37271
37272     layout : function(initialRender, is2ndPass){
37273         var g = this.grid;
37274         var auto = g.autoHeight;
37275         var scrollOffset = 16;
37276         var c = g.getGridEl(), cm = this.cm,
37277                 expandCol = g.autoExpandColumn,
37278                 gv = this;
37279         //c.beginMeasure();
37280
37281         if(!c.dom.offsetWidth){ // display:none?
37282             if(initialRender){
37283                 this.lockedWrap.show();
37284                 this.mainWrap.show();
37285             }
37286             return;
37287         }
37288
37289         var hasLock = this.cm.isLocked(0);
37290
37291         var tbh = this.headerPanel.getHeight();
37292         var bbh = this.footerPanel.getHeight();
37293
37294         if(auto){
37295             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37296             var newHeight = ch + c.getBorderWidth("tb");
37297             if(g.maxHeight){
37298                 newHeight = Math.min(g.maxHeight, newHeight);
37299             }
37300             c.setHeight(newHeight);
37301         }
37302
37303         if(g.autoWidth){
37304             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37305         }
37306
37307         var s = this.scroller;
37308
37309         var csize = c.getSize(true);
37310
37311         this.el.setSize(csize.width, csize.height);
37312
37313         this.headerPanel.setWidth(csize.width);
37314         this.footerPanel.setWidth(csize.width);
37315
37316         var hdHeight = this.mainHd.getHeight();
37317         var vw = csize.width;
37318         var vh = csize.height - (tbh + bbh);
37319
37320         s.setSize(vw, vh);
37321
37322         var bt = this.getBodyTable();
37323         var ltWidth = hasLock ?
37324                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37325
37326         var scrollHeight = bt.offsetHeight;
37327         var scrollWidth = ltWidth + bt.offsetWidth;
37328         var vscroll = false, hscroll = false;
37329
37330         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37331
37332         var lw = this.lockedWrap, mw = this.mainWrap;
37333         var lb = this.lockedBody, mb = this.mainBody;
37334
37335         setTimeout(function(){
37336             var t = s.dom.offsetTop;
37337             var w = s.dom.clientWidth,
37338                 h = s.dom.clientHeight;
37339
37340             lw.setTop(t);
37341             lw.setSize(ltWidth, h);
37342
37343             mw.setLeftTop(ltWidth, t);
37344             mw.setSize(w-ltWidth, h);
37345
37346             lb.setHeight(h-hdHeight);
37347             mb.setHeight(h-hdHeight);
37348
37349             if(is2ndPass !== true && !gv.userResized && expandCol){
37350                 // high speed resize without full column calculation
37351                 
37352                 var ci = cm.getIndexById(expandCol);
37353                 if (ci < 0) {
37354                     ci = cm.findColumnIndex(expandCol);
37355                 }
37356                 ci = Math.max(0, ci); // make sure it's got at least the first col.
37357                 var expandId = cm.getColumnId(ci);
37358                 var  tw = cm.getTotalWidth(false);
37359                 var currentWidth = cm.getColumnWidth(ci);
37360                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
37361                 if(currentWidth != cw){
37362                     cm.setColumnWidth(ci, cw, true);
37363                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
37364                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
37365                     gv.updateSplitters();
37366                     gv.layout(false, true);
37367                 }
37368             }
37369
37370             if(initialRender){
37371                 lw.show();
37372                 mw.show();
37373             }
37374             //c.endMeasure();
37375         }, 10);
37376     },
37377
37378     onWindowResize : function(){
37379         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
37380             return;
37381         }
37382         this.layout();
37383     },
37384
37385     appendFooter : function(parentEl){
37386         return null;
37387     },
37388
37389     sortAscText : "Sort Ascending",
37390     sortDescText : "Sort Descending",
37391     lockText : "Lock Column",
37392     unlockText : "Unlock Column",
37393     columnsText : "Columns"
37394 });
37395
37396
37397 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
37398     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
37399     this.proxy.el.addClass('x-grid3-col-dd');
37400 };
37401
37402 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
37403     handleMouseDown : function(e){
37404
37405     },
37406
37407     callHandleMouseDown : function(e){
37408         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
37409     }
37410 });
37411 /*
37412  * Based on:
37413  * Ext JS Library 1.1.1
37414  * Copyright(c) 2006-2007, Ext JS, LLC.
37415  *
37416  * Originally Released Under LGPL - original licence link has changed is not relivant.
37417  *
37418  * Fork - LGPL
37419  * <script type="text/javascript">
37420  */
37421  
37422 // private
37423 // This is a support class used internally by the Grid components
37424 Roo.grid.SplitDragZone = function(grid, hd, hd2){
37425     this.grid = grid;
37426     this.view = grid.getView();
37427     this.proxy = this.view.resizeProxy;
37428     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
37429         "gridSplitters" + this.grid.getGridEl().id, {
37430         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
37431     });
37432     this.setHandleElId(Roo.id(hd));
37433     this.setOuterHandleElId(Roo.id(hd2));
37434     this.scroll = false;
37435 };
37436 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
37437     fly: Roo.Element.fly,
37438
37439     b4StartDrag : function(x, y){
37440         this.view.headersDisabled = true;
37441         this.proxy.setHeight(this.view.mainWrap.getHeight());
37442         var w = this.cm.getColumnWidth(this.cellIndex);
37443         var minw = Math.max(w-this.grid.minColumnWidth, 0);
37444         this.resetConstraints();
37445         this.setXConstraint(minw, 1000);
37446         this.setYConstraint(0, 0);
37447         this.minX = x - minw;
37448         this.maxX = x + 1000;
37449         this.startPos = x;
37450         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
37451     },
37452
37453
37454     handleMouseDown : function(e){
37455         ev = Roo.EventObject.setEvent(e);
37456         var t = this.fly(ev.getTarget());
37457         if(t.hasClass("x-grid-split")){
37458             this.cellIndex = this.view.getCellIndex(t.dom);
37459             this.split = t.dom;
37460             this.cm = this.grid.colModel;
37461             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
37462                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
37463             }
37464         }
37465     },
37466
37467     endDrag : function(e){
37468         this.view.headersDisabled = false;
37469         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
37470         var diff = endX - this.startPos;
37471         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
37472     },
37473
37474     autoOffset : function(){
37475         this.setDelta(0,0);
37476     }
37477 });/*
37478  * Based on:
37479  * Ext JS Library 1.1.1
37480  * Copyright(c) 2006-2007, Ext JS, LLC.
37481  *
37482  * Originally Released Under LGPL - original licence link has changed is not relivant.
37483  *
37484  * Fork - LGPL
37485  * <script type="text/javascript">
37486  */
37487  
37488 // private
37489 // This is a support class used internally by the Grid components
37490 Roo.grid.GridDragZone = function(grid, config){
37491     this.view = grid.getView();
37492     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
37493     if(this.view.lockedBody){
37494         this.setHandleElId(Roo.id(this.view.mainBody.dom));
37495         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
37496     }
37497     this.scroll = false;
37498     this.grid = grid;
37499     this.ddel = document.createElement('div');
37500     this.ddel.className = 'x-grid-dd-wrap';
37501 };
37502
37503 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
37504     ddGroup : "GridDD",
37505
37506     getDragData : function(e){
37507         var t = Roo.lib.Event.getTarget(e);
37508         var rowIndex = this.view.findRowIndex(t);
37509         var sm = this.grid.selModel;
37510             
37511         //Roo.log(rowIndex);
37512         
37513         if (sm.getSelectedCell) {
37514             // cell selection..
37515             if (!sm.getSelectedCell()) {
37516                 return false;
37517             }
37518             if (rowIndex != sm.getSelectedCell()[0]) {
37519                 return false;
37520             }
37521         
37522         }
37523         
37524         if(rowIndex !== false){
37525             
37526             // if editorgrid.. 
37527             
37528             
37529             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
37530                
37531             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
37532               //  
37533             //}
37534             if (e.hasModifier()){
37535                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
37536             }
37537             
37538             Roo.log("getDragData");
37539             
37540             return {
37541                 grid: this.grid,
37542                 ddel: this.ddel,
37543                 rowIndex: rowIndex,
37544                 selections:sm.getSelections ? sm.getSelections() : (
37545                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
37546                 )
37547             };
37548         }
37549         return false;
37550     },
37551
37552     onInitDrag : function(e){
37553         var data = this.dragData;
37554         this.ddel.innerHTML = this.grid.getDragDropText();
37555         this.proxy.update(this.ddel);
37556         // fire start drag?
37557     },
37558
37559     afterRepair : function(){
37560         this.dragging = false;
37561     },
37562
37563     getRepairXY : function(e, data){
37564         return false;
37565     },
37566
37567     onEndDrag : function(data, e){
37568         // fire end drag?
37569     },
37570
37571     onValidDrop : function(dd, e, id){
37572         // fire drag drop?
37573         this.hideProxy();
37574     },
37575
37576     beforeInvalidDrop : function(e, id){
37577
37578     }
37579 });/*
37580  * Based on:
37581  * Ext JS Library 1.1.1
37582  * Copyright(c) 2006-2007, Ext JS, LLC.
37583  *
37584  * Originally Released Under LGPL - original licence link has changed is not relivant.
37585  *
37586  * Fork - LGPL
37587  * <script type="text/javascript">
37588  */
37589  
37590
37591 /**
37592  * @class Roo.grid.ColumnModel
37593  * @extends Roo.util.Observable
37594  * This is the default implementation of a ColumnModel used by the Grid. It defines
37595  * the columns in the grid.
37596  * <br>Usage:<br>
37597  <pre><code>
37598  var colModel = new Roo.grid.ColumnModel([
37599         {header: "Ticker", width: 60, sortable: true, locked: true},
37600         {header: "Company Name", width: 150, sortable: true},
37601         {header: "Market Cap.", width: 100, sortable: true},
37602         {header: "$ Sales", width: 100, sortable: true, renderer: money},
37603         {header: "Employees", width: 100, sortable: true, resizable: false}
37604  ]);
37605  </code></pre>
37606  * <p>
37607  
37608  * The config options listed for this class are options which may appear in each
37609  * individual column definition.
37610  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
37611  * @constructor
37612  * @param {Object} config An Array of column config objects. See this class's
37613  * config objects for details.
37614 */
37615 Roo.grid.ColumnModel = function(config){
37616         /**
37617      * The config passed into the constructor
37618      */
37619     this.config = config;
37620     this.lookup = {};
37621
37622     // if no id, create one
37623     // if the column does not have a dataIndex mapping,
37624     // map it to the order it is in the config
37625     for(var i = 0, len = config.length; i < len; i++){
37626         var c = config[i];
37627         if(typeof c.dataIndex == "undefined"){
37628             c.dataIndex = i;
37629         }
37630         if(typeof c.renderer == "string"){
37631             c.renderer = Roo.util.Format[c.renderer];
37632         }
37633         if(typeof c.id == "undefined"){
37634             c.id = Roo.id();
37635         }
37636         if(c.editor && c.editor.xtype){
37637             c.editor  = Roo.factory(c.editor, Roo.grid);
37638         }
37639         if(c.editor && c.editor.isFormField){
37640             c.editor = new Roo.grid.GridEditor(c.editor);
37641         }
37642         this.lookup[c.id] = c;
37643     }
37644
37645     /**
37646      * The width of columns which have no width specified (defaults to 100)
37647      * @type Number
37648      */
37649     this.defaultWidth = 100;
37650
37651     /**
37652      * Default sortable of columns which have no sortable specified (defaults to false)
37653      * @type Boolean
37654      */
37655     this.defaultSortable = false;
37656
37657     this.addEvents({
37658         /**
37659              * @event widthchange
37660              * Fires when the width of a column changes.
37661              * @param {ColumnModel} this
37662              * @param {Number} columnIndex The column index
37663              * @param {Number} newWidth The new width
37664              */
37665             "widthchange": true,
37666         /**
37667              * @event headerchange
37668              * Fires when the text of a header changes.
37669              * @param {ColumnModel} this
37670              * @param {Number} columnIndex The column index
37671              * @param {Number} newText The new header text
37672              */
37673             "headerchange": true,
37674         /**
37675              * @event hiddenchange
37676              * Fires when a column is hidden or "unhidden".
37677              * @param {ColumnModel} this
37678              * @param {Number} columnIndex The column index
37679              * @param {Boolean} hidden true if hidden, false otherwise
37680              */
37681             "hiddenchange": true,
37682             /**
37683          * @event columnmoved
37684          * Fires when a column is moved.
37685          * @param {ColumnModel} this
37686          * @param {Number} oldIndex
37687          * @param {Number} newIndex
37688          */
37689         "columnmoved" : true,
37690         /**
37691          * @event columlockchange
37692          * Fires when a column's locked state is changed
37693          * @param {ColumnModel} this
37694          * @param {Number} colIndex
37695          * @param {Boolean} locked true if locked
37696          */
37697         "columnlockchange" : true
37698     });
37699     Roo.grid.ColumnModel.superclass.constructor.call(this);
37700 };
37701 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
37702     /**
37703      * @cfg {String} header The header text to display in the Grid view.
37704      */
37705     /**
37706      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
37707      * {@link Roo.data.Record} definition from which to draw the column's value. If not
37708      * specified, the column's index is used as an index into the Record's data Array.
37709      */
37710     /**
37711      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
37712      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
37713      */
37714     /**
37715      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
37716      * Defaults to the value of the {@link #defaultSortable} property.
37717      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
37718      */
37719     /**
37720      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
37721      */
37722     /**
37723      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
37724      */
37725     /**
37726      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
37727      */
37728     /**
37729      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
37730      */
37731     /**
37732      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
37733      * given the cell's data value. See {@link #setRenderer}. If not specified, the
37734      * default renderer uses the raw data value.
37735      */
37736        /**
37737      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
37738      */
37739     /**
37740      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
37741      */
37742
37743     /**
37744      * Returns the id of the column at the specified index.
37745      * @param {Number} index The column index
37746      * @return {String} the id
37747      */
37748     getColumnId : function(index){
37749         return this.config[index].id;
37750     },
37751
37752     /**
37753      * Returns the column for a specified id.
37754      * @param {String} id The column id
37755      * @return {Object} the column
37756      */
37757     getColumnById : function(id){
37758         return this.lookup[id];
37759     },
37760
37761     
37762     /**
37763      * Returns the column for a specified dataIndex.
37764      * @param {String} dataIndex The column dataIndex
37765      * @return {Object|Boolean} the column or false if not found
37766      */
37767     getColumnByDataIndex: function(dataIndex){
37768         var index = this.findColumnIndex(dataIndex);
37769         return index > -1 ? this.config[index] : false;
37770     },
37771     
37772     /**
37773      * Returns the index for a specified column id.
37774      * @param {String} id The column id
37775      * @return {Number} the index, or -1 if not found
37776      */
37777     getIndexById : function(id){
37778         for(var i = 0, len = this.config.length; i < len; i++){
37779             if(this.config[i].id == id){
37780                 return i;
37781             }
37782         }
37783         return -1;
37784     },
37785     
37786     /**
37787      * Returns the index for a specified column dataIndex.
37788      * @param {String} dataIndex The column dataIndex
37789      * @return {Number} the index, or -1 if not found
37790      */
37791     
37792     findColumnIndex : function(dataIndex){
37793         for(var i = 0, len = this.config.length; i < len; i++){
37794             if(this.config[i].dataIndex == dataIndex){
37795                 return i;
37796             }
37797         }
37798         return -1;
37799     },
37800     
37801     
37802     moveColumn : function(oldIndex, newIndex){
37803         var c = this.config[oldIndex];
37804         this.config.splice(oldIndex, 1);
37805         this.config.splice(newIndex, 0, c);
37806         this.dataMap = null;
37807         this.fireEvent("columnmoved", this, oldIndex, newIndex);
37808     },
37809
37810     isLocked : function(colIndex){
37811         return this.config[colIndex].locked === true;
37812     },
37813
37814     setLocked : function(colIndex, value, suppressEvent){
37815         if(this.isLocked(colIndex) == value){
37816             return;
37817         }
37818         this.config[colIndex].locked = value;
37819         if(!suppressEvent){
37820             this.fireEvent("columnlockchange", this, colIndex, value);
37821         }
37822     },
37823
37824     getTotalLockedWidth : function(){
37825         var totalWidth = 0;
37826         for(var i = 0; i < this.config.length; i++){
37827             if(this.isLocked(i) && !this.isHidden(i)){
37828                 this.totalWidth += this.getColumnWidth(i);
37829             }
37830         }
37831         return totalWidth;
37832     },
37833
37834     getLockedCount : function(){
37835         for(var i = 0, len = this.config.length; i < len; i++){
37836             if(!this.isLocked(i)){
37837                 return i;
37838             }
37839         }
37840     },
37841
37842     /**
37843      * Returns the number of columns.
37844      * @return {Number}
37845      */
37846     getColumnCount : function(visibleOnly){
37847         if(visibleOnly === true){
37848             var c = 0;
37849             for(var i = 0, len = this.config.length; i < len; i++){
37850                 if(!this.isHidden(i)){
37851                     c++;
37852                 }
37853             }
37854             return c;
37855         }
37856         return this.config.length;
37857     },
37858
37859     /**
37860      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37861      * @param {Function} fn
37862      * @param {Object} scope (optional)
37863      * @return {Array} result
37864      */
37865     getColumnsBy : function(fn, scope){
37866         var r = [];
37867         for(var i = 0, len = this.config.length; i < len; i++){
37868             var c = this.config[i];
37869             if(fn.call(scope||this, c, i) === true){
37870                 r[r.length] = c;
37871             }
37872         }
37873         return r;
37874     },
37875
37876     /**
37877      * Returns true if the specified column is sortable.
37878      * @param {Number} col The column index
37879      * @return {Boolean}
37880      */
37881     isSortable : function(col){
37882         if(typeof this.config[col].sortable == "undefined"){
37883             return this.defaultSortable;
37884         }
37885         return this.config[col].sortable;
37886     },
37887
37888     /**
37889      * Returns the rendering (formatting) function defined for the column.
37890      * @param {Number} col The column index.
37891      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37892      */
37893     getRenderer : function(col){
37894         if(!this.config[col].renderer){
37895             return Roo.grid.ColumnModel.defaultRenderer;
37896         }
37897         return this.config[col].renderer;
37898     },
37899
37900     /**
37901      * Sets the rendering (formatting) function for a column.
37902      * @param {Number} col The column index
37903      * @param {Function} fn The function to use to process the cell's raw data
37904      * to return HTML markup for the grid view. The render function is called with
37905      * the following parameters:<ul>
37906      * <li>Data value.</li>
37907      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37908      * <li>css A CSS style string to apply to the table cell.</li>
37909      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37910      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37911      * <li>Row index</li>
37912      * <li>Column index</li>
37913      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37914      */
37915     setRenderer : function(col, fn){
37916         this.config[col].renderer = fn;
37917     },
37918
37919     /**
37920      * Returns the width for the specified column.
37921      * @param {Number} col The column index
37922      * @return {Number}
37923      */
37924     getColumnWidth : function(col){
37925         return this.config[col].width * 1 || this.defaultWidth;
37926     },
37927
37928     /**
37929      * Sets the width for a column.
37930      * @param {Number} col The column index
37931      * @param {Number} width The new width
37932      */
37933     setColumnWidth : function(col, width, suppressEvent){
37934         this.config[col].width = width;
37935         this.totalWidth = null;
37936         if(!suppressEvent){
37937              this.fireEvent("widthchange", this, col, width);
37938         }
37939     },
37940
37941     /**
37942      * Returns the total width of all columns.
37943      * @param {Boolean} includeHidden True to include hidden column widths
37944      * @return {Number}
37945      */
37946     getTotalWidth : function(includeHidden){
37947         if(!this.totalWidth){
37948             this.totalWidth = 0;
37949             for(var i = 0, len = this.config.length; i < len; i++){
37950                 if(includeHidden || !this.isHidden(i)){
37951                     this.totalWidth += this.getColumnWidth(i);
37952                 }
37953             }
37954         }
37955         return this.totalWidth;
37956     },
37957
37958     /**
37959      * Returns the header for the specified column.
37960      * @param {Number} col The column index
37961      * @return {String}
37962      */
37963     getColumnHeader : function(col){
37964         return this.config[col].header;
37965     },
37966
37967     /**
37968      * Sets the header for a column.
37969      * @param {Number} col The column index
37970      * @param {String} header The new header
37971      */
37972     setColumnHeader : function(col, header){
37973         this.config[col].header = header;
37974         this.fireEvent("headerchange", this, col, header);
37975     },
37976
37977     /**
37978      * Returns the tooltip for the specified column.
37979      * @param {Number} col The column index
37980      * @return {String}
37981      */
37982     getColumnTooltip : function(col){
37983             return this.config[col].tooltip;
37984     },
37985     /**
37986      * Sets the tooltip for a column.
37987      * @param {Number} col The column index
37988      * @param {String} tooltip The new tooltip
37989      */
37990     setColumnTooltip : function(col, tooltip){
37991             this.config[col].tooltip = tooltip;
37992     },
37993
37994     /**
37995      * Returns the dataIndex for the specified column.
37996      * @param {Number} col The column index
37997      * @return {Number}
37998      */
37999     getDataIndex : function(col){
38000         return this.config[col].dataIndex;
38001     },
38002
38003     /**
38004      * Sets the dataIndex for a column.
38005      * @param {Number} col The column index
38006      * @param {Number} dataIndex The new dataIndex
38007      */
38008     setDataIndex : function(col, dataIndex){
38009         this.config[col].dataIndex = dataIndex;
38010     },
38011
38012     
38013     
38014     /**
38015      * Returns true if the cell is editable.
38016      * @param {Number} colIndex The column index
38017      * @param {Number} rowIndex The row index
38018      * @return {Boolean}
38019      */
38020     isCellEditable : function(colIndex, rowIndex){
38021         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38022     },
38023
38024     /**
38025      * Returns the editor defined for the cell/column.
38026      * return false or null to disable editing.
38027      * @param {Number} colIndex The column index
38028      * @param {Number} rowIndex The row index
38029      * @return {Object}
38030      */
38031     getCellEditor : function(colIndex, rowIndex){
38032         return this.config[colIndex].editor;
38033     },
38034
38035     /**
38036      * Sets if a column is editable.
38037      * @param {Number} col The column index
38038      * @param {Boolean} editable True if the column is editable
38039      */
38040     setEditable : function(col, editable){
38041         this.config[col].editable = editable;
38042     },
38043
38044
38045     /**
38046      * Returns true if the column is hidden.
38047      * @param {Number} colIndex The column index
38048      * @return {Boolean}
38049      */
38050     isHidden : function(colIndex){
38051         return this.config[colIndex].hidden;
38052     },
38053
38054
38055     /**
38056      * Returns true if the column width cannot be changed
38057      */
38058     isFixed : function(colIndex){
38059         return this.config[colIndex].fixed;
38060     },
38061
38062     /**
38063      * Returns true if the column can be resized
38064      * @return {Boolean}
38065      */
38066     isResizable : function(colIndex){
38067         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38068     },
38069     /**
38070      * Sets if a column is hidden.
38071      * @param {Number} colIndex The column index
38072      * @param {Boolean} hidden True if the column is hidden
38073      */
38074     setHidden : function(colIndex, hidden){
38075         this.config[colIndex].hidden = hidden;
38076         this.totalWidth = null;
38077         this.fireEvent("hiddenchange", this, colIndex, hidden);
38078     },
38079
38080     /**
38081      * Sets the editor for a column.
38082      * @param {Number} col The column index
38083      * @param {Object} editor The editor object
38084      */
38085     setEditor : function(col, editor){
38086         this.config[col].editor = editor;
38087     }
38088 });
38089
38090 Roo.grid.ColumnModel.defaultRenderer = function(value){
38091         if(typeof value == "string" && value.length < 1){
38092             return "&#160;";
38093         }
38094         return value;
38095 };
38096
38097 // Alias for backwards compatibility
38098 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38099 /*
38100  * Based on:
38101  * Ext JS Library 1.1.1
38102  * Copyright(c) 2006-2007, Ext JS, LLC.
38103  *
38104  * Originally Released Under LGPL - original licence link has changed is not relivant.
38105  *
38106  * Fork - LGPL
38107  * <script type="text/javascript">
38108  */
38109
38110 /**
38111  * @class Roo.grid.AbstractSelectionModel
38112  * @extends Roo.util.Observable
38113  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38114  * implemented by descendant classes.  This class should not be directly instantiated.
38115  * @constructor
38116  */
38117 Roo.grid.AbstractSelectionModel = function(){
38118     this.locked = false;
38119     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38120 };
38121
38122 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38123     /** @ignore Called by the grid automatically. Do not call directly. */
38124     init : function(grid){
38125         this.grid = grid;
38126         this.initEvents();
38127     },
38128
38129     /**
38130      * Locks the selections.
38131      */
38132     lock : function(){
38133         this.locked = true;
38134     },
38135
38136     /**
38137      * Unlocks the selections.
38138      */
38139     unlock : function(){
38140         this.locked = false;
38141     },
38142
38143     /**
38144      * Returns true if the selections are locked.
38145      * @return {Boolean}
38146      */
38147     isLocked : function(){
38148         return this.locked;
38149     }
38150 });/*
38151  * Based on:
38152  * Ext JS Library 1.1.1
38153  * Copyright(c) 2006-2007, Ext JS, LLC.
38154  *
38155  * Originally Released Under LGPL - original licence link has changed is not relivant.
38156  *
38157  * Fork - LGPL
38158  * <script type="text/javascript">
38159  */
38160 /**
38161  * @extends Roo.grid.AbstractSelectionModel
38162  * @class Roo.grid.RowSelectionModel
38163  * The default SelectionModel used by {@link Roo.grid.Grid}.
38164  * It supports multiple selections and keyboard selection/navigation. 
38165  * @constructor
38166  * @param {Object} config
38167  */
38168 Roo.grid.RowSelectionModel = function(config){
38169     Roo.apply(this, config);
38170     this.selections = new Roo.util.MixedCollection(false, function(o){
38171         return o.id;
38172     });
38173
38174     this.last = false;
38175     this.lastActive = false;
38176
38177     this.addEvents({
38178         /**
38179              * @event selectionchange
38180              * Fires when the selection changes
38181              * @param {SelectionModel} this
38182              */
38183             "selectionchange" : true,
38184         /**
38185              * @event afterselectionchange
38186              * Fires after the selection changes (eg. by key press or clicking)
38187              * @param {SelectionModel} this
38188              */
38189             "afterselectionchange" : true,
38190         /**
38191              * @event beforerowselect
38192              * Fires when a row is selected being selected, return false to cancel.
38193              * @param {SelectionModel} this
38194              * @param {Number} rowIndex The selected index
38195              * @param {Boolean} keepExisting False if other selections will be cleared
38196              */
38197             "beforerowselect" : true,
38198         /**
38199              * @event rowselect
38200              * Fires when a row is selected.
38201              * @param {SelectionModel} this
38202              * @param {Number} rowIndex The selected index
38203              * @param {Roo.data.Record} r The record
38204              */
38205             "rowselect" : true,
38206         /**
38207              * @event rowdeselect
38208              * Fires when a row is deselected.
38209              * @param {SelectionModel} this
38210              * @param {Number} rowIndex The selected index
38211              */
38212         "rowdeselect" : true
38213     });
38214     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38215     this.locked = false;
38216 };
38217
38218 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38219     /**
38220      * @cfg {Boolean} singleSelect
38221      * True to allow selection of only one row at a time (defaults to false)
38222      */
38223     singleSelect : false,
38224
38225     // private
38226     initEvents : function(){
38227
38228         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38229             this.grid.on("mousedown", this.handleMouseDown, this);
38230         }else{ // allow click to work like normal
38231             this.grid.on("rowclick", this.handleDragableRowClick, this);
38232         }
38233
38234         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38235             "up" : function(e){
38236                 if(!e.shiftKey){
38237                     this.selectPrevious(e.shiftKey);
38238                 }else if(this.last !== false && this.lastActive !== false){
38239                     var last = this.last;
38240                     this.selectRange(this.last,  this.lastActive-1);
38241                     this.grid.getView().focusRow(this.lastActive);
38242                     if(last !== false){
38243                         this.last = last;
38244                     }
38245                 }else{
38246                     this.selectFirstRow();
38247                 }
38248                 this.fireEvent("afterselectionchange", this);
38249             },
38250             "down" : function(e){
38251                 if(!e.shiftKey){
38252                     this.selectNext(e.shiftKey);
38253                 }else if(this.last !== false && this.lastActive !== false){
38254                     var last = this.last;
38255                     this.selectRange(this.last,  this.lastActive+1);
38256                     this.grid.getView().focusRow(this.lastActive);
38257                     if(last !== false){
38258                         this.last = last;
38259                     }
38260                 }else{
38261                     this.selectFirstRow();
38262                 }
38263                 this.fireEvent("afterselectionchange", this);
38264             },
38265             scope: this
38266         });
38267
38268         var view = this.grid.view;
38269         view.on("refresh", this.onRefresh, this);
38270         view.on("rowupdated", this.onRowUpdated, this);
38271         view.on("rowremoved", this.onRemove, this);
38272     },
38273
38274     // private
38275     onRefresh : function(){
38276         var ds = this.grid.dataSource, i, v = this.grid.view;
38277         var s = this.selections;
38278         s.each(function(r){
38279             if((i = ds.indexOfId(r.id)) != -1){
38280                 v.onRowSelect(i);
38281             }else{
38282                 s.remove(r);
38283             }
38284         });
38285     },
38286
38287     // private
38288     onRemove : function(v, index, r){
38289         this.selections.remove(r);
38290     },
38291
38292     // private
38293     onRowUpdated : function(v, index, r){
38294         if(this.isSelected(r)){
38295             v.onRowSelect(index);
38296         }
38297     },
38298
38299     /**
38300      * Select records.
38301      * @param {Array} records The records to select
38302      * @param {Boolean} keepExisting (optional) True to keep existing selections
38303      */
38304     selectRecords : function(records, keepExisting){
38305         if(!keepExisting){
38306             this.clearSelections();
38307         }
38308         var ds = this.grid.dataSource;
38309         for(var i = 0, len = records.length; i < len; i++){
38310             this.selectRow(ds.indexOf(records[i]), true);
38311         }
38312     },
38313
38314     /**
38315      * Gets the number of selected rows.
38316      * @return {Number}
38317      */
38318     getCount : function(){
38319         return this.selections.length;
38320     },
38321
38322     /**
38323      * Selects the first row in the grid.
38324      */
38325     selectFirstRow : function(){
38326         this.selectRow(0);
38327     },
38328
38329     /**
38330      * Select the last row.
38331      * @param {Boolean} keepExisting (optional) True to keep existing selections
38332      */
38333     selectLastRow : function(keepExisting){
38334         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
38335     },
38336
38337     /**
38338      * Selects the row immediately following the last selected row.
38339      * @param {Boolean} keepExisting (optional) True to keep existing selections
38340      */
38341     selectNext : function(keepExisting){
38342         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
38343             this.selectRow(this.last+1, keepExisting);
38344             this.grid.getView().focusRow(this.last);
38345         }
38346     },
38347
38348     /**
38349      * Selects the row that precedes the last selected row.
38350      * @param {Boolean} keepExisting (optional) True to keep existing selections
38351      */
38352     selectPrevious : function(keepExisting){
38353         if(this.last){
38354             this.selectRow(this.last-1, keepExisting);
38355             this.grid.getView().focusRow(this.last);
38356         }
38357     },
38358
38359     /**
38360      * Returns the selected records
38361      * @return {Array} Array of selected records
38362      */
38363     getSelections : function(){
38364         return [].concat(this.selections.items);
38365     },
38366
38367     /**
38368      * Returns the first selected record.
38369      * @return {Record}
38370      */
38371     getSelected : function(){
38372         return this.selections.itemAt(0);
38373     },
38374
38375
38376     /**
38377      * Clears all selections.
38378      */
38379     clearSelections : function(fast){
38380         if(this.locked) return;
38381         if(fast !== true){
38382             var ds = this.grid.dataSource;
38383             var s = this.selections;
38384             s.each(function(r){
38385                 this.deselectRow(ds.indexOfId(r.id));
38386             }, this);
38387             s.clear();
38388         }else{
38389             this.selections.clear();
38390         }
38391         this.last = false;
38392     },
38393
38394
38395     /**
38396      * Selects all rows.
38397      */
38398     selectAll : function(){
38399         if(this.locked) return;
38400         this.selections.clear();
38401         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
38402             this.selectRow(i, true);
38403         }
38404     },
38405
38406     /**
38407      * Returns True if there is a selection.
38408      * @return {Boolean}
38409      */
38410     hasSelection : function(){
38411         return this.selections.length > 0;
38412     },
38413
38414     /**
38415      * Returns True if the specified row is selected.
38416      * @param {Number/Record} record The record or index of the record to check
38417      * @return {Boolean}
38418      */
38419     isSelected : function(index){
38420         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
38421         return (r && this.selections.key(r.id) ? true : false);
38422     },
38423
38424     /**
38425      * Returns True if the specified record id is selected.
38426      * @param {String} id The id of record to check
38427      * @return {Boolean}
38428      */
38429     isIdSelected : function(id){
38430         return (this.selections.key(id) ? true : false);
38431     },
38432
38433     // private
38434     handleMouseDown : function(e, t){
38435         var view = this.grid.getView(), rowIndex;
38436         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
38437             return;
38438         };
38439         if(e.shiftKey && this.last !== false){
38440             var last = this.last;
38441             this.selectRange(last, rowIndex, e.ctrlKey);
38442             this.last = last; // reset the last
38443             view.focusRow(rowIndex);
38444         }else{
38445             var isSelected = this.isSelected(rowIndex);
38446             if(e.button !== 0 && isSelected){
38447                 view.focusRow(rowIndex);
38448             }else if(e.ctrlKey && isSelected){
38449                 this.deselectRow(rowIndex);
38450             }else if(!isSelected){
38451                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
38452                 view.focusRow(rowIndex);
38453             }
38454         }
38455         this.fireEvent("afterselectionchange", this);
38456     },
38457     // private
38458     handleDragableRowClick :  function(grid, rowIndex, e) 
38459     {
38460         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
38461             this.selectRow(rowIndex, false);
38462             grid.view.focusRow(rowIndex);
38463              this.fireEvent("afterselectionchange", this);
38464         }
38465     },
38466     
38467     /**
38468      * Selects multiple rows.
38469      * @param {Array} rows Array of the indexes of the row to select
38470      * @param {Boolean} keepExisting (optional) True to keep existing selections
38471      */
38472     selectRows : function(rows, keepExisting){
38473         if(!keepExisting){
38474             this.clearSelections();
38475         }
38476         for(var i = 0, len = rows.length; i < len; i++){
38477             this.selectRow(rows[i], true);
38478         }
38479     },
38480
38481     /**
38482      * Selects a range of rows. All rows in between startRow and endRow are also selected.
38483      * @param {Number} startRow The index of the first row in the range
38484      * @param {Number} endRow The index of the last row in the range
38485      * @param {Boolean} keepExisting (optional) True to retain existing selections
38486      */
38487     selectRange : function(startRow, endRow, keepExisting){
38488         if(this.locked) return;
38489         if(!keepExisting){
38490             this.clearSelections();
38491         }
38492         if(startRow <= endRow){
38493             for(var i = startRow; i <= endRow; i++){
38494                 this.selectRow(i, true);
38495             }
38496         }else{
38497             for(var i = startRow; i >= endRow; i--){
38498                 this.selectRow(i, true);
38499             }
38500         }
38501     },
38502
38503     /**
38504      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
38505      * @param {Number} startRow The index of the first row in the range
38506      * @param {Number} endRow The index of the last row in the range
38507      */
38508     deselectRange : function(startRow, endRow, preventViewNotify){
38509         if(this.locked) return;
38510         for(var i = startRow; i <= endRow; i++){
38511             this.deselectRow(i, preventViewNotify);
38512         }
38513     },
38514
38515     /**
38516      * Selects a row.
38517      * @param {Number} row The index of the row to select
38518      * @param {Boolean} keepExisting (optional) True to keep existing selections
38519      */
38520     selectRow : function(index, keepExisting, preventViewNotify){
38521         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
38522         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
38523             if(!keepExisting || this.singleSelect){
38524                 this.clearSelections();
38525             }
38526             var r = this.grid.dataSource.getAt(index);
38527             this.selections.add(r);
38528             this.last = this.lastActive = index;
38529             if(!preventViewNotify){
38530                 this.grid.getView().onRowSelect(index);
38531             }
38532             this.fireEvent("rowselect", this, index, r);
38533             this.fireEvent("selectionchange", this);
38534         }
38535     },
38536
38537     /**
38538      * Deselects a row.
38539      * @param {Number} row The index of the row to deselect
38540      */
38541     deselectRow : function(index, preventViewNotify){
38542         if(this.locked) return;
38543         if(this.last == index){
38544             this.last = false;
38545         }
38546         if(this.lastActive == index){
38547             this.lastActive = false;
38548         }
38549         var r = this.grid.dataSource.getAt(index);
38550         this.selections.remove(r);
38551         if(!preventViewNotify){
38552             this.grid.getView().onRowDeselect(index);
38553         }
38554         this.fireEvent("rowdeselect", this, index);
38555         this.fireEvent("selectionchange", this);
38556     },
38557
38558     // private
38559     restoreLast : function(){
38560         if(this._last){
38561             this.last = this._last;
38562         }
38563     },
38564
38565     // private
38566     acceptsNav : function(row, col, cm){
38567         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38568     },
38569
38570     // private
38571     onEditorKey : function(field, e){
38572         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
38573         if(k == e.TAB){
38574             e.stopEvent();
38575             ed.completeEdit();
38576             if(e.shiftKey){
38577                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38578             }else{
38579                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38580             }
38581         }else if(k == e.ENTER && !e.ctrlKey){
38582             e.stopEvent();
38583             ed.completeEdit();
38584             if(e.shiftKey){
38585                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
38586             }else{
38587                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
38588             }
38589         }else if(k == e.ESC){
38590             ed.cancelEdit();
38591         }
38592         if(newCell){
38593             g.startEditing(newCell[0], newCell[1]);
38594         }
38595     }
38596 });/*
38597  * Based on:
38598  * Ext JS Library 1.1.1
38599  * Copyright(c) 2006-2007, Ext JS, LLC.
38600  *
38601  * Originally Released Under LGPL - original licence link has changed is not relivant.
38602  *
38603  * Fork - LGPL
38604  * <script type="text/javascript">
38605  */
38606 /**
38607  * @class Roo.grid.CellSelectionModel
38608  * @extends Roo.grid.AbstractSelectionModel
38609  * This class provides the basic implementation for cell selection in a grid.
38610  * @constructor
38611  * @param {Object} config The object containing the configuration of this model.
38612  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
38613  */
38614 Roo.grid.CellSelectionModel = function(config){
38615     Roo.apply(this, config);
38616
38617     this.selection = null;
38618
38619     this.addEvents({
38620         /**
38621              * @event beforerowselect
38622              * Fires before a cell is selected.
38623              * @param {SelectionModel} this
38624              * @param {Number} rowIndex The selected row index
38625              * @param {Number} colIndex The selected cell index
38626              */
38627             "beforecellselect" : true,
38628         /**
38629              * @event cellselect
38630              * Fires when a cell is selected.
38631              * @param {SelectionModel} this
38632              * @param {Number} rowIndex The selected row index
38633              * @param {Number} colIndex The selected cell index
38634              */
38635             "cellselect" : true,
38636         /**
38637              * @event selectionchange
38638              * Fires when the active selection changes.
38639              * @param {SelectionModel} this
38640              * @param {Object} selection null for no selection or an object (o) with two properties
38641                 <ul>
38642                 <li>o.record: the record object for the row the selection is in</li>
38643                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
38644                 </ul>
38645              */
38646             "selectionchange" : true,
38647         /**
38648              * @event tabend
38649              * Fires when the tab (or enter) was pressed on the last editable cell
38650              * You can use this to trigger add new row.
38651              * @param {SelectionModel} this
38652              */
38653             "tabend" : true,
38654          /**
38655              * @event beforeeditnext
38656              * Fires before the next editable sell is made active
38657              * You can use this to skip to another cell or fire the tabend
38658              *    if you set cell to false
38659              * @param {Object} eventdata object : { cell : [ row, col ] } 
38660              */
38661             "beforeeditnext" : true
38662     });
38663     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
38664 };
38665
38666 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
38667     
38668     enter_is_tab: false,
38669
38670     /** @ignore */
38671     initEvents : function(){
38672         this.grid.on("mousedown", this.handleMouseDown, this);
38673         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
38674         var view = this.grid.view;
38675         view.on("refresh", this.onViewChange, this);
38676         view.on("rowupdated", this.onRowUpdated, this);
38677         view.on("beforerowremoved", this.clearSelections, this);
38678         view.on("beforerowsinserted", this.clearSelections, this);
38679         if(this.grid.isEditor){
38680             this.grid.on("beforeedit", this.beforeEdit,  this);
38681         }
38682     },
38683
38684         //private
38685     beforeEdit : function(e){
38686         this.select(e.row, e.column, false, true, e.record);
38687     },
38688
38689         //private
38690     onRowUpdated : function(v, index, r){
38691         if(this.selection && this.selection.record == r){
38692             v.onCellSelect(index, this.selection.cell[1]);
38693         }
38694     },
38695
38696         //private
38697     onViewChange : function(){
38698         this.clearSelections(true);
38699     },
38700
38701         /**
38702          * Returns the currently selected cell,.
38703          * @return {Array} The selected cell (row, column) or null if none selected.
38704          */
38705     getSelectedCell : function(){
38706         return this.selection ? this.selection.cell : null;
38707     },
38708
38709     /**
38710      * Clears all selections.
38711      * @param {Boolean} true to prevent the gridview from being notified about the change.
38712      */
38713     clearSelections : function(preventNotify){
38714         var s = this.selection;
38715         if(s){
38716             if(preventNotify !== true){
38717                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
38718             }
38719             this.selection = null;
38720             this.fireEvent("selectionchange", this, null);
38721         }
38722     },
38723
38724     /**
38725      * Returns true if there is a selection.
38726      * @return {Boolean}
38727      */
38728     hasSelection : function(){
38729         return this.selection ? true : false;
38730     },
38731
38732     /** @ignore */
38733     handleMouseDown : function(e, t){
38734         var v = this.grid.getView();
38735         if(this.isLocked()){
38736             return;
38737         };
38738         var row = v.findRowIndex(t);
38739         var cell = v.findCellIndex(t);
38740         if(row !== false && cell !== false){
38741             this.select(row, cell);
38742         }
38743     },
38744
38745     /**
38746      * Selects a cell.
38747      * @param {Number} rowIndex
38748      * @param {Number} collIndex
38749      */
38750     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
38751         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
38752             this.clearSelections();
38753             r = r || this.grid.dataSource.getAt(rowIndex);
38754             this.selection = {
38755                 record : r,
38756                 cell : [rowIndex, colIndex]
38757             };
38758             if(!preventViewNotify){
38759                 var v = this.grid.getView();
38760                 v.onCellSelect(rowIndex, colIndex);
38761                 if(preventFocus !== true){
38762                     v.focusCell(rowIndex, colIndex);
38763                 }
38764             }
38765             this.fireEvent("cellselect", this, rowIndex, colIndex);
38766             this.fireEvent("selectionchange", this, this.selection);
38767         }
38768     },
38769
38770         //private
38771     isSelectable : function(rowIndex, colIndex, cm){
38772         return !cm.isHidden(colIndex);
38773     },
38774
38775     /** @ignore */
38776     handleKeyDown : function(e){
38777         //Roo.log('Cell Sel Model handleKeyDown');
38778         if(!e.isNavKeyPress()){
38779             return;
38780         }
38781         var g = this.grid, s = this.selection;
38782         if(!s){
38783             e.stopEvent();
38784             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
38785             if(cell){
38786                 this.select(cell[0], cell[1]);
38787             }
38788             return;
38789         }
38790         var sm = this;
38791         var walk = function(row, col, step){
38792             return g.walkCells(row, col, step, sm.isSelectable,  sm);
38793         };
38794         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
38795         var newCell;
38796
38797       
38798
38799         switch(k){
38800             case e.TAB:
38801                 // handled by onEditorKey
38802                 if (g.isEditor && g.editing) {
38803                     return;
38804                 }
38805                 if(e.shiftKey) {
38806                     newCell = walk(r, c-1, -1);
38807                 } else {
38808                     newCell = walk(r, c+1, 1);
38809                 }
38810                 break;
38811             
38812             case e.DOWN:
38813                newCell = walk(r+1, c, 1);
38814                 break;
38815             
38816             case e.UP:
38817                 newCell = walk(r-1, c, -1);
38818                 break;
38819             
38820             case e.RIGHT:
38821                 newCell = walk(r, c+1, 1);
38822                 break;
38823             
38824             case e.LEFT:
38825                 newCell = walk(r, c-1, -1);
38826                 break;
38827             
38828             case e.ENTER:
38829                 
38830                 if(g.isEditor && !g.editing){
38831                    g.startEditing(r, c);
38832                    e.stopEvent();
38833                    return;
38834                 }
38835                 
38836                 
38837              break;
38838         };
38839         if(newCell){
38840             this.select(newCell[0], newCell[1]);
38841             e.stopEvent();
38842             
38843         }
38844     },
38845
38846     acceptsNav : function(row, col, cm){
38847         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38848     },
38849     /**
38850      * Selects a cell.
38851      * @param {Number} field (not used) - as it's normally used as a listener
38852      * @param {Number} e - event - fake it by using
38853      *
38854      * var e = Roo.EventObjectImpl.prototype;
38855      * e.keyCode = e.TAB
38856      *
38857      * 
38858      */
38859     onEditorKey : function(field, e){
38860         
38861         var k = e.getKey(),
38862             newCell,
38863             g = this.grid,
38864             ed = g.activeEditor,
38865             forward = false;
38866         ///Roo.log('onEditorKey' + k);
38867         
38868         
38869         if (this.enter_is_tab && k == e.ENTER) {
38870             k = e.TAB;
38871         }
38872         
38873         if(k == e.TAB){
38874             if(e.shiftKey){
38875                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38876             }else{
38877                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38878                 forward = true;
38879             }
38880             
38881             e.stopEvent();
38882             
38883         } else if(k == e.ENTER &&  !e.ctrlKey){
38884             ed.completeEdit();
38885             e.stopEvent();
38886             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38887         
38888                 } else if(k == e.ESC){
38889             ed.cancelEdit();
38890         }
38891                 
38892         if (newCell) {
38893             var ecall = { cell : newCell, forward : forward };
38894             this.fireEvent('beforeeditnext', ecall );
38895             newCell = ecall.cell;
38896                         forward = ecall.forward;
38897         }
38898                 
38899         if(newCell){
38900             //Roo.log('next cell after edit');
38901             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38902         } else if (forward) {
38903             // tabbed past last
38904             this.fireEvent.defer(100, this, ['tabend',this]);
38905         }
38906     }
38907 });/*
38908  * Based on:
38909  * Ext JS Library 1.1.1
38910  * Copyright(c) 2006-2007, Ext JS, LLC.
38911  *
38912  * Originally Released Under LGPL - original licence link has changed is not relivant.
38913  *
38914  * Fork - LGPL
38915  * <script type="text/javascript">
38916  */
38917  
38918 /**
38919  * @class Roo.grid.EditorGrid
38920  * @extends Roo.grid.Grid
38921  * Class for creating and editable grid.
38922  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38923  * The container MUST have some type of size defined for the grid to fill. The container will be 
38924  * automatically set to position relative if it isn't already.
38925  * @param {Object} dataSource The data model to bind to
38926  * @param {Object} colModel The column model with info about this grid's columns
38927  */
38928 Roo.grid.EditorGrid = function(container, config){
38929     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38930     this.getGridEl().addClass("xedit-grid");
38931
38932     if(!this.selModel){
38933         this.selModel = new Roo.grid.CellSelectionModel();
38934     }
38935
38936     this.activeEditor = null;
38937
38938         this.addEvents({
38939             /**
38940              * @event beforeedit
38941              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38942              * <ul style="padding:5px;padding-left:16px;">
38943              * <li>grid - This grid</li>
38944              * <li>record - The record being edited</li>
38945              * <li>field - The field name being edited</li>
38946              * <li>value - The value for the field being edited.</li>
38947              * <li>row - The grid row index</li>
38948              * <li>column - The grid column index</li>
38949              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38950              * </ul>
38951              * @param {Object} e An edit event (see above for description)
38952              */
38953             "beforeedit" : true,
38954             /**
38955              * @event afteredit
38956              * Fires after a cell is edited. <br />
38957              * <ul style="padding:5px;padding-left:16px;">
38958              * <li>grid - This grid</li>
38959              * <li>record - The record being edited</li>
38960              * <li>field - The field name being edited</li>
38961              * <li>value - The value being set</li>
38962              * <li>originalValue - The original value for the field, before the edit.</li>
38963              * <li>row - The grid row index</li>
38964              * <li>column - The grid column index</li>
38965              * </ul>
38966              * @param {Object} e An edit event (see above for description)
38967              */
38968             "afteredit" : true,
38969             /**
38970              * @event validateedit
38971              * Fires after a cell is edited, but before the value is set in the record. 
38972          * You can use this to modify the value being set in the field, Return false
38973              * to cancel the change. The edit event object has the following properties <br />
38974              * <ul style="padding:5px;padding-left:16px;">
38975          * <li>editor - This editor</li>
38976              * <li>grid - This grid</li>
38977              * <li>record - The record being edited</li>
38978              * <li>field - The field name being edited</li>
38979              * <li>value - The value being set</li>
38980              * <li>originalValue - The original value for the field, before the edit.</li>
38981              * <li>row - The grid row index</li>
38982              * <li>column - The grid column index</li>
38983              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38984              * </ul>
38985              * @param {Object} e An edit event (see above for description)
38986              */
38987             "validateedit" : true
38988         });
38989     this.on("bodyscroll", this.stopEditing,  this);
38990     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38991 };
38992
38993 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38994     /**
38995      * @cfg {Number} clicksToEdit
38996      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38997      */
38998     clicksToEdit: 2,
38999
39000     // private
39001     isEditor : true,
39002     // private
39003     trackMouseOver: false, // causes very odd FF errors
39004
39005     onCellDblClick : function(g, row, col){
39006         this.startEditing(row, col);
39007     },
39008
39009     onEditComplete : function(ed, value, startValue){
39010         this.editing = false;
39011         this.activeEditor = null;
39012         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39013         var r = ed.record;
39014         var field = this.colModel.getDataIndex(ed.col);
39015         var e = {
39016             grid: this,
39017             record: r,
39018             field: field,
39019             originalValue: startValue,
39020             value: value,
39021             row: ed.row,
39022             column: ed.col,
39023             cancel:false,
39024             editor: ed
39025         };
39026         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39027         cell.show();
39028           
39029         if(String(value) !== String(startValue)){
39030             
39031             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39032                 r.set(field, e.value);
39033                 // if we are dealing with a combo box..
39034                 // then we also set the 'name' colum to be the displayField
39035                 if (ed.field.displayField && ed.field.name) {
39036                     r.set(ed.field.name, ed.field.el.dom.value);
39037                 }
39038                 
39039                 delete e.cancel; //?? why!!!
39040                 this.fireEvent("afteredit", e);
39041             }
39042         } else {
39043             this.fireEvent("afteredit", e); // always fire it!
39044         }
39045         this.view.focusCell(ed.row, ed.col);
39046     },
39047
39048     /**
39049      * Starts editing the specified for the specified row/column
39050      * @param {Number} rowIndex
39051      * @param {Number} colIndex
39052      */
39053     startEditing : function(row, col){
39054         this.stopEditing();
39055         if(this.colModel.isCellEditable(col, row)){
39056             this.view.ensureVisible(row, col, true);
39057           
39058             var r = this.dataSource.getAt(row);
39059             var field = this.colModel.getDataIndex(col);
39060             var cell = Roo.get(this.view.getCell(row,col));
39061             var e = {
39062                 grid: this,
39063                 record: r,
39064                 field: field,
39065                 value: r.data[field],
39066                 row: row,
39067                 column: col,
39068                 cancel:false 
39069             };
39070             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39071                 this.editing = true;
39072                 var ed = this.colModel.getCellEditor(col, row);
39073                 
39074                 if (!ed) {
39075                     return;
39076                 }
39077                 if(!ed.rendered){
39078                     ed.render(ed.parentEl || document.body);
39079                 }
39080                 ed.field.reset();
39081                
39082                 cell.hide();
39083                 
39084                 (function(){ // complex but required for focus issues in safari, ie and opera
39085                     ed.row = row;
39086                     ed.col = col;
39087                     ed.record = r;
39088                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39089                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39090                     this.activeEditor = ed;
39091                     var v = r.data[field];
39092                     ed.startEdit(this.view.getCell(row, col), v);
39093                     // combo's with 'displayField and name set
39094                     if (ed.field.displayField && ed.field.name) {
39095                         ed.field.el.dom.value = r.data[ed.field.name];
39096                     }
39097                     
39098                     
39099                 }).defer(50, this);
39100             }
39101         }
39102     },
39103         
39104     /**
39105      * Stops any active editing
39106      */
39107     stopEditing : function(){
39108         if(this.activeEditor){
39109             this.activeEditor.completeEdit();
39110         }
39111         this.activeEditor = null;
39112     },
39113         
39114          /**
39115      * Called to get grid's drag proxy text, by default returns this.ddText.
39116      * @return {String}
39117      */
39118     getDragDropText : function(){
39119         var count = this.selModel.getSelectedCell() ? 1 : 0;
39120         return String.format(this.ddText, count, count == 1 ? '' : 's');
39121     }
39122         
39123 });/*
39124  * Based on:
39125  * Ext JS Library 1.1.1
39126  * Copyright(c) 2006-2007, Ext JS, LLC.
39127  *
39128  * Originally Released Under LGPL - original licence link has changed is not relivant.
39129  *
39130  * Fork - LGPL
39131  * <script type="text/javascript">
39132  */
39133
39134 // private - not really -- you end up using it !
39135 // This is a support class used internally by the Grid components
39136
39137 /**
39138  * @class Roo.grid.GridEditor
39139  * @extends Roo.Editor
39140  * Class for creating and editable grid elements.
39141  * @param {Object} config any settings (must include field)
39142  */
39143 Roo.grid.GridEditor = function(field, config){
39144     if (!config && field.field) {
39145         config = field;
39146         field = Roo.factory(config.field, Roo.form);
39147     }
39148     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39149     field.monitorTab = false;
39150 };
39151
39152 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39153     
39154     /**
39155      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39156      */
39157     
39158     alignment: "tl-tl",
39159     autoSize: "width",
39160     hideEl : false,
39161     cls: "x-small-editor x-grid-editor",
39162     shim:false,
39163     shadow:"frame"
39164 });/*
39165  * Based on:
39166  * Ext JS Library 1.1.1
39167  * Copyright(c) 2006-2007, Ext JS, LLC.
39168  *
39169  * Originally Released Under LGPL - original licence link has changed is not relivant.
39170  *
39171  * Fork - LGPL
39172  * <script type="text/javascript">
39173  */
39174   
39175
39176   
39177 Roo.grid.PropertyRecord = Roo.data.Record.create([
39178     {name:'name',type:'string'},  'value'
39179 ]);
39180
39181
39182 Roo.grid.PropertyStore = function(grid, source){
39183     this.grid = grid;
39184     this.store = new Roo.data.Store({
39185         recordType : Roo.grid.PropertyRecord
39186     });
39187     this.store.on('update', this.onUpdate,  this);
39188     if(source){
39189         this.setSource(source);
39190     }
39191     Roo.grid.PropertyStore.superclass.constructor.call(this);
39192 };
39193
39194
39195
39196 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39197     setSource : function(o){
39198         this.source = o;
39199         this.store.removeAll();
39200         var data = [];
39201         for(var k in o){
39202             if(this.isEditableValue(o[k])){
39203                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39204             }
39205         }
39206         this.store.loadRecords({records: data}, {}, true);
39207     },
39208
39209     onUpdate : function(ds, record, type){
39210         if(type == Roo.data.Record.EDIT){
39211             var v = record.data['value'];
39212             var oldValue = record.modified['value'];
39213             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39214                 this.source[record.id] = v;
39215                 record.commit();
39216                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39217             }else{
39218                 record.reject();
39219             }
39220         }
39221     },
39222
39223     getProperty : function(row){
39224        return this.store.getAt(row);
39225     },
39226
39227     isEditableValue: function(val){
39228         if(val && val instanceof Date){
39229             return true;
39230         }else if(typeof val == 'object' || typeof val == 'function'){
39231             return false;
39232         }
39233         return true;
39234     },
39235
39236     setValue : function(prop, value){
39237         this.source[prop] = value;
39238         this.store.getById(prop).set('value', value);
39239     },
39240
39241     getSource : function(){
39242         return this.source;
39243     }
39244 });
39245
39246 Roo.grid.PropertyColumnModel = function(grid, store){
39247     this.grid = grid;
39248     var g = Roo.grid;
39249     g.PropertyColumnModel.superclass.constructor.call(this, [
39250         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39251         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39252     ]);
39253     this.store = store;
39254     this.bselect = Roo.DomHelper.append(document.body, {
39255         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39256             {tag: 'option', value: 'true', html: 'true'},
39257             {tag: 'option', value: 'false', html: 'false'}
39258         ]
39259     });
39260     Roo.id(this.bselect);
39261     var f = Roo.form;
39262     this.editors = {
39263         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39264         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39265         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39266         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39267         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39268     };
39269     this.renderCellDelegate = this.renderCell.createDelegate(this);
39270     this.renderPropDelegate = this.renderProp.createDelegate(this);
39271 };
39272
39273 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39274     
39275     
39276     nameText : 'Name',
39277     valueText : 'Value',
39278     
39279     dateFormat : 'm/j/Y',
39280     
39281     
39282     renderDate : function(dateVal){
39283         return dateVal.dateFormat(this.dateFormat);
39284     },
39285
39286     renderBool : function(bVal){
39287         return bVal ? 'true' : 'false';
39288     },
39289
39290     isCellEditable : function(colIndex, rowIndex){
39291         return colIndex == 1;
39292     },
39293
39294     getRenderer : function(col){
39295         return col == 1 ?
39296             this.renderCellDelegate : this.renderPropDelegate;
39297     },
39298
39299     renderProp : function(v){
39300         return this.getPropertyName(v);
39301     },
39302
39303     renderCell : function(val){
39304         var rv = val;
39305         if(val instanceof Date){
39306             rv = this.renderDate(val);
39307         }else if(typeof val == 'boolean'){
39308             rv = this.renderBool(val);
39309         }
39310         return Roo.util.Format.htmlEncode(rv);
39311     },
39312
39313     getPropertyName : function(name){
39314         var pn = this.grid.propertyNames;
39315         return pn && pn[name] ? pn[name] : name;
39316     },
39317
39318     getCellEditor : function(colIndex, rowIndex){
39319         var p = this.store.getProperty(rowIndex);
39320         var n = p.data['name'], val = p.data['value'];
39321         
39322         if(typeof(this.grid.customEditors[n]) == 'string'){
39323             return this.editors[this.grid.customEditors[n]];
39324         }
39325         if(typeof(this.grid.customEditors[n]) != 'undefined'){
39326             return this.grid.customEditors[n];
39327         }
39328         if(val instanceof Date){
39329             return this.editors['date'];
39330         }else if(typeof val == 'number'){
39331             return this.editors['number'];
39332         }else if(typeof val == 'boolean'){
39333             return this.editors['boolean'];
39334         }else{
39335             return this.editors['string'];
39336         }
39337     }
39338 });
39339
39340 /**
39341  * @class Roo.grid.PropertyGrid
39342  * @extends Roo.grid.EditorGrid
39343  * This class represents the  interface of a component based property grid control.
39344  * <br><br>Usage:<pre><code>
39345  var grid = new Roo.grid.PropertyGrid("my-container-id", {
39346       
39347  });
39348  // set any options
39349  grid.render();
39350  * </code></pre>
39351   
39352  * @constructor
39353  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
39354  * The container MUST have some type of size defined for the grid to fill. The container will be
39355  * automatically set to position relative if it isn't already.
39356  * @param {Object} config A config object that sets properties on this grid.
39357  */
39358 Roo.grid.PropertyGrid = function(container, config){
39359     config = config || {};
39360     var store = new Roo.grid.PropertyStore(this);
39361     this.store = store;
39362     var cm = new Roo.grid.PropertyColumnModel(this, store);
39363     store.store.sort('name', 'ASC');
39364     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
39365         ds: store.store,
39366         cm: cm,
39367         enableColLock:false,
39368         enableColumnMove:false,
39369         stripeRows:false,
39370         trackMouseOver: false,
39371         clicksToEdit:1
39372     }, config));
39373     this.getGridEl().addClass('x-props-grid');
39374     this.lastEditRow = null;
39375     this.on('columnresize', this.onColumnResize, this);
39376     this.addEvents({
39377          /**
39378              * @event beforepropertychange
39379              * Fires before a property changes (return false to stop?)
39380              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
39381              * @param {String} id Record Id
39382              * @param {String} newval New Value
39383          * @param {String} oldval Old Value
39384              */
39385         "beforepropertychange": true,
39386         /**
39387              * @event propertychange
39388              * Fires after a property changes
39389              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
39390              * @param {String} id Record Id
39391              * @param {String} newval New Value
39392          * @param {String} oldval Old Value
39393              */
39394         "propertychange": true
39395     });
39396     this.customEditors = this.customEditors || {};
39397 };
39398 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
39399     
39400      /**
39401      * @cfg {Object} customEditors map of colnames=> custom editors.
39402      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
39403      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
39404      * false disables editing of the field.
39405          */
39406     
39407       /**
39408      * @cfg {Object} propertyNames map of property Names to their displayed value
39409          */
39410     
39411     render : function(){
39412         Roo.grid.PropertyGrid.superclass.render.call(this);
39413         this.autoSize.defer(100, this);
39414     },
39415
39416     autoSize : function(){
39417         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
39418         if(this.view){
39419             this.view.fitColumns();
39420         }
39421     },
39422
39423     onColumnResize : function(){
39424         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
39425         this.autoSize();
39426     },
39427     /**
39428      * Sets the data for the Grid
39429      * accepts a Key => Value object of all the elements avaiable.
39430      * @param {Object} data  to appear in grid.
39431      */
39432     setSource : function(source){
39433         this.store.setSource(source);
39434         //this.autoSize();
39435     },
39436     /**
39437      * Gets all the data from the grid.
39438      * @return {Object} data  data stored in grid
39439      */
39440     getSource : function(){
39441         return this.store.getSource();
39442     }
39443 });/*
39444  * Based on:
39445  * Ext JS Library 1.1.1
39446  * Copyright(c) 2006-2007, Ext JS, LLC.
39447  *
39448  * Originally Released Under LGPL - original licence link has changed is not relivant.
39449  *
39450  * Fork - LGPL
39451  * <script type="text/javascript">
39452  */
39453  
39454 /**
39455  * @class Roo.LoadMask
39456  * A simple utility class for generically masking elements while loading data.  If the element being masked has
39457  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
39458  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
39459  * element's UpdateManager load indicator and will be destroyed after the initial load.
39460  * @constructor
39461  * Create a new LoadMask
39462  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
39463  * @param {Object} config The config object
39464  */
39465 Roo.LoadMask = function(el, config){
39466     this.el = Roo.get(el);
39467     Roo.apply(this, config);
39468     if(this.store){
39469         this.store.on('beforeload', this.onBeforeLoad, this);
39470         this.store.on('load', this.onLoad, this);
39471         this.store.on('loadexception', this.onLoadException, this);
39472         this.removeMask = false;
39473     }else{
39474         var um = this.el.getUpdateManager();
39475         um.showLoadIndicator = false; // disable the default indicator
39476         um.on('beforeupdate', this.onBeforeLoad, this);
39477         um.on('update', this.onLoad, this);
39478         um.on('failure', this.onLoad, this);
39479         this.removeMask = true;
39480     }
39481 };
39482
39483 Roo.LoadMask.prototype = {
39484     /**
39485      * @cfg {Boolean} removeMask
39486      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
39487      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
39488      */
39489     /**
39490      * @cfg {String} msg
39491      * The text to display in a centered loading message box (defaults to 'Loading...')
39492      */
39493     msg : 'Loading...',
39494     /**
39495      * @cfg {String} msgCls
39496      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
39497      */
39498     msgCls : 'x-mask-loading',
39499
39500     /**
39501      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
39502      * @type Boolean
39503      */
39504     disabled: false,
39505
39506     /**
39507      * Disables the mask to prevent it from being displayed
39508      */
39509     disable : function(){
39510        this.disabled = true;
39511     },
39512
39513     /**
39514      * Enables the mask so that it can be displayed
39515      */
39516     enable : function(){
39517         this.disabled = false;
39518     },
39519     
39520     onLoadException : function()
39521     {
39522         Roo.log(arguments);
39523         
39524         if (typeof(arguments[3]) != 'undefined') {
39525             Roo.MessageBox.alert("Error loading",arguments[3]);
39526         } 
39527         /*
39528         try {
39529             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39530                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39531             }   
39532         } catch(e) {
39533             
39534         }
39535         */
39536     
39537         
39538         
39539         this.el.unmask(this.removeMask);
39540     },
39541     // private
39542     onLoad : function()
39543     {
39544         this.el.unmask(this.removeMask);
39545     },
39546
39547     // private
39548     onBeforeLoad : function(){
39549         if(!this.disabled){
39550             this.el.mask(this.msg, this.msgCls);
39551         }
39552     },
39553
39554     // private
39555     destroy : function(){
39556         if(this.store){
39557             this.store.un('beforeload', this.onBeforeLoad, this);
39558             this.store.un('load', this.onLoad, this);
39559             this.store.un('loadexception', this.onLoadException, this);
39560         }else{
39561             var um = this.el.getUpdateManager();
39562             um.un('beforeupdate', this.onBeforeLoad, this);
39563             um.un('update', this.onLoad, this);
39564             um.un('failure', this.onLoad, this);
39565         }
39566     }
39567 };/*
39568  * Based on:
39569  * Ext JS Library 1.1.1
39570  * Copyright(c) 2006-2007, Ext JS, LLC.
39571  *
39572  * Originally Released Under LGPL - original licence link has changed is not relivant.
39573  *
39574  * Fork - LGPL
39575  * <script type="text/javascript">
39576  */
39577
39578
39579 /**
39580  * @class Roo.XTemplate
39581  * @extends Roo.Template
39582  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
39583 <pre><code>
39584 var t = new Roo.XTemplate(
39585         '&lt;select name="{name}"&gt;',
39586                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
39587         '&lt;/select&gt;'
39588 );
39589  
39590 // then append, applying the master template values
39591  </code></pre>
39592  *
39593  * Supported features:
39594  *
39595  *  Tags:
39596
39597 <pre><code>
39598       {a_variable} - output encoded.
39599       {a_variable.format:("Y-m-d")} - call a method on the variable
39600       {a_variable:raw} - unencoded output
39601       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
39602       {a_variable:this.method_on_template(...)} - call a method on the template object.
39603  
39604 </code></pre>
39605  *  The tpl tag:
39606 <pre><code>
39607         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
39608         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
39609         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
39610         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
39611   
39612         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
39613         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
39614 </code></pre>
39615  *      
39616  */
39617 Roo.XTemplate = function()
39618 {
39619     Roo.XTemplate.superclass.constructor.apply(this, arguments);
39620     if (this.html) {
39621         this.compile();
39622     }
39623 };
39624
39625
39626 Roo.extend(Roo.XTemplate, Roo.Template, {
39627
39628     /**
39629      * The various sub templates
39630      */
39631     tpls : false,
39632     /**
39633      *
39634      * basic tag replacing syntax
39635      * WORD:WORD()
39636      *
39637      * // you can fake an object call by doing this
39638      *  x.t:(test,tesT) 
39639      * 
39640      */
39641     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
39642
39643     /**
39644      * compile the template
39645      *
39646      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
39647      *
39648      */
39649     compile: function()
39650     {
39651         var s = this.html;
39652      
39653         s = ['<tpl>', s, '</tpl>'].join('');
39654     
39655         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
39656             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
39657             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
39658             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
39659             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
39660             m,
39661             id     = 0,
39662             tpls   = [];
39663     
39664         while(true == !!(m = s.match(re))){
39665             var forMatch   = m[0].match(nameRe),
39666                 ifMatch   = m[0].match(ifRe),
39667                 execMatch   = m[0].match(execRe),
39668                 namedMatch   = m[0].match(namedRe),
39669                 
39670                 exp  = null, 
39671                 fn   = null,
39672                 exec = null,
39673                 name = forMatch && forMatch[1] ? forMatch[1] : '';
39674                 
39675             if (ifMatch) {
39676                 // if - puts fn into test..
39677                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
39678                 if(exp){
39679                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
39680                 }
39681             }
39682             
39683             if (execMatch) {
39684                 // exec - calls a function... returns empty if true is  returned.
39685                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
39686                 if(exp){
39687                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
39688                 }
39689             }
39690             
39691             
39692             if (name) {
39693                 // for = 
39694                 switch(name){
39695                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
39696                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
39697                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
39698                 }
39699             }
39700             var uid = namedMatch ? namedMatch[1] : id;
39701             
39702             
39703             tpls.push({
39704                 id:     namedMatch ? namedMatch[1] : id,
39705                 target: name,
39706                 exec:   exec,
39707                 test:   fn,
39708                 body:   m[1] || ''
39709             });
39710             if (namedMatch) {
39711                 s = s.replace(m[0], '');
39712             } else { 
39713                 s = s.replace(m[0], '{xtpl'+ id + '}');
39714             }
39715             ++id;
39716         }
39717         this.tpls = [];
39718         for(var i = tpls.length-1; i >= 0; --i){
39719             this.compileTpl(tpls[i]);
39720             this.tpls[tpls[i].id] = tpls[i];
39721         }
39722         this.master = tpls[tpls.length-1];
39723         return this;
39724     },
39725     /**
39726      * same as applyTemplate, except it's done to one of the subTemplates
39727      * when using named templates, you can do:
39728      *
39729      * var str = pl.applySubTemplate('your-name', values);
39730      *
39731      * 
39732      * @param {Number} id of the template
39733      * @param {Object} values to apply to template
39734      * @param {Object} parent (normaly the instance of this object)
39735      */
39736     applySubTemplate : function(id, values, parent)
39737     {
39738         
39739         
39740         var t = this.tpls[id];
39741         
39742         
39743         try { 
39744             if(t.test && !t.test.call(this, values, parent)){
39745                 return '';
39746             }
39747         } catch(e) {
39748             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
39749             Roo.log(e.toString());
39750             Roo.log(t.test);
39751             return ''
39752         }
39753         try { 
39754             
39755             if(t.exec && t.exec.call(this, values, parent)){
39756                 return '';
39757             }
39758         } catch(e) {
39759             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
39760             Roo.log(e.toString());
39761             Roo.log(t.exec);
39762             return ''
39763         }
39764         try {
39765             var vs = t.target ? t.target.call(this, values, parent) : values;
39766             parent = t.target ? values : parent;
39767             if(t.target && vs instanceof Array){
39768                 var buf = [];
39769                 for(var i = 0, len = vs.length; i < len; i++){
39770                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
39771                 }
39772                 return buf.join('');
39773             }
39774             return t.compiled.call(this, vs, parent);
39775         } catch (e) {
39776             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
39777             Roo.log(e.toString());
39778             Roo.log(t.compiled);
39779             return '';
39780         }
39781     },
39782
39783     compileTpl : function(tpl)
39784     {
39785         var fm = Roo.util.Format;
39786         var useF = this.disableFormats !== true;
39787         var sep = Roo.isGecko ? "+" : ",";
39788         var undef = function(str) {
39789             Roo.log("Property not found :"  + str);
39790             return '';
39791         };
39792         
39793         var fn = function(m, name, format, args)
39794         {
39795             //Roo.log(arguments);
39796             args = args ? args.replace(/\\'/g,"'") : args;
39797             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
39798             if (typeof(format) == 'undefined') {
39799                 format= 'htmlEncode';
39800             }
39801             if (format == 'raw' ) {
39802                 format = false;
39803             }
39804             
39805             if(name.substr(0, 4) == 'xtpl'){
39806                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
39807             }
39808             
39809             // build an array of options to determine if value is undefined..
39810             
39811             // basically get 'xxxx.yyyy' then do
39812             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
39813             //    (function () { Roo.log("Property not found"); return ''; })() :
39814             //    ......
39815             
39816             var udef_ar = [];
39817             var lookfor = '';
39818             Roo.each(name.split('.'), function(st) {
39819                 lookfor += (lookfor.length ? '.': '') + st;
39820                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
39821             });
39822             
39823             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
39824             
39825             
39826             if(format && useF){
39827                 
39828                 args = args ? ',' + args : "";
39829                  
39830                 if(format.substr(0, 5) != "this."){
39831                     format = "fm." + format + '(';
39832                 }else{
39833                     format = 'this.call("'+ format.substr(5) + '", ';
39834                     args = ", values";
39835                 }
39836                 
39837                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
39838             }
39839              
39840             if (args.length) {
39841                 // called with xxyx.yuu:(test,test)
39842                 // change to ()
39843                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
39844             }
39845             // raw.. - :raw modifier..
39846             return "'"+ sep + udef_st  + name + ")"+sep+"'";
39847             
39848         };
39849         var body;
39850         // branched to use + in gecko and [].join() in others
39851         if(Roo.isGecko){
39852             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
39853                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
39854                     "';};};";
39855         }else{
39856             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
39857             body.push(tpl.body.replace(/(\r\n|\n)/g,
39858                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
39859             body.push("'].join('');};};");
39860             body = body.join('');
39861         }
39862         
39863         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
39864        
39865         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
39866         eval(body);
39867         
39868         return this;
39869     },
39870
39871     applyTemplate : function(values){
39872         return this.master.compiled.call(this, values, {});
39873         //var s = this.subs;
39874     },
39875
39876     apply : function(){
39877         return this.applyTemplate.apply(this, arguments);
39878     }
39879
39880  });
39881
39882 Roo.XTemplate.from = function(el){
39883     el = Roo.getDom(el);
39884     return new Roo.XTemplate(el.value || el.innerHTML);
39885 };/*
39886  * Original code for Roojs - LGPL
39887  * <script type="text/javascript">
39888  */
39889  
39890 /**
39891  * @class Roo.XComponent
39892  * A delayed Element creator...
39893  * Or a way to group chunks of interface together.
39894  * 
39895  * Mypart.xyx = new Roo.XComponent({
39896
39897     parent : 'Mypart.xyz', // empty == document.element.!!
39898     order : '001',
39899     name : 'xxxx'
39900     region : 'xxxx'
39901     disabled : function() {} 
39902      
39903     tree : function() { // return an tree of xtype declared components
39904         var MODULE = this;
39905         return 
39906         {
39907             xtype : 'NestedLayoutPanel',
39908             // technicall
39909         }
39910      ]
39911  *})
39912  *
39913  *
39914  * It can be used to build a big heiracy, with parent etc.
39915  * or you can just use this to render a single compoent to a dom element
39916  * MYPART.render(Roo.Element | String(id) | dom_element )
39917  * 
39918  * @extends Roo.util.Observable
39919  * @constructor
39920  * @param cfg {Object} configuration of component
39921  * 
39922  */
39923 Roo.XComponent = function(cfg) {
39924     Roo.apply(this, cfg);
39925     this.addEvents({ 
39926         /**
39927              * @event built
39928              * Fires when this the componnt is built
39929              * @param {Roo.XComponent} c the component
39930              */
39931         'built' : true
39932         
39933     });
39934     this.region = this.region || 'center'; // default..
39935     Roo.XComponent.register(this);
39936     this.modules = false;
39937     this.el = false; // where the layout goes..
39938     
39939     
39940 }
39941 Roo.extend(Roo.XComponent, Roo.util.Observable, {
39942     /**
39943      * @property el
39944      * The created element (with Roo.factory())
39945      * @type {Roo.Layout}
39946      */
39947     el  : false,
39948     
39949     /**
39950      * @property el
39951      * for BC  - use el in new code
39952      * @type {Roo.Layout}
39953      */
39954     panel : false,
39955     
39956     /**
39957      * @property layout
39958      * for BC  - use el in new code
39959      * @type {Roo.Layout}
39960      */
39961     layout : false,
39962     
39963      /**
39964      * @cfg {Function|boolean} disabled
39965      * If this module is disabled by some rule, return true from the funtion
39966      */
39967     disabled : false,
39968     
39969     /**
39970      * @cfg {String} parent 
39971      * Name of parent element which it get xtype added to..
39972      */
39973     parent: false,
39974     
39975     /**
39976      * @cfg {String} order
39977      * Used to set the order in which elements are created (usefull for multiple tabs)
39978      */
39979     
39980     order : false,
39981     /**
39982      * @cfg {String} name
39983      * String to display while loading.
39984      */
39985     name : false,
39986     /**
39987      * @cfg {String} region
39988      * Region to render component to (defaults to center)
39989      */
39990     region : 'center',
39991     
39992     /**
39993      * @cfg {Array} items
39994      * A single item array - the first element is the root of the tree..
39995      * It's done this way to stay compatible with the Xtype system...
39996      */
39997     items : false,
39998     
39999     /**
40000      * @property _tree
40001      * The method that retuns the tree of parts that make up this compoennt 
40002      * @type {function}
40003      */
40004     _tree  : false,
40005     
40006      /**
40007      * render
40008      * render element to dom or tree
40009      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40010      */
40011     
40012     render : function(el)
40013     {
40014         
40015         el = el || false;
40016         var hp = this.parent ? 1 : 0;
40017         
40018         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40019             // if parent is a '#.....' string, then let's use that..
40020             var ename = this.parent.substr(1)
40021             this.parent = false;
40022             el = Roo.get(ename);
40023             if (!el) {
40024                 Roo.log("Warning - element can not be found :#" + ename );
40025                 return;
40026             }
40027         }
40028         
40029         
40030         if (!this.parent) {
40031             
40032             el = el ? Roo.get(el) : false;      
40033             
40034             // it's a top level one..
40035             this.parent =  {
40036                 el : new Roo.BorderLayout(el || document.body, {
40037                 
40038                      center: {
40039                          titlebar: false,
40040                          autoScroll:false,
40041                          closeOnTab: true,
40042                          tabPosition: 'top',
40043                           //resizeTabs: true,
40044                          alwaysShowTabs: el && hp? false :  true,
40045                          hideTabs: el || !hp ? true :  false,
40046                          minTabWidth: 140
40047                      }
40048                  })
40049             }
40050         }
40051         
40052                 if (!this.parent.el) {
40053                         // probably an old style ctor, which has been disabled.
40054                         return;
40055                         
40056                 }
40057                 // The 'tree' method is  '_tree now' 
40058             
40059         var tree = this._tree ? this._tree() : this.tree();
40060         tree.region = tree.region || this.region;
40061         this.el = this.parent.el.addxtype(tree);
40062         this.fireEvent('built', this);
40063         
40064         this.panel = this.el;
40065         this.layout = this.panel.layout;
40066                 this.parentLayout = this.parent.layout  || false;  
40067          
40068     }
40069     
40070 });
40071
40072 Roo.apply(Roo.XComponent, {
40073     /**
40074      * @property  hideProgress
40075      * true to disable the building progress bar.. usefull on single page renders.
40076      * @type Boolean
40077      */
40078     hideProgress : false,
40079     /**
40080      * @property  buildCompleted
40081      * True when the builder has completed building the interface.
40082      * @type Boolean
40083      */
40084     buildCompleted : false,
40085      
40086     /**
40087      * @property  topModule
40088      * the upper most module - uses document.element as it's constructor.
40089      * @type Object
40090      */
40091      
40092     topModule  : false,
40093       
40094     /**
40095      * @property  modules
40096      * array of modules to be created by registration system.
40097      * @type {Array} of Roo.XComponent
40098      */
40099     
40100     modules : [],
40101     /**
40102      * @property  elmodules
40103      * array of modules to be created by which use #ID 
40104      * @type {Array} of Roo.XComponent
40105      */
40106      
40107     elmodules : [],
40108
40109     
40110     /**
40111      * Register components to be built later.
40112      *
40113      * This solves the following issues
40114      * - Building is not done on page load, but after an authentication process has occured.
40115      * - Interface elements are registered on page load
40116      * - Parent Interface elements may not be loaded before child, so this handles that..
40117      * 
40118      *
40119      * example:
40120      * 
40121      * MyApp.register({
40122           order : '000001',
40123           module : 'Pman.Tab.projectMgr',
40124           region : 'center',
40125           parent : 'Pman.layout',
40126           disabled : false,  // or use a function..
40127         })
40128      
40129      * * @param {Object} details about module
40130      */
40131     register : function(obj) {
40132                 
40133         Roo.XComponent.event.fireEvent('register', obj);
40134         switch(typeof(obj.disabled) ) {
40135                 
40136             case 'undefined':
40137                 break;
40138             
40139             case 'function':
40140                 if ( obj.disabled() ) {
40141                         return;
40142                 }
40143                 break;
40144             
40145             default:
40146                 if (obj.disabled) {
40147                         return;
40148                 }
40149                 break;
40150         }
40151                 
40152         this.modules.push(obj);
40153          
40154     },
40155     /**
40156      * convert a string to an object..
40157      * eg. 'AAA.BBB' -> finds AAA.BBB
40158
40159      */
40160     
40161     toObject : function(str)
40162     {
40163         if (!str || typeof(str) == 'object') {
40164             return str;
40165         }
40166         if (str.substring(0,1) == '#') {
40167             return str;
40168         }
40169
40170         var ar = str.split('.');
40171         var rt, o;
40172         rt = ar.shift();
40173             /** eval:var:o */
40174         try {
40175             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40176         } catch (e) {
40177             throw "Module not found : " + str;
40178         }
40179         
40180         if (o === false) {
40181             throw "Module not found : " + str;
40182         }
40183         Roo.each(ar, function(e) {
40184             if (typeof(o[e]) == 'undefined') {
40185                 throw "Module not found : " + str;
40186             }
40187             o = o[e];
40188         });
40189         
40190         return o;
40191         
40192     },
40193     
40194     
40195     /**
40196      * move modules into their correct place in the tree..
40197      * 
40198      */
40199     preBuild : function ()
40200     {
40201         var _t = this;
40202         Roo.each(this.modules , function (obj)
40203         {
40204             Roo.XComponent.event.fireEvent('beforebuild', obj);
40205             
40206             var opar = obj.parent;
40207             try { 
40208                 obj.parent = this.toObject(opar);
40209             } catch(e) {
40210                 Roo.log("parent:toObject failed: " + e.toString());
40211                 return;
40212             }
40213             
40214             if (!obj.parent) {
40215                 Roo.debug && Roo.log("GOT top level module");
40216                 Roo.debug && Roo.log(obj);
40217                 obj.modules = new Roo.util.MixedCollection(false, 
40218                     function(o) { return o.order + '' }
40219                 );
40220                 this.topModule = obj;
40221                 return;
40222             }
40223                         // parent is a string (usually a dom element name..)
40224             if (typeof(obj.parent) == 'string') {
40225                 this.elmodules.push(obj);
40226                 return;
40227             }
40228             if (obj.parent.constructor != Roo.XComponent) {
40229                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40230             }
40231             if (!obj.parent.modules) {
40232                 obj.parent.modules = new Roo.util.MixedCollection(false, 
40233                     function(o) { return o.order + '' }
40234                 );
40235             }
40236             if (obj.parent.disabled) {
40237                 obj.disabled = true;
40238             }
40239             obj.parent.modules.add(obj);
40240         }, this);
40241     },
40242     
40243      /**
40244      * make a list of modules to build.
40245      * @return {Array} list of modules. 
40246      */ 
40247     
40248     buildOrder : function()
40249     {
40250         var _this = this;
40251         var cmp = function(a,b) {   
40252             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40253         };
40254         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40255             throw "No top level modules to build";
40256         }
40257         
40258         // make a flat list in order of modules to build.
40259         var mods = this.topModule ? [ this.topModule ] : [];
40260                 
40261         
40262         // elmodules (is a list of DOM based modules )
40263         Roo.each(this.elmodules, function(e) {
40264             mods.push(e);
40265             if (!this.topModule &&
40266                 typeof(e.parent) == 'string' &&
40267                 e.parent.substring(0,1) == '#' &&
40268                 Roo.get(e.parent.substr(1))
40269                ) {
40270                 
40271                 _this.topModule = e;
40272             }
40273             
40274         });
40275
40276         
40277         // add modules to their parents..
40278         var addMod = function(m) {
40279             Roo.debug && Roo.log("build Order: add: " + m.name);
40280                 
40281             mods.push(m);
40282             if (m.modules && !m.disabled) {
40283                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40284                 m.modules.keySort('ASC',  cmp );
40285                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40286     
40287                 m.modules.each(addMod);
40288             } else {
40289                 Roo.debug && Roo.log("build Order: no child modules");
40290             }
40291             // not sure if this is used any more..
40292             if (m.finalize) {
40293                 m.finalize.name = m.name + " (clean up) ";
40294                 mods.push(m.finalize);
40295             }
40296             
40297         }
40298         if (this.topModule && this.topModule.modules) { 
40299             this.topModule.modules.keySort('ASC',  cmp );
40300             this.topModule.modules.each(addMod);
40301         } 
40302         return mods;
40303     },
40304     
40305      /**
40306      * Build the registered modules.
40307      * @param {Object} parent element.
40308      * @param {Function} optional method to call after module has been added.
40309      * 
40310      */ 
40311    
40312     build : function() 
40313     {
40314         
40315         this.preBuild();
40316         var mods = this.buildOrder();
40317       
40318         //this.allmods = mods;
40319         //Roo.debug && Roo.log(mods);
40320         //return;
40321         if (!mods.length) { // should not happen
40322             throw "NO modules!!!";
40323         }
40324         
40325         
40326         var msg = "Building Interface...";
40327         // flash it up as modal - so we store the mask!?
40328         if (!this.hideProgress) {
40329             Roo.MessageBox.show({ title: 'loading' });
40330             Roo.MessageBox.show({
40331                title: "Please wait...",
40332                msg: msg,
40333                width:450,
40334                progress:true,
40335                closable:false,
40336                modal: false
40337               
40338             });
40339         }
40340         var total = mods.length;
40341         
40342         var _this = this;
40343         var progressRun = function() {
40344             if (!mods.length) {
40345                 Roo.debug && Roo.log('hide?');
40346                 if (!this.hideProgress) {
40347                     Roo.MessageBox.hide();
40348                 }
40349                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
40350                 
40351                 // THE END...
40352                 return false;   
40353             }
40354             
40355             var m = mods.shift();
40356             
40357             
40358             Roo.debug && Roo.log(m);
40359             // not sure if this is supported any more.. - modules that are are just function
40360             if (typeof(m) == 'function') { 
40361                 m.call(this);
40362                 return progressRun.defer(10, _this);
40363             } 
40364             
40365             
40366             msg = "Building Interface " + (total  - mods.length) + 
40367                     " of " + total + 
40368                     (m.name ? (' - ' + m.name) : '');
40369                         Roo.debug && Roo.log(msg);
40370             if (!this.hideProgress) { 
40371                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
40372             }
40373             
40374          
40375             // is the module disabled?
40376             var disabled = (typeof(m.disabled) == 'function') ?
40377                 m.disabled.call(m.module.disabled) : m.disabled;    
40378             
40379             
40380             if (disabled) {
40381                 return progressRun(); // we do not update the display!
40382             }
40383             
40384             // now build 
40385             
40386                         
40387                         
40388             m.render();
40389             // it's 10 on top level, and 1 on others??? why...
40390             return progressRun.defer(10, _this);
40391              
40392         }
40393         progressRun.defer(1, _this);
40394      
40395         
40396         
40397     },
40398         
40399         
40400         /**
40401          * Event Object.
40402          *
40403          *
40404          */
40405         event: false, 
40406     /**
40407          * wrapper for event.on - aliased later..  
40408          * Typically use to register a event handler for register:
40409          *
40410          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
40411          *
40412          */
40413     on : false
40414    
40415     
40416     
40417 });
40418
40419 Roo.XComponent.event = new Roo.util.Observable({
40420                 events : { 
40421                         /**
40422                          * @event register
40423                          * Fires when an Component is registered,
40424                          * set the disable property on the Component to stop registration.
40425                          * @param {Roo.XComponent} c the component being registerd.
40426                          * 
40427                          */
40428                         'register' : true,
40429             /**
40430                          * @event beforebuild
40431                          * Fires before each Component is built
40432                          * can be used to apply permissions.
40433                          * @param {Roo.XComponent} c the component being registerd.
40434                          * 
40435                          */
40436                         'beforebuild' : true,
40437                         /**
40438                          * @event buildcomplete
40439                          * Fires on the top level element when all elements have been built
40440                          * @param {Roo.XComponent} the top level component.
40441                          */
40442                         'buildcomplete' : true
40443                         
40444                 }
40445 });
40446
40447 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
40448