roojs-core.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         if (!Roo.isTouch) {
569             Event.on(this.id, "mousedown", this.handleMouseDown, this);
570         }
571         Event.on(this.id, "touchstart", this.handleMouseDown, this);
572         // Event.on(this.id, "selectstart", Event.preventDefault);
573     },
574
575     /**
576      * Initializes Targeting functionality only... the object does not
577      * get a mousedown handler.
578      * @method initTarget
579      * @param id the id of the linked element
580      * @param {String} sGroup the group of related items
581      * @param {object} config configuration attributes
582      */
583     initTarget: function(id, sGroup, config) {
584
585         // configuration attributes
586         this.config = config || {};
587
588         // create a local reference to the drag and drop manager
589         this.DDM = Roo.dd.DDM;
590         // initialize the groups array
591         this.groups = {};
592
593         // assume that we have an element reference instead of an id if the
594         // parameter is not a string
595         if (typeof id !== "string") {
596             id = Roo.id(id);
597         }
598
599         // set the id
600         this.id = id;
601
602         // add to an interaction group
603         this.addToGroup((sGroup) ? sGroup : "default");
604
605         // We don't want to register this as the handle with the manager
606         // so we just set the id rather than calling the setter.
607         this.handleElId = id;
608
609         // the linked element is the element that gets dragged by default
610         this.setDragElId(id);
611
612         // by default, clicked anchors will not start drag operations.
613         this.invalidHandleTypes = { A: "A" };
614         this.invalidHandleIds = {};
615         this.invalidHandleClasses = [];
616
617         this.applyConfig();
618
619         this.handleOnAvailable();
620     },
621
622     /**
623      * Applies the configuration parameters that were passed into the constructor.
624      * This is supposed to happen at each level through the inheritance chain.  So
625      * a DDProxy implentation will execute apply config on DDProxy, DD, and
626      * DragDrop in order to get all of the parameters that are available in
627      * each object.
628      * @method applyConfig
629      */
630     applyConfig: function() {
631
632         // configurable properties:
633         //    padding, isTarget, maintainOffset, primaryButtonOnly
634         this.padding           = this.config.padding || [0, 0, 0, 0];
635         this.isTarget          = (this.config.isTarget !== false);
636         this.maintainOffset    = (this.config.maintainOffset);
637         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
638
639     },
640
641     /**
642      * Executed when the linked element is available
643      * @method handleOnAvailable
644      * @private
645      */
646     handleOnAvailable: function() {
647         this.available = true;
648         this.resetConstraints();
649         this.onAvailable();
650     },
651
652      /**
653      * Configures the padding for the target zone in px.  Effectively expands
654      * (or reduces) the virtual object size for targeting calculations.
655      * Supports css-style shorthand; if only one parameter is passed, all sides
656      * will have that padding, and if only two are passed, the top and bottom
657      * will have the first param, the left and right the second.
658      * @method setPadding
659      * @param {int} iTop    Top pad
660      * @param {int} iRight  Right pad
661      * @param {int} iBot    Bot pad
662      * @param {int} iLeft   Left pad
663      */
664     setPadding: function(iTop, iRight, iBot, iLeft) {
665         // this.padding = [iLeft, iRight, iTop, iBot];
666         if (!iRight && 0 !== iRight) {
667             this.padding = [iTop, iTop, iTop, iTop];
668         } else if (!iBot && 0 !== iBot) {
669             this.padding = [iTop, iRight, iTop, iRight];
670         } else {
671             this.padding = [iTop, iRight, iBot, iLeft];
672         }
673     },
674
675     /**
676      * Stores the initial placement of the linked element.
677      * @method setInitialPosition
678      * @param {int} diffX   the X offset, default 0
679      * @param {int} diffY   the Y offset, default 0
680      */
681     setInitPosition: function(diffX, diffY) {
682         var el = this.getEl();
683
684         if (!this.DDM.verifyEl(el)) {
685             return;
686         }
687
688         var dx = diffX || 0;
689         var dy = diffY || 0;
690
691         var p = Dom.getXY( el );
692
693         this.initPageX = p[0] - dx;
694         this.initPageY = p[1] - dy;
695
696         this.lastPageX = p[0];
697         this.lastPageY = p[1];
698
699
700         this.setStartPosition(p);
701     },
702
703     /**
704      * Sets the start position of the element.  This is set when the obj
705      * is initialized, the reset when a drag is started.
706      * @method setStartPosition
707      * @param pos current position (from previous lookup)
708      * @private
709      */
710     setStartPosition: function(pos) {
711         var p = pos || Dom.getXY( this.getEl() );
712         this.deltaSetXY = null;
713
714         this.startPageX = p[0];
715         this.startPageY = p[1];
716     },
717
718     /**
719      * Add this instance to a group of related drag/drop objects.  All
720      * instances belong to at least one group, and can belong to as many
721      * groups as needed.
722      * @method addToGroup
723      * @param sGroup {string} the name of the group
724      */
725     addToGroup: function(sGroup) {
726         this.groups[sGroup] = true;
727         this.DDM.regDragDrop(this, sGroup);
728     },
729
730     /**
731      * Remove's this instance from the supplied interaction group
732      * @method removeFromGroup
733      * @param {string}  sGroup  The group to drop
734      */
735     removeFromGroup: function(sGroup) {
736         if (this.groups[sGroup]) {
737             delete this.groups[sGroup];
738         }
739
740         this.DDM.removeDDFromGroup(this, sGroup);
741     },
742
743     /**
744      * Allows you to specify that an element other than the linked element
745      * will be moved with the cursor during a drag
746      * @method setDragElId
747      * @param id {string} the id of the element that will be used to initiate the drag
748      */
749     setDragElId: function(id) {
750         this.dragElId = id;
751     },
752
753     /**
754      * Allows you to specify a child of the linked element that should be
755      * used to initiate the drag operation.  An example of this would be if
756      * you have a content div with text and links.  Clicking anywhere in the
757      * content area would normally start the drag operation.  Use this method
758      * to specify that an element inside of the content div is the element
759      * that starts the drag operation.
760      * @method setHandleElId
761      * @param id {string} the id of the element that will be used to
762      * initiate the drag.
763      */
764     setHandleElId: function(id) {
765         if (typeof id !== "string") {
766             id = Roo.id(id);
767         }
768         this.handleElId = id;
769         this.DDM.regHandle(this.id, id);
770     },
771
772     /**
773      * Allows you to set an element outside of the linked element as a drag
774      * handle
775      * @method setOuterHandleElId
776      * @param id the id of the element that will be used to initiate the drag
777      */
778     setOuterHandleElId: function(id) {
779         if (typeof id !== "string") {
780             id = Roo.id(id);
781         }
782         Event.on(id, "mousedown",
783                 this.handleMouseDown, this);
784         this.setHandleElId(id);
785
786         this.hasOuterHandles = true;
787     },
788
789     /**
790      * Remove all drag and drop hooks for this element
791      * @method unreg
792      */
793     unreg: function() {
794         Event.un(this.id, "mousedown",
795                 this.handleMouseDown);
796         Event.un(this.id, "touchstart",
797                 this.handleMouseDown);
798         this._domRef = null;
799         this.DDM._remove(this);
800     },
801
802     destroy : function(){
803         this.unreg();
804     },
805
806     /**
807      * Returns true if this instance is locked, or the drag drop mgr is locked
808      * (meaning that all drag/drop is disabled on the page.)
809      * @method isLocked
810      * @return {boolean} true if this obj or all drag/drop is locked, else
811      * false
812      */
813     isLocked: function() {
814         return (this.DDM.isLocked() || this.locked);
815     },
816
817     /**
818      * Fired when this object is clicked
819      * @method handleMouseDown
820      * @param {Event} e
821      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
822      * @private
823      */
824     handleMouseDown: function(e, oDD){
825      
826         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
827             //Roo.log('not touch/ button !=0');
828             return;
829         }
830         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
831             return; // double touch..
832         }
833         
834
835         if (this.isLocked()) {
836             //Roo.log('locked');
837             return;
838         }
839
840         this.DDM.refreshCache(this.groups);
841 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
842         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
843         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
844             //Roo.log('no outer handes or not over target');
845                 // do nothing.
846         } else {
847 //            Roo.log('check validator');
848             if (this.clickValidator(e)) {
849 //                Roo.log('validate success');
850                 // set the initial element position
851                 this.setStartPosition();
852
853
854                 this.b4MouseDown(e);
855                 this.onMouseDown(e);
856
857                 this.DDM.handleMouseDown(e, this);
858
859                 this.DDM.stopEvent(e);
860             } else {
861
862
863             }
864         }
865     },
866
867     clickValidator: function(e) {
868         var target = e.getTarget();
869         return ( this.isValidHandleChild(target) &&
870                     (this.id == this.handleElId ||
871                         this.DDM.handleWasClicked(target, this.id)) );
872     },
873
874     /**
875      * Allows you to specify a tag name that should not start a drag operation
876      * when clicked.  This is designed to facilitate embedding links within a
877      * drag handle that do something other than start the drag.
878      * @method addInvalidHandleType
879      * @param {string} tagName the type of element to exclude
880      */
881     addInvalidHandleType: function(tagName) {
882         var type = tagName.toUpperCase();
883         this.invalidHandleTypes[type] = type;
884     },
885
886     /**
887      * Lets you to specify an element id for a child of a drag handle
888      * that should not initiate a drag
889      * @method addInvalidHandleId
890      * @param {string} id the element id of the element you wish to ignore
891      */
892     addInvalidHandleId: function(id) {
893         if (typeof id !== "string") {
894             id = Roo.id(id);
895         }
896         this.invalidHandleIds[id] = id;
897     },
898
899     /**
900      * Lets you specify a css class of elements that will not initiate a drag
901      * @method addInvalidHandleClass
902      * @param {string} cssClass the class of the elements you wish to ignore
903      */
904     addInvalidHandleClass: function(cssClass) {
905         this.invalidHandleClasses.push(cssClass);
906     },
907
908     /**
909      * Unsets an excluded tag name set by addInvalidHandleType
910      * @method removeInvalidHandleType
911      * @param {string} tagName the type of element to unexclude
912      */
913     removeInvalidHandleType: function(tagName) {
914         var type = tagName.toUpperCase();
915         // this.invalidHandleTypes[type] = null;
916         delete this.invalidHandleTypes[type];
917     },
918
919     /**
920      * Unsets an invalid handle id
921      * @method removeInvalidHandleId
922      * @param {string} id the id of the element to re-enable
923      */
924     removeInvalidHandleId: function(id) {
925         if (typeof id !== "string") {
926             id = Roo.id(id);
927         }
928         delete this.invalidHandleIds[id];
929     },
930
931     /**
932      * Unsets an invalid css class
933      * @method removeInvalidHandleClass
934      * @param {string} cssClass the class of the element(s) you wish to
935      * re-enable
936      */
937     removeInvalidHandleClass: function(cssClass) {
938         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
939             if (this.invalidHandleClasses[i] == cssClass) {
940                 delete this.invalidHandleClasses[i];
941             }
942         }
943     },
944
945     /**
946      * Checks the tag exclusion list to see if this click should be ignored
947      * @method isValidHandleChild
948      * @param {HTMLElement} node the HTMLElement to evaluate
949      * @return {boolean} true if this is a valid tag type, false if not
950      */
951     isValidHandleChild: function(node) {
952
953         var valid = true;
954         // var n = (node.nodeName == "#text") ? node.parentNode : node;
955         var nodeName;
956         try {
957             nodeName = node.nodeName.toUpperCase();
958         } catch(e) {
959             nodeName = node.nodeName;
960         }
961         valid = valid && !this.invalidHandleTypes[nodeName];
962         valid = valid && !this.invalidHandleIds[node.id];
963
964         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
965             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
966         }
967
968
969         return valid;
970
971     },
972
973     /**
974      * Create the array of horizontal tick marks if an interval was specified
975      * in setXConstraint().
976      * @method setXTicks
977      * @private
978      */
979     setXTicks: function(iStartX, iTickSize) {
980         this.xTicks = [];
981         this.xTickSize = iTickSize;
982
983         var tickMap = {};
984
985         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
986             if (!tickMap[i]) {
987                 this.xTicks[this.xTicks.length] = i;
988                 tickMap[i] = true;
989             }
990         }
991
992         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
993             if (!tickMap[i]) {
994                 this.xTicks[this.xTicks.length] = i;
995                 tickMap[i] = true;
996             }
997         }
998
999         this.xTicks.sort(this.DDM.numericSort) ;
1000     },
1001
1002     /**
1003      * Create the array of vertical tick marks if an interval was specified in
1004      * setYConstraint().
1005      * @method setYTicks
1006      * @private
1007      */
1008     setYTicks: function(iStartY, iTickSize) {
1009         this.yTicks = [];
1010         this.yTickSize = iTickSize;
1011
1012         var tickMap = {};
1013
1014         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1015             if (!tickMap[i]) {
1016                 this.yTicks[this.yTicks.length] = i;
1017                 tickMap[i] = true;
1018             }
1019         }
1020
1021         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1022             if (!tickMap[i]) {
1023                 this.yTicks[this.yTicks.length] = i;
1024                 tickMap[i] = true;
1025             }
1026         }
1027
1028         this.yTicks.sort(this.DDM.numericSort) ;
1029     },
1030
1031     /**
1032      * By default, the element can be dragged any place on the screen.  Use
1033      * this method to limit the horizontal travel of the element.  Pass in
1034      * 0,0 for the parameters if you want to lock the drag to the y axis.
1035      * @method setXConstraint
1036      * @param {int} iLeft the number of pixels the element can move to the left
1037      * @param {int} iRight the number of pixels the element can move to the
1038      * right
1039      * @param {int} iTickSize optional parameter for specifying that the
1040      * element
1041      * should move iTickSize pixels at a time.
1042      */
1043     setXConstraint: function(iLeft, iRight, iTickSize) {
1044         this.leftConstraint = iLeft;
1045         this.rightConstraint = iRight;
1046
1047         this.minX = this.initPageX - iLeft;
1048         this.maxX = this.initPageX + iRight;
1049         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1050
1051         this.constrainX = true;
1052     },
1053
1054     /**
1055      * Clears any constraints applied to this instance.  Also clears ticks
1056      * since they can't exist independent of a constraint at this time.
1057      * @method clearConstraints
1058      */
1059     clearConstraints: function() {
1060         this.constrainX = false;
1061         this.constrainY = false;
1062         this.clearTicks();
1063     },
1064
1065     /**
1066      * Clears any tick interval defined for this instance
1067      * @method clearTicks
1068      */
1069     clearTicks: function() {
1070         this.xTicks = null;
1071         this.yTicks = null;
1072         this.xTickSize = 0;
1073         this.yTickSize = 0;
1074     },
1075
1076     /**
1077      * By default, the element can be dragged any place on the screen.  Set
1078      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1079      * parameters if you want to lock the drag to the x axis.
1080      * @method setYConstraint
1081      * @param {int} iUp the number of pixels the element can move up
1082      * @param {int} iDown the number of pixels the element can move down
1083      * @param {int} iTickSize optional parameter for specifying that the
1084      * element should move iTickSize pixels at a time.
1085      */
1086     setYConstraint: function(iUp, iDown, iTickSize) {
1087         this.topConstraint = iUp;
1088         this.bottomConstraint = iDown;
1089
1090         this.minY = this.initPageY - iUp;
1091         this.maxY = this.initPageY + iDown;
1092         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1093
1094         this.constrainY = true;
1095
1096     },
1097
1098     /**
1099      * resetConstraints must be called if you manually reposition a dd element.
1100      * @method resetConstraints
1101      * @param {boolean} maintainOffset
1102      */
1103     resetConstraints: function() {
1104
1105
1106         // Maintain offsets if necessary
1107         if (this.initPageX || this.initPageX === 0) {
1108             // figure out how much this thing has moved
1109             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1110             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1111
1112             this.setInitPosition(dx, dy);
1113
1114         // This is the first time we have detected the element's position
1115         } else {
1116             this.setInitPosition();
1117         }
1118
1119         if (this.constrainX) {
1120             this.setXConstraint( this.leftConstraint,
1121                                  this.rightConstraint,
1122                                  this.xTickSize        );
1123         }
1124
1125         if (this.constrainY) {
1126             this.setYConstraint( this.topConstraint,
1127                                  this.bottomConstraint,
1128                                  this.yTickSize         );
1129         }
1130     },
1131
1132     /**
1133      * Normally the drag element is moved pixel by pixel, but we can specify
1134      * that it move a number of pixels at a time.  This method resolves the
1135      * location when we have it set up like this.
1136      * @method getTick
1137      * @param {int} val where we want to place the object
1138      * @param {int[]} tickArray sorted array of valid points
1139      * @return {int} the closest tick
1140      * @private
1141      */
1142     getTick: function(val, tickArray) {
1143
1144         if (!tickArray) {
1145             // If tick interval is not defined, it is effectively 1 pixel,
1146             // so we return the value passed to us.
1147             return val;
1148         } else if (tickArray[0] >= val) {
1149             // The value is lower than the first tick, so we return the first
1150             // tick.
1151             return tickArray[0];
1152         } else {
1153             for (var i=0, len=tickArray.length; i<len; ++i) {
1154                 var next = i + 1;
1155                 if (tickArray[next] && tickArray[next] >= val) {
1156                     var diff1 = val - tickArray[i];
1157                     var diff2 = tickArray[next] - val;
1158                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1159                 }
1160             }
1161
1162             // The value is larger than the last tick, so we return the last
1163             // tick.
1164             return tickArray[tickArray.length - 1];
1165         }
1166     },
1167
1168     /**
1169      * toString method
1170      * @method toString
1171      * @return {string} string representation of the dd obj
1172      */
1173     toString: function() {
1174         return ("DragDrop " + this.id);
1175     }
1176
1177 });
1178
1179 })();
1180 /*
1181  * Based on:
1182  * Ext JS Library 1.1.1
1183  * Copyright(c) 2006-2007, Ext JS, LLC.
1184  *
1185  * Originally Released Under LGPL - original licence link has changed is not relivant.
1186  *
1187  * Fork - LGPL
1188  * <script type="text/javascript">
1189  */
1190
1191
1192 /**
1193  * The drag and drop utility provides a framework for building drag and drop
1194  * applications.  In addition to enabling drag and drop for specific elements,
1195  * the drag and drop elements are tracked by the manager class, and the
1196  * interactions between the various elements are tracked during the drag and
1197  * the implementing code is notified about these important moments.
1198  */
1199
1200 // Only load the library once.  Rewriting the manager class would orphan
1201 // existing drag and drop instances.
1202 if (!Roo.dd.DragDropMgr) {
1203
1204 /**
1205  * @class Roo.dd.DragDropMgr
1206  * DragDropMgr is a singleton that tracks the element interaction for
1207  * all DragDrop items in the window.  Generally, you will not call
1208  * this class directly, but it does have helper methods that could
1209  * be useful in your DragDrop implementations.
1210  * @singleton
1211  */
1212 Roo.dd.DragDropMgr = function() {
1213
1214     var Event = Roo.EventManager;
1215
1216     return {
1217
1218         /**
1219          * Two dimensional Array of registered DragDrop objects.  The first
1220          * dimension is the DragDrop item group, the second the DragDrop
1221          * object.
1222          * @property ids
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         ids: {},
1228
1229         /**
1230          * Array of element ids defined as drag handles.  Used to determine
1231          * if the element that generated the mousedown event is actually the
1232          * handle and not the html element itself.
1233          * @property handleIds
1234          * @type {string: string}
1235          * @private
1236          * @static
1237          */
1238         handleIds: {},
1239
1240         /**
1241          * the DragDrop object that is currently being dragged
1242          * @property dragCurrent
1243          * @type DragDrop
1244          * @private
1245          * @static
1246          **/
1247         dragCurrent: null,
1248
1249         /**
1250          * the DragDrop object(s) that are being hovered over
1251          * @property dragOvers
1252          * @type Array
1253          * @private
1254          * @static
1255          */
1256         dragOvers: {},
1257
1258         /**
1259          * the X distance between the cursor and the object being dragged
1260          * @property deltaX
1261          * @type int
1262          * @private
1263          * @static
1264          */
1265         deltaX: 0,
1266
1267         /**
1268          * the Y distance between the cursor and the object being dragged
1269          * @property deltaY
1270          * @type int
1271          * @private
1272          * @static
1273          */
1274         deltaY: 0,
1275
1276         /**
1277          * Flag to determine if we should prevent the default behavior of the
1278          * events we define. By default this is true, but this can be set to
1279          * false if you need the default behavior (not recommended)
1280          * @property preventDefault
1281          * @type boolean
1282          * @static
1283          */
1284         preventDefault: true,
1285
1286         /**
1287          * Flag to determine if we should stop the propagation of the events
1288          * we generate. This is true by default but you may want to set it to
1289          * false if the html element contains other features that require the
1290          * mouse click.
1291          * @property stopPropagation
1292          * @type boolean
1293          * @static
1294          */
1295         stopPropagation: true,
1296
1297         /**
1298          * Internal flag that is set to true when drag and drop has been
1299          * intialized
1300          * @property initialized
1301          * @private
1302          * @static
1303          */
1304         initalized: false,
1305
1306         /**
1307          * All drag and drop can be disabled.
1308          * @property locked
1309          * @private
1310          * @static
1311          */
1312         locked: false,
1313
1314         /**
1315          * Called the first time an element is registered.
1316          * @method init
1317          * @private
1318          * @static
1319          */
1320         init: function() {
1321             this.initialized = true;
1322         },
1323
1324         /**
1325          * In point mode, drag and drop interaction is defined by the
1326          * location of the cursor during the drag/drop
1327          * @property POINT
1328          * @type int
1329          * @static
1330          */
1331         POINT: 0,
1332
1333         /**
1334          * In intersect mode, drag and drop interactio nis defined by the
1335          * overlap of two or more drag and drop objects.
1336          * @property INTERSECT
1337          * @type int
1338          * @static
1339          */
1340         INTERSECT: 1,
1341
1342         /**
1343          * The current drag and drop mode.  Default: POINT
1344          * @property mode
1345          * @type int
1346          * @static
1347          */
1348         mode: 0,
1349
1350         /**
1351          * Runs method on all drag and drop objects
1352          * @method _execOnAll
1353          * @private
1354          * @static
1355          */
1356         _execOnAll: function(sMethod, args) {
1357             for (var i in this.ids) {
1358                 for (var j in this.ids[i]) {
1359                     var oDD = this.ids[i][j];
1360                     if (! this.isTypeOfDD(oDD)) {
1361                         continue;
1362                     }
1363                     oDD[sMethod].apply(oDD, args);
1364                 }
1365             }
1366         },
1367
1368         /**
1369          * Drag and drop initialization.  Sets up the global event handlers
1370          * @method _onLoad
1371          * @private
1372          * @static
1373          */
1374         _onLoad: function() {
1375
1376             this.init();
1377
1378             if (!Roo.isTouch) {
1379                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1380                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1381             }
1382             Event.on(document, "touchend",   this.handleMouseUp, this, true);
1383             Event.on(document, "touchmove", this.handleMouseMove, this, true);
1384             
1385             Event.on(window,   "unload",    this._onUnload, this, true);
1386             Event.on(window,   "resize",    this._onResize, this, true);
1387             // Event.on(window,   "mouseout",    this._test);
1388
1389         },
1390
1391         /**
1392          * Reset constraints on all drag and drop objs
1393          * @method _onResize
1394          * @private
1395          * @static
1396          */
1397         _onResize: function(e) {
1398             this._execOnAll("resetConstraints", []);
1399         },
1400
1401         /**
1402          * Lock all drag and drop functionality
1403          * @method lock
1404          * @static
1405          */
1406         lock: function() { this.locked = true; },
1407
1408         /**
1409          * Unlock all drag and drop functionality
1410          * @method unlock
1411          * @static
1412          */
1413         unlock: function() { this.locked = false; },
1414
1415         /**
1416          * Is drag and drop locked?
1417          * @method isLocked
1418          * @return {boolean} True if drag and drop is locked, false otherwise.
1419          * @static
1420          */
1421         isLocked: function() { return this.locked; },
1422
1423         /**
1424          * Location cache that is set for all drag drop objects when a drag is
1425          * initiated, cleared when the drag is finished.
1426          * @property locationCache
1427          * @private
1428          * @static
1429          */
1430         locationCache: {},
1431
1432         /**
1433          * Set useCache to false if you want to force object the lookup of each
1434          * drag and drop linked element constantly during a drag.
1435          * @property useCache
1436          * @type boolean
1437          * @static
1438          */
1439         useCache: true,
1440
1441         /**
1442          * The number of pixels that the mouse needs to move after the
1443          * mousedown before the drag is initiated.  Default=3;
1444          * @property clickPixelThresh
1445          * @type int
1446          * @static
1447          */
1448         clickPixelThresh: 3,
1449
1450         /**
1451          * The number of milliseconds after the mousedown event to initiate the
1452          * drag if we don't get a mouseup event. Default=1000
1453          * @property clickTimeThresh
1454          * @type int
1455          * @static
1456          */
1457         clickTimeThresh: 350,
1458
1459         /**
1460          * Flag that indicates that either the drag pixel threshold or the
1461          * mousdown time threshold has been met
1462          * @property dragThreshMet
1463          * @type boolean
1464          * @private
1465          * @static
1466          */
1467         dragThreshMet: false,
1468
1469         /**
1470          * Timeout used for the click time threshold
1471          * @property clickTimeout
1472          * @type Object
1473          * @private
1474          * @static
1475          */
1476         clickTimeout: null,
1477
1478         /**
1479          * The X position of the mousedown event stored for later use when a
1480          * drag threshold is met.
1481          * @property startX
1482          * @type int
1483          * @private
1484          * @static
1485          */
1486         startX: 0,
1487
1488         /**
1489          * The Y position of the mousedown event stored for later use when a
1490          * drag threshold is met.
1491          * @property startY
1492          * @type int
1493          * @private
1494          * @static
1495          */
1496         startY: 0,
1497
1498         /**
1499          * Each DragDrop instance must be registered with the DragDropMgr.
1500          * This is executed in DragDrop.init()
1501          * @method regDragDrop
1502          * @param {DragDrop} oDD the DragDrop object to register
1503          * @param {String} sGroup the name of the group this element belongs to
1504          * @static
1505          */
1506         regDragDrop: function(oDD, sGroup) {
1507             if (!this.initialized) { this.init(); }
1508
1509             if (!this.ids[sGroup]) {
1510                 this.ids[sGroup] = {};
1511             }
1512             this.ids[sGroup][oDD.id] = oDD;
1513         },
1514
1515         /**
1516          * Removes the supplied dd instance from the supplied group. Executed
1517          * by DragDrop.removeFromGroup, so don't call this function directly.
1518          * @method removeDDFromGroup
1519          * @private
1520          * @static
1521          */
1522         removeDDFromGroup: function(oDD, sGroup) {
1523             if (!this.ids[sGroup]) {
1524                 this.ids[sGroup] = {};
1525             }
1526
1527             var obj = this.ids[sGroup];
1528             if (obj && obj[oDD.id]) {
1529                 delete obj[oDD.id];
1530             }
1531         },
1532
1533         /**
1534          * Unregisters a drag and drop item.  This is executed in
1535          * DragDrop.unreg, use that method instead of calling this directly.
1536          * @method _remove
1537          * @private
1538          * @static
1539          */
1540         _remove: function(oDD) {
1541             for (var g in oDD.groups) {
1542                 if (g && this.ids[g][oDD.id]) {
1543                     delete this.ids[g][oDD.id];
1544                 }
1545             }
1546             delete this.handleIds[oDD.id];
1547         },
1548
1549         /**
1550          * Each DragDrop handle element must be registered.  This is done
1551          * automatically when executing DragDrop.setHandleElId()
1552          * @method regHandle
1553          * @param {String} sDDId the DragDrop id this element is a handle for
1554          * @param {String} sHandleId the id of the element that is the drag
1555          * handle
1556          * @static
1557          */
1558         regHandle: function(sDDId, sHandleId) {
1559             if (!this.handleIds[sDDId]) {
1560                 this.handleIds[sDDId] = {};
1561             }
1562             this.handleIds[sDDId][sHandleId] = sHandleId;
1563         },
1564
1565         /**
1566          * Utility function to determine if a given element has been
1567          * registered as a drag drop item.
1568          * @method isDragDrop
1569          * @param {String} id the element id to check
1570          * @return {boolean} true if this element is a DragDrop item,
1571          * false otherwise
1572          * @static
1573          */
1574         isDragDrop: function(id) {
1575             return ( this.getDDById(id) ) ? true : false;
1576         },
1577
1578         /**
1579          * Returns the drag and drop instances that are in all groups the
1580          * passed in instance belongs to.
1581          * @method getRelated
1582          * @param {DragDrop} p_oDD the obj to get related data for
1583          * @param {boolean} bTargetsOnly if true, only return targetable objs
1584          * @return {DragDrop[]} the related instances
1585          * @static
1586          */
1587         getRelated: function(p_oDD, bTargetsOnly) {
1588             var oDDs = [];
1589             for (var i in p_oDD.groups) {
1590                 for (j in this.ids[i]) {
1591                     var dd = this.ids[i][j];
1592                     if (! this.isTypeOfDD(dd)) {
1593                         continue;
1594                     }
1595                     if (!bTargetsOnly || dd.isTarget) {
1596                         oDDs[oDDs.length] = dd;
1597                     }
1598                 }
1599             }
1600
1601             return oDDs;
1602         },
1603
1604         /**
1605          * Returns true if the specified dd target is a legal target for
1606          * the specifice drag obj
1607          * @method isLegalTarget
1608          * @param {DragDrop} the drag obj
1609          * @param {DragDrop} the target
1610          * @return {boolean} true if the target is a legal target for the
1611          * dd obj
1612          * @static
1613          */
1614         isLegalTarget: function (oDD, oTargetDD) {
1615             var targets = this.getRelated(oDD, true);
1616             for (var i=0, len=targets.length;i<len;++i) {
1617                 if (targets[i].id == oTargetDD.id) {
1618                     return true;
1619                 }
1620             }
1621
1622             return false;
1623         },
1624
1625         /**
1626          * My goal is to be able to transparently determine if an object is
1627          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1628          * returns "object", oDD.constructor.toString() always returns
1629          * "DragDrop" and not the name of the subclass.  So for now it just
1630          * evaluates a well-known variable in DragDrop.
1631          * @method isTypeOfDD
1632          * @param {Object} the object to evaluate
1633          * @return {boolean} true if typeof oDD = DragDrop
1634          * @static
1635          */
1636         isTypeOfDD: function (oDD) {
1637             return (oDD && oDD.__ygDragDrop);
1638         },
1639
1640         /**
1641          * Utility function to determine if a given element has been
1642          * registered as a drag drop handle for the given Drag Drop object.
1643          * @method isHandle
1644          * @param {String} id the element id to check
1645          * @return {boolean} true if this element is a DragDrop handle, false
1646          * otherwise
1647          * @static
1648          */
1649         isHandle: function(sDDId, sHandleId) {
1650             return ( this.handleIds[sDDId] &&
1651                             this.handleIds[sDDId][sHandleId] );
1652         },
1653
1654         /**
1655          * Returns the DragDrop instance for a given id
1656          * @method getDDById
1657          * @param {String} id the id of the DragDrop object
1658          * @return {DragDrop} the drag drop object, null if it is not found
1659          * @static
1660          */
1661         getDDById: function(id) {
1662             for (var i in this.ids) {
1663                 if (this.ids[i][id]) {
1664                     return this.ids[i][id];
1665                 }
1666             }
1667             return null;
1668         },
1669
1670         /**
1671          * Fired after a registered DragDrop object gets the mousedown event.
1672          * Sets up the events required to track the object being dragged
1673          * @method handleMouseDown
1674          * @param {Event} e the event
1675          * @param oDD the DragDrop object being dragged
1676          * @private
1677          * @static
1678          */
1679         handleMouseDown: function(e, oDD) {
1680             if(Roo.QuickTips){
1681                 Roo.QuickTips.disable();
1682             }
1683             this.currentTarget = e.getTarget();
1684
1685             this.dragCurrent = oDD;
1686
1687             var el = oDD.getEl();
1688
1689             // track start position
1690             this.startX = e.getPageX();
1691             this.startY = e.getPageY();
1692
1693             this.deltaX = this.startX - el.offsetLeft;
1694             this.deltaY = this.startY - el.offsetTop;
1695
1696             this.dragThreshMet = false;
1697
1698             this.clickTimeout = setTimeout(
1699                     function() {
1700                         var DDM = Roo.dd.DDM;
1701                         DDM.startDrag(DDM.startX, DDM.startY);
1702                     },
1703                     this.clickTimeThresh );
1704         },
1705
1706         /**
1707          * Fired when either the drag pixel threshol or the mousedown hold
1708          * time threshold has been met.
1709          * @method startDrag
1710          * @param x {int} the X position of the original mousedown
1711          * @param y {int} the Y position of the original mousedown
1712          * @static
1713          */
1714         startDrag: function(x, y) {
1715             clearTimeout(this.clickTimeout);
1716             if (this.dragCurrent) {
1717                 this.dragCurrent.b4StartDrag(x, y);
1718                 this.dragCurrent.startDrag(x, y);
1719             }
1720             this.dragThreshMet = true;
1721         },
1722
1723         /**
1724          * Internal function to handle the mouseup event.  Will be invoked
1725          * from the context of the document.
1726          * @method handleMouseUp
1727          * @param {Event} e the event
1728          * @private
1729          * @static
1730          */
1731         handleMouseUp: function(e) {
1732
1733             if(Roo.QuickTips){
1734                 Roo.QuickTips.enable();
1735             }
1736             if (! this.dragCurrent) {
1737                 return;
1738             }
1739
1740             clearTimeout(this.clickTimeout);
1741
1742             if (this.dragThreshMet) {
1743                 this.fireEvents(e, true);
1744             } else {
1745             }
1746
1747             this.stopDrag(e);
1748
1749             this.stopEvent(e);
1750         },
1751
1752         /**
1753          * Utility to stop event propagation and event default, if these
1754          * features are turned on.
1755          * @method stopEvent
1756          * @param {Event} e the event as returned by this.getEvent()
1757          * @static
1758          */
1759         stopEvent: function(e){
1760             if(this.stopPropagation) {
1761                 e.stopPropagation();
1762             }
1763
1764             if (this.preventDefault) {
1765                 e.preventDefault();
1766             }
1767         },
1768
1769         /**
1770          * Internal function to clean up event handlers after the drag
1771          * operation is complete
1772          * @method stopDrag
1773          * @param {Event} e the event
1774          * @private
1775          * @static
1776          */
1777         stopDrag: function(e) {
1778             // Fire the drag end event for the item that was dragged
1779             if (this.dragCurrent) {
1780                 if (this.dragThreshMet) {
1781                     this.dragCurrent.b4EndDrag(e);
1782                     this.dragCurrent.endDrag(e);
1783                 }
1784
1785                 this.dragCurrent.onMouseUp(e);
1786             }
1787
1788             this.dragCurrent = null;
1789             this.dragOvers = {};
1790         },
1791
1792         /**
1793          * Internal function to handle the mousemove event.  Will be invoked
1794          * from the context of the html element.
1795          *
1796          * @TODO figure out what we can do about mouse events lost when the
1797          * user drags objects beyond the window boundary.  Currently we can
1798          * detect this in internet explorer by verifying that the mouse is
1799          * down during the mousemove event.  Firefox doesn't give us the
1800          * button state on the mousemove event.
1801          * @method handleMouseMove
1802          * @param {Event} e the event
1803          * @private
1804          * @static
1805          */
1806         handleMouseMove: function(e) {
1807             if (! this.dragCurrent) {
1808                 return true;
1809             }
1810
1811             // var button = e.which || e.button;
1812
1813             // check for IE mouseup outside of page boundary
1814             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1815                 this.stopEvent(e);
1816                 return this.handleMouseUp(e);
1817             }
1818
1819             if (!this.dragThreshMet) {
1820                 var diffX = Math.abs(this.startX - e.getPageX());
1821                 var diffY = Math.abs(this.startY - e.getPageY());
1822                 if (diffX > this.clickPixelThresh ||
1823                             diffY > this.clickPixelThresh) {
1824                     this.startDrag(this.startX, this.startY);
1825                 }
1826             }
1827
1828             if (this.dragThreshMet) {
1829                 this.dragCurrent.b4Drag(e);
1830                 this.dragCurrent.onDrag(e);
1831                 if(!this.dragCurrent.moveOnly){
1832                     this.fireEvents(e, false);
1833                 }
1834             }
1835
1836             this.stopEvent(e);
1837
1838             return true;
1839         },
1840
1841         /**
1842          * Iterates over all of the DragDrop elements to find ones we are
1843          * hovering over or dropping on
1844          * @method fireEvents
1845          * @param {Event} e the event
1846          * @param {boolean} isDrop is this a drop op or a mouseover op?
1847          * @private
1848          * @static
1849          */
1850         fireEvents: function(e, isDrop) {
1851             var dc = this.dragCurrent;
1852
1853             // If the user did the mouse up outside of the window, we could
1854             // get here even though we have ended the drag.
1855             if (!dc || dc.isLocked()) {
1856                 return;
1857             }
1858
1859             var pt = e.getPoint();
1860
1861             // cache the previous dragOver array
1862             var oldOvers = [];
1863
1864             var outEvts   = [];
1865             var overEvts  = [];
1866             var dropEvts  = [];
1867             var enterEvts = [];
1868
1869             // Check to see if the object(s) we were hovering over is no longer
1870             // being hovered over so we can fire the onDragOut event
1871             for (var i in this.dragOvers) {
1872
1873                 var ddo = this.dragOvers[i];
1874
1875                 if (! this.isTypeOfDD(ddo)) {
1876                     continue;
1877                 }
1878
1879                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1880                     outEvts.push( ddo );
1881                 }
1882
1883                 oldOvers[i] = true;
1884                 delete this.dragOvers[i];
1885             }
1886
1887             for (var sGroup in dc.groups) {
1888
1889                 if ("string" != typeof sGroup) {
1890                     continue;
1891                 }
1892
1893                 for (i in this.ids[sGroup]) {
1894                     var oDD = this.ids[sGroup][i];
1895                     if (! this.isTypeOfDD(oDD)) {
1896                         continue;
1897                     }
1898
1899                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1900                         if (this.isOverTarget(pt, oDD, this.mode)) {
1901                             // look for drop interactions
1902                             if (isDrop) {
1903                                 dropEvts.push( oDD );
1904                             // look for drag enter and drag over interactions
1905                             } else {
1906
1907                                 // initial drag over: dragEnter fires
1908                                 if (!oldOvers[oDD.id]) {
1909                                     enterEvts.push( oDD );
1910                                 // subsequent drag overs: dragOver fires
1911                                 } else {
1912                                     overEvts.push( oDD );
1913                                 }
1914
1915                                 this.dragOvers[oDD.id] = oDD;
1916                             }
1917                         }
1918                     }
1919                 }
1920             }
1921
1922             if (this.mode) {
1923                 if (outEvts.length) {
1924                     dc.b4DragOut(e, outEvts);
1925                     dc.onDragOut(e, outEvts);
1926                 }
1927
1928                 if (enterEvts.length) {
1929                     dc.onDragEnter(e, enterEvts);
1930                 }
1931
1932                 if (overEvts.length) {
1933                     dc.b4DragOver(e, overEvts);
1934                     dc.onDragOver(e, overEvts);
1935                 }
1936
1937                 if (dropEvts.length) {
1938                     dc.b4DragDrop(e, dropEvts);
1939                     dc.onDragDrop(e, dropEvts);
1940                 }
1941
1942             } else {
1943                 // fire dragout events
1944                 var len = 0;
1945                 for (i=0, len=outEvts.length; i<len; ++i) {
1946                     dc.b4DragOut(e, outEvts[i].id);
1947                     dc.onDragOut(e, outEvts[i].id);
1948                 }
1949
1950                 // fire enter events
1951                 for (i=0,len=enterEvts.length; i<len; ++i) {
1952                     // dc.b4DragEnter(e, oDD.id);
1953                     dc.onDragEnter(e, enterEvts[i].id);
1954                 }
1955
1956                 // fire over events
1957                 for (i=0,len=overEvts.length; i<len; ++i) {
1958                     dc.b4DragOver(e, overEvts[i].id);
1959                     dc.onDragOver(e, overEvts[i].id);
1960                 }
1961
1962                 // fire drop events
1963                 for (i=0, len=dropEvts.length; i<len; ++i) {
1964                     dc.b4DragDrop(e, dropEvts[i].id);
1965                     dc.onDragDrop(e, dropEvts[i].id);
1966                 }
1967
1968             }
1969
1970             // notify about a drop that did not find a target
1971             if (isDrop && !dropEvts.length) {
1972                 dc.onInvalidDrop(e);
1973             }
1974
1975         },
1976
1977         /**
1978          * Helper function for getting the best match from the list of drag
1979          * and drop objects returned by the drag and drop events when we are
1980          * in INTERSECT mode.  It returns either the first object that the
1981          * cursor is over, or the object that has the greatest overlap with
1982          * the dragged element.
1983          * @method getBestMatch
1984          * @param  {DragDrop[]} dds The array of drag and drop objects
1985          * targeted
1986          * @return {DragDrop}       The best single match
1987          * @static
1988          */
1989         getBestMatch: function(dds) {
1990             var winner = null;
1991             // Return null if the input is not what we expect
1992             //if (!dds || !dds.length || dds.length == 0) {
1993                // winner = null;
1994             // If there is only one item, it wins
1995             //} else if (dds.length == 1) {
1996
1997             var len = dds.length;
1998
1999             if (len == 1) {
2000                 winner = dds[0];
2001             } else {
2002                 // Loop through the targeted items
2003                 for (var i=0; i<len; ++i) {
2004                     var dd = dds[i];
2005                     // If the cursor is over the object, it wins.  If the
2006                     // cursor is over multiple matches, the first one we come
2007                     // to wins.
2008                     if (dd.cursorIsOver) {
2009                         winner = dd;
2010                         break;
2011                     // Otherwise the object with the most overlap wins
2012                     } else {
2013                         if (!winner ||
2014                             winner.overlap.getArea() < dd.overlap.getArea()) {
2015                             winner = dd;
2016                         }
2017                     }
2018                 }
2019             }
2020
2021             return winner;
2022         },
2023
2024         /**
2025          * Refreshes the cache of the top-left and bottom-right points of the
2026          * drag and drop objects in the specified group(s).  This is in the
2027          * format that is stored in the drag and drop instance, so typical
2028          * usage is:
2029          * <code>
2030          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2031          * </code>
2032          * Alternatively:
2033          * <code>
2034          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2035          * </code>
2036          * @TODO this really should be an indexed array.  Alternatively this
2037          * method could accept both.
2038          * @method refreshCache
2039          * @param {Object} groups an associative array of groups to refresh
2040          * @static
2041          */
2042         refreshCache: function(groups) {
2043             for (var sGroup in groups) {
2044                 if ("string" != typeof sGroup) {
2045                     continue;
2046                 }
2047                 for (var i in this.ids[sGroup]) {
2048                     var oDD = this.ids[sGroup][i];
2049
2050                     if (this.isTypeOfDD(oDD)) {
2051                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2052                         var loc = this.getLocation(oDD);
2053                         if (loc) {
2054                             this.locationCache[oDD.id] = loc;
2055                         } else {
2056                             delete this.locationCache[oDD.id];
2057                             // this will unregister the drag and drop object if
2058                             // the element is not in a usable state
2059                             // oDD.unreg();
2060                         }
2061                     }
2062                 }
2063             }
2064         },
2065
2066         /**
2067          * This checks to make sure an element exists and is in the DOM.  The
2068          * main purpose is to handle cases where innerHTML is used to remove
2069          * drag and drop objects from the DOM.  IE provides an 'unspecified
2070          * error' when trying to access the offsetParent of such an element
2071          * @method verifyEl
2072          * @param {HTMLElement} el the element to check
2073          * @return {boolean} true if the element looks usable
2074          * @static
2075          */
2076         verifyEl: function(el) {
2077             if (el) {
2078                 var parent;
2079                 if(Roo.isIE){
2080                     try{
2081                         parent = el.offsetParent;
2082                     }catch(e){}
2083                 }else{
2084                     parent = el.offsetParent;
2085                 }
2086                 if (parent) {
2087                     return true;
2088                 }
2089             }
2090
2091             return false;
2092         },
2093
2094         /**
2095          * Returns a Region object containing the drag and drop element's position
2096          * and size, including the padding configured for it
2097          * @method getLocation
2098          * @param {DragDrop} oDD the drag and drop object to get the
2099          *                       location for
2100          * @return {Roo.lib.Region} a Region object representing the total area
2101          *                             the element occupies, including any padding
2102          *                             the instance is configured for.
2103          * @static
2104          */
2105         getLocation: function(oDD) {
2106             if (! this.isTypeOfDD(oDD)) {
2107                 return null;
2108             }
2109
2110             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2111
2112             try {
2113                 pos= Roo.lib.Dom.getXY(el);
2114             } catch (e) { }
2115
2116             if (!pos) {
2117                 return null;
2118             }
2119
2120             x1 = pos[0];
2121             x2 = x1 + el.offsetWidth;
2122             y1 = pos[1];
2123             y2 = y1 + el.offsetHeight;
2124
2125             t = y1 - oDD.padding[0];
2126             r = x2 + oDD.padding[1];
2127             b = y2 + oDD.padding[2];
2128             l = x1 - oDD.padding[3];
2129
2130             return new Roo.lib.Region( t, r, b, l );
2131         },
2132
2133         /**
2134          * Checks the cursor location to see if it over the target
2135          * @method isOverTarget
2136          * @param {Roo.lib.Point} pt The point to evaluate
2137          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2138          * @return {boolean} true if the mouse is over the target
2139          * @private
2140          * @static
2141          */
2142         isOverTarget: function(pt, oTarget, intersect) {
2143             // use cache if available
2144             var loc = this.locationCache[oTarget.id];
2145             if (!loc || !this.useCache) {
2146                 loc = this.getLocation(oTarget);
2147                 this.locationCache[oTarget.id] = loc;
2148
2149             }
2150
2151             if (!loc) {
2152                 return false;
2153             }
2154
2155             oTarget.cursorIsOver = loc.contains( pt );
2156
2157             // DragDrop is using this as a sanity check for the initial mousedown
2158             // in this case we are done.  In POINT mode, if the drag obj has no
2159             // contraints, we are also done. Otherwise we need to evaluate the
2160             // location of the target as related to the actual location of the
2161             // dragged element.
2162             var dc = this.dragCurrent;
2163             if (!dc || !dc.getTargetCoord ||
2164                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2165                 return oTarget.cursorIsOver;
2166             }
2167
2168             oTarget.overlap = null;
2169
2170             // Get the current location of the drag element, this is the
2171             // location of the mouse event less the delta that represents
2172             // where the original mousedown happened on the element.  We
2173             // need to consider constraints and ticks as well.
2174             var pos = dc.getTargetCoord(pt.x, pt.y);
2175
2176             var el = dc.getDragEl();
2177             var curRegion = new Roo.lib.Region( pos.y,
2178                                                    pos.x + el.offsetWidth,
2179                                                    pos.y + el.offsetHeight,
2180                                                    pos.x );
2181
2182             var overlap = curRegion.intersect(loc);
2183
2184             if (overlap) {
2185                 oTarget.overlap = overlap;
2186                 return (intersect) ? true : oTarget.cursorIsOver;
2187             } else {
2188                 return false;
2189             }
2190         },
2191
2192         /**
2193          * unload event handler
2194          * @method _onUnload
2195          * @private
2196          * @static
2197          */
2198         _onUnload: function(e, me) {
2199             Roo.dd.DragDropMgr.unregAll();
2200         },
2201
2202         /**
2203          * Cleans up the drag and drop events and objects.
2204          * @method unregAll
2205          * @private
2206          * @static
2207          */
2208         unregAll: function() {
2209
2210             if (this.dragCurrent) {
2211                 this.stopDrag();
2212                 this.dragCurrent = null;
2213             }
2214
2215             this._execOnAll("unreg", []);
2216
2217             for (i in this.elementCache) {
2218                 delete this.elementCache[i];
2219             }
2220
2221             this.elementCache = {};
2222             this.ids = {};
2223         },
2224
2225         /**
2226          * A cache of DOM elements
2227          * @property elementCache
2228          * @private
2229          * @static
2230          */
2231         elementCache: {},
2232
2233         /**
2234          * Get the wrapper for the DOM element specified
2235          * @method getElWrapper
2236          * @param {String} id the id of the element to get
2237          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2238          * @private
2239          * @deprecated This wrapper isn't that useful
2240          * @static
2241          */
2242         getElWrapper: function(id) {
2243             var oWrapper = this.elementCache[id];
2244             if (!oWrapper || !oWrapper.el) {
2245                 oWrapper = this.elementCache[id] =
2246                     new this.ElementWrapper(Roo.getDom(id));
2247             }
2248             return oWrapper;
2249         },
2250
2251         /**
2252          * Returns the actual DOM element
2253          * @method getElement
2254          * @param {String} id the id of the elment to get
2255          * @return {Object} The element
2256          * @deprecated use Roo.getDom instead
2257          * @static
2258          */
2259         getElement: function(id) {
2260             return Roo.getDom(id);
2261         },
2262
2263         /**
2264          * Returns the style property for the DOM element (i.e.,
2265          * document.getElById(id).style)
2266          * @method getCss
2267          * @param {String} id the id of the elment to get
2268          * @return {Object} The style property of the element
2269          * @deprecated use Roo.getDom instead
2270          * @static
2271          */
2272         getCss: function(id) {
2273             var el = Roo.getDom(id);
2274             return (el) ? el.style : null;
2275         },
2276
2277         /**
2278          * Inner class for cached elements
2279          * @class DragDropMgr.ElementWrapper
2280          * @for DragDropMgr
2281          * @private
2282          * @deprecated
2283          */
2284         ElementWrapper: function(el) {
2285                 /**
2286                  * The element
2287                  * @property el
2288                  */
2289                 this.el = el || null;
2290                 /**
2291                  * The element id
2292                  * @property id
2293                  */
2294                 this.id = this.el && el.id;
2295                 /**
2296                  * A reference to the style property
2297                  * @property css
2298                  */
2299                 this.css = this.el && el.style;
2300             },
2301
2302         /**
2303          * Returns the X position of an html element
2304          * @method getPosX
2305          * @param el the element for which to get the position
2306          * @return {int} the X coordinate
2307          * @for DragDropMgr
2308          * @deprecated use Roo.lib.Dom.getX instead
2309          * @static
2310          */
2311         getPosX: function(el) {
2312             return Roo.lib.Dom.getX(el);
2313         },
2314
2315         /**
2316          * Returns the Y position of an html element
2317          * @method getPosY
2318          * @param el the element for which to get the position
2319          * @return {int} the Y coordinate
2320          * @deprecated use Roo.lib.Dom.getY instead
2321          * @static
2322          */
2323         getPosY: function(el) {
2324             return Roo.lib.Dom.getY(el);
2325         },
2326
2327         /**
2328          * Swap two nodes.  In IE, we use the native method, for others we
2329          * emulate the IE behavior
2330          * @method swapNode
2331          * @param n1 the first node to swap
2332          * @param n2 the other node to swap
2333          * @static
2334          */
2335         swapNode: function(n1, n2) {
2336             if (n1.swapNode) {
2337                 n1.swapNode(n2);
2338             } else {
2339                 var p = n2.parentNode;
2340                 var s = n2.nextSibling;
2341
2342                 if (s == n1) {
2343                     p.insertBefore(n1, n2);
2344                 } else if (n2 == n1.nextSibling) {
2345                     p.insertBefore(n2, n1);
2346                 } else {
2347                     n1.parentNode.replaceChild(n2, n1);
2348                     p.insertBefore(n1, s);
2349                 }
2350             }
2351         },
2352
2353         /**
2354          * Returns the current scroll position
2355          * @method getScroll
2356          * @private
2357          * @static
2358          */
2359         getScroll: function () {
2360             var t, l, dde=document.documentElement, db=document.body;
2361             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2362                 t = dde.scrollTop;
2363                 l = dde.scrollLeft;
2364             } else if (db) {
2365                 t = db.scrollTop;
2366                 l = db.scrollLeft;
2367             } else {
2368
2369             }
2370             return { top: t, left: l };
2371         },
2372
2373         /**
2374          * Returns the specified element style property
2375          * @method getStyle
2376          * @param {HTMLElement} el          the element
2377          * @param {string}      styleProp   the style property
2378          * @return {string} The value of the style property
2379          * @deprecated use Roo.lib.Dom.getStyle
2380          * @static
2381          */
2382         getStyle: function(el, styleProp) {
2383             return Roo.fly(el).getStyle(styleProp);
2384         },
2385
2386         /**
2387          * Gets the scrollTop
2388          * @method getScrollTop
2389          * @return {int} the document's scrollTop
2390          * @static
2391          */
2392         getScrollTop: function () { return this.getScroll().top; },
2393
2394         /**
2395          * Gets the scrollLeft
2396          * @method getScrollLeft
2397          * @return {int} the document's scrollTop
2398          * @static
2399          */
2400         getScrollLeft: function () { return this.getScroll().left; },
2401
2402         /**
2403          * Sets the x/y position of an element to the location of the
2404          * target element.
2405          * @method moveToEl
2406          * @param {HTMLElement} moveEl      The element to move
2407          * @param {HTMLElement} targetEl    The position reference element
2408          * @static
2409          */
2410         moveToEl: function (moveEl, targetEl) {
2411             var aCoord = Roo.lib.Dom.getXY(targetEl);
2412             Roo.lib.Dom.setXY(moveEl, aCoord);
2413         },
2414
2415         /**
2416          * Numeric array sort function
2417          * @method numericSort
2418          * @static
2419          */
2420         numericSort: function(a, b) { return (a - b); },
2421
2422         /**
2423          * Internal counter
2424          * @property _timeoutCount
2425          * @private
2426          * @static
2427          */
2428         _timeoutCount: 0,
2429
2430         /**
2431          * Trying to make the load order less important.  Without this we get
2432          * an error if this file is loaded before the Event Utility.
2433          * @method _addListeners
2434          * @private
2435          * @static
2436          */
2437         _addListeners: function() {
2438             var DDM = Roo.dd.DDM;
2439             if ( Roo.lib.Event && document ) {
2440                 DDM._onLoad();
2441             } else {
2442                 if (DDM._timeoutCount > 2000) {
2443                 } else {
2444                     setTimeout(DDM._addListeners, 10);
2445                     if (document && document.body) {
2446                         DDM._timeoutCount += 1;
2447                     }
2448                 }
2449             }
2450         },
2451
2452         /**
2453          * Recursively searches the immediate parent and all child nodes for
2454          * the handle element in order to determine wheter or not it was
2455          * clicked.
2456          * @method handleWasClicked
2457          * @param node the html element to inspect
2458          * @static
2459          */
2460         handleWasClicked: function(node, id) {
2461             if (this.isHandle(id, node.id)) {
2462                 return true;
2463             } else {
2464                 // check to see if this is a text node child of the one we want
2465                 var p = node.parentNode;
2466
2467                 while (p) {
2468                     if (this.isHandle(id, p.id)) {
2469                         return true;
2470                     } else {
2471                         p = p.parentNode;
2472                     }
2473                 }
2474             }
2475
2476             return false;
2477         }
2478
2479     };
2480
2481 }();
2482
2483 // shorter alias, save a few bytes
2484 Roo.dd.DDM = Roo.dd.DragDropMgr;
2485 Roo.dd.DDM._addListeners();
2486
2487 }/*
2488  * Based on:
2489  * Ext JS Library 1.1.1
2490  * Copyright(c) 2006-2007, Ext JS, LLC.
2491  *
2492  * Originally Released Under LGPL - original licence link has changed is not relivant.
2493  *
2494  * Fork - LGPL
2495  * <script type="text/javascript">
2496  */
2497
2498 /**
2499  * @class Roo.dd.DD
2500  * A DragDrop implementation where the linked element follows the
2501  * mouse cursor during a drag.
2502  * @extends Roo.dd.DragDrop
2503  * @constructor
2504  * @param {String} id the id of the linked element
2505  * @param {String} sGroup the group of related DragDrop items
2506  * @param {object} config an object containing configurable attributes
2507  *                Valid properties for DD:
2508  *                    scroll
2509  */
2510 Roo.dd.DD = function(id, sGroup, config) {
2511     if (id) {
2512         this.init(id, sGroup, config);
2513     }
2514 };
2515
2516 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2517
2518     /**
2519      * When set to true, the utility automatically tries to scroll the browser
2520      * window wehn a drag and drop element is dragged near the viewport boundary.
2521      * Defaults to true.
2522      * @property scroll
2523      * @type boolean
2524      */
2525     scroll: true,
2526
2527     /**
2528      * Sets the pointer offset to the distance between the linked element's top
2529      * left corner and the location the element was clicked
2530      * @method autoOffset
2531      * @param {int} iPageX the X coordinate of the click
2532      * @param {int} iPageY the Y coordinate of the click
2533      */
2534     autoOffset: function(iPageX, iPageY) {
2535         var x = iPageX - this.startPageX;
2536         var y = iPageY - this.startPageY;
2537         this.setDelta(x, y);
2538     },
2539
2540     /**
2541      * Sets the pointer offset.  You can call this directly to force the
2542      * offset to be in a particular location (e.g., pass in 0,0 to set it
2543      * to the center of the object)
2544      * @method setDelta
2545      * @param {int} iDeltaX the distance from the left
2546      * @param {int} iDeltaY the distance from the top
2547      */
2548     setDelta: function(iDeltaX, iDeltaY) {
2549         this.deltaX = iDeltaX;
2550         this.deltaY = iDeltaY;
2551     },
2552
2553     /**
2554      * Sets the drag element to the location of the mousedown or click event,
2555      * maintaining the cursor location relative to the location on the element
2556      * that was clicked.  Override this if you want to place the element in a
2557      * location other than where the cursor is.
2558      * @method setDragElPos
2559      * @param {int} iPageX the X coordinate of the mousedown or drag event
2560      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2561      */
2562     setDragElPos: function(iPageX, iPageY) {
2563         // the first time we do this, we are going to check to make sure
2564         // the element has css positioning
2565
2566         var el = this.getDragEl();
2567         this.alignElWithMouse(el, iPageX, iPageY);
2568     },
2569
2570     /**
2571      * Sets the element to the location of the mousedown or click event,
2572      * maintaining the cursor location relative to the location on the element
2573      * that was clicked.  Override this if you want to place the element in a
2574      * location other than where the cursor is.
2575      * @method alignElWithMouse
2576      * @param {HTMLElement} el the element to move
2577      * @param {int} iPageX the X coordinate of the mousedown or drag event
2578      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2579      */
2580     alignElWithMouse: function(el, iPageX, iPageY) {
2581         var oCoord = this.getTargetCoord(iPageX, iPageY);
2582         var fly = el.dom ? el : Roo.fly(el);
2583         if (!this.deltaSetXY) {
2584             var aCoord = [oCoord.x, oCoord.y];
2585             fly.setXY(aCoord);
2586             var newLeft = fly.getLeft(true);
2587             var newTop  = fly.getTop(true);
2588             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2589         } else {
2590             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2591         }
2592
2593         this.cachePosition(oCoord.x, oCoord.y);
2594         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2595         return oCoord;
2596     },
2597
2598     /**
2599      * Saves the most recent position so that we can reset the constraints and
2600      * tick marks on-demand.  We need to know this so that we can calculate the
2601      * number of pixels the element is offset from its original position.
2602      * @method cachePosition
2603      * @param iPageX the current x position (optional, this just makes it so we
2604      * don't have to look it up again)
2605      * @param iPageY the current y position (optional, this just makes it so we
2606      * don't have to look it up again)
2607      */
2608     cachePosition: function(iPageX, iPageY) {
2609         if (iPageX) {
2610             this.lastPageX = iPageX;
2611             this.lastPageY = iPageY;
2612         } else {
2613             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2614             this.lastPageX = aCoord[0];
2615             this.lastPageY = aCoord[1];
2616         }
2617     },
2618
2619     /**
2620      * Auto-scroll the window if the dragged object has been moved beyond the
2621      * visible window boundary.
2622      * @method autoScroll
2623      * @param {int} x the drag element's x position
2624      * @param {int} y the drag element's y position
2625      * @param {int} h the height of the drag element
2626      * @param {int} w the width of the drag element
2627      * @private
2628      */
2629     autoScroll: function(x, y, h, w) {
2630
2631         if (this.scroll) {
2632             // The client height
2633             var clientH = Roo.lib.Dom.getViewWidth();
2634
2635             // The client width
2636             var clientW = Roo.lib.Dom.getViewHeight();
2637
2638             // The amt scrolled down
2639             var st = this.DDM.getScrollTop();
2640
2641             // The amt scrolled right
2642             var sl = this.DDM.getScrollLeft();
2643
2644             // Location of the bottom of the element
2645             var bot = h + y;
2646
2647             // Location of the right of the element
2648             var right = w + x;
2649
2650             // The distance from the cursor to the bottom of the visible area,
2651             // adjusted so that we don't scroll if the cursor is beyond the
2652             // element drag constraints
2653             var toBot = (clientH + st - y - this.deltaY);
2654
2655             // The distance from the cursor to the right of the visible area
2656             var toRight = (clientW + sl - x - this.deltaX);
2657
2658
2659             // How close to the edge the cursor must be before we scroll
2660             // var thresh = (document.all) ? 100 : 40;
2661             var thresh = 40;
2662
2663             // How many pixels to scroll per autoscroll op.  This helps to reduce
2664             // clunky scrolling. IE is more sensitive about this ... it needs this
2665             // value to be higher.
2666             var scrAmt = (document.all) ? 80 : 30;
2667
2668             // Scroll down if we are near the bottom of the visible page and the
2669             // obj extends below the crease
2670             if ( bot > clientH && toBot < thresh ) {
2671                 window.scrollTo(sl, st + scrAmt);
2672             }
2673
2674             // Scroll up if the window is scrolled down and the top of the object
2675             // goes above the top border
2676             if ( y < st && st > 0 && y - st < thresh ) {
2677                 window.scrollTo(sl, st - scrAmt);
2678             }
2679
2680             // Scroll right if the obj is beyond the right border and the cursor is
2681             // near the border.
2682             if ( right > clientW && toRight < thresh ) {
2683                 window.scrollTo(sl + scrAmt, st);
2684             }
2685
2686             // Scroll left if the window has been scrolled to the right and the obj
2687             // extends past the left border
2688             if ( x < sl && sl > 0 && x - sl < thresh ) {
2689                 window.scrollTo(sl - scrAmt, st);
2690             }
2691         }
2692     },
2693
2694     /**
2695      * Finds the location the element should be placed if we want to move
2696      * it to where the mouse location less the click offset would place us.
2697      * @method getTargetCoord
2698      * @param {int} iPageX the X coordinate of the click
2699      * @param {int} iPageY the Y coordinate of the click
2700      * @return an object that contains the coordinates (Object.x and Object.y)
2701      * @private
2702      */
2703     getTargetCoord: function(iPageX, iPageY) {
2704
2705
2706         var x = iPageX - this.deltaX;
2707         var y = iPageY - this.deltaY;
2708
2709         if (this.constrainX) {
2710             if (x < this.minX) { x = this.minX; }
2711             if (x > this.maxX) { x = this.maxX; }
2712         }
2713
2714         if (this.constrainY) {
2715             if (y < this.minY) { y = this.minY; }
2716             if (y > this.maxY) { y = this.maxY; }
2717         }
2718
2719         x = this.getTick(x, this.xTicks);
2720         y = this.getTick(y, this.yTicks);
2721
2722
2723         return {x:x, y:y};
2724     },
2725
2726     /*
2727      * Sets up config options specific to this class. Overrides
2728      * Roo.dd.DragDrop, but all versions of this method through the
2729      * inheritance chain are called
2730      */
2731     applyConfig: function() {
2732         Roo.dd.DD.superclass.applyConfig.call(this);
2733         this.scroll = (this.config.scroll !== false);
2734     },
2735
2736     /*
2737      * Event that fires prior to the onMouseDown event.  Overrides
2738      * Roo.dd.DragDrop.
2739      */
2740     b4MouseDown: function(e) {
2741         // this.resetConstraints();
2742         this.autoOffset(e.getPageX(),
2743                             e.getPageY());
2744     },
2745
2746     /*
2747      * Event that fires prior to the onDrag event.  Overrides
2748      * Roo.dd.DragDrop.
2749      */
2750     b4Drag: function(e) {
2751         this.setDragElPos(e.getPageX(),
2752                             e.getPageY());
2753     },
2754
2755     toString: function() {
2756         return ("DD " + this.id);
2757     }
2758
2759     //////////////////////////////////////////////////////////////////////////
2760     // Debugging ygDragDrop events that can be overridden
2761     //////////////////////////////////////////////////////////////////////////
2762     /*
2763     startDrag: function(x, y) {
2764     },
2765
2766     onDrag: function(e) {
2767     },
2768
2769     onDragEnter: function(e, id) {
2770     },
2771
2772     onDragOver: function(e, id) {
2773     },
2774
2775     onDragOut: function(e, id) {
2776     },
2777
2778     onDragDrop: function(e, id) {
2779     },
2780
2781     endDrag: function(e) {
2782     }
2783
2784     */
2785
2786 });/*
2787  * Based on:
2788  * Ext JS Library 1.1.1
2789  * Copyright(c) 2006-2007, Ext JS, LLC.
2790  *
2791  * Originally Released Under LGPL - original licence link has changed is not relivant.
2792  *
2793  * Fork - LGPL
2794  * <script type="text/javascript">
2795  */
2796
2797 /**
2798  * @class Roo.dd.DDProxy
2799  * A DragDrop implementation that inserts an empty, bordered div into
2800  * the document that follows the cursor during drag operations.  At the time of
2801  * the click, the frame div is resized to the dimensions of the linked html
2802  * element, and moved to the exact location of the linked element.
2803  *
2804  * References to the "frame" element refer to the single proxy element that
2805  * was created to be dragged in place of all DDProxy elements on the
2806  * page.
2807  *
2808  * @extends Roo.dd.DD
2809  * @constructor
2810  * @param {String} id the id of the linked html element
2811  * @param {String} sGroup the group of related DragDrop objects
2812  * @param {object} config an object containing configurable attributes
2813  *                Valid properties for DDProxy in addition to those in DragDrop:
2814  *                   resizeFrame, centerFrame, dragElId
2815  */
2816 Roo.dd.DDProxy = function(id, sGroup, config) {
2817     if (id) {
2818         this.init(id, sGroup, config);
2819         this.initFrame();
2820     }
2821 };
2822
2823 /**
2824  * The default drag frame div id
2825  * @property Roo.dd.DDProxy.dragElId
2826  * @type String
2827  * @static
2828  */
2829 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2830
2831 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2832
2833     /**
2834      * By default we resize the drag frame to be the same size as the element
2835      * we want to drag (this is to get the frame effect).  We can turn it off
2836      * if we want a different behavior.
2837      * @property resizeFrame
2838      * @type boolean
2839      */
2840     resizeFrame: true,
2841
2842     /**
2843      * By default the frame is positioned exactly where the drag element is, so
2844      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2845      * you do not have constraints on the obj is to have the drag frame centered
2846      * around the cursor.  Set centerFrame to true for this effect.
2847      * @property centerFrame
2848      * @type boolean
2849      */
2850     centerFrame: false,
2851
2852     /**
2853      * Creates the proxy element if it does not yet exist
2854      * @method createFrame
2855      */
2856     createFrame: function() {
2857         var self = this;
2858         var body = document.body;
2859
2860         if (!body || !body.firstChild) {
2861             setTimeout( function() { self.createFrame(); }, 50 );
2862             return;
2863         }
2864
2865         var div = this.getDragEl();
2866
2867         if (!div) {
2868             div    = document.createElement("div");
2869             div.id = this.dragElId;
2870             var s  = div.style;
2871
2872             s.position   = "absolute";
2873             s.visibility = "hidden";
2874             s.cursor     = "move";
2875             s.border     = "2px solid #aaa";
2876             s.zIndex     = 999;
2877
2878             // appendChild can blow up IE if invoked prior to the window load event
2879             // while rendering a table.  It is possible there are other scenarios
2880             // that would cause this to happen as well.
2881             body.insertBefore(div, body.firstChild);
2882         }
2883     },
2884
2885     /**
2886      * Initialization for the drag frame element.  Must be called in the
2887      * constructor of all subclasses
2888      * @method initFrame
2889      */
2890     initFrame: function() {
2891         this.createFrame();
2892     },
2893
2894     applyConfig: function() {
2895         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2896
2897         this.resizeFrame = (this.config.resizeFrame !== false);
2898         this.centerFrame = (this.config.centerFrame);
2899         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2900     },
2901
2902     /**
2903      * Resizes the drag frame to the dimensions of the clicked object, positions
2904      * it over the object, and finally displays it
2905      * @method showFrame
2906      * @param {int} iPageX X click position
2907      * @param {int} iPageY Y click position
2908      * @private
2909      */
2910     showFrame: function(iPageX, iPageY) {
2911         var el = this.getEl();
2912         var dragEl = this.getDragEl();
2913         var s = dragEl.style;
2914
2915         this._resizeProxy();
2916
2917         if (this.centerFrame) {
2918             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2919                            Math.round(parseInt(s.height, 10)/2) );
2920         }
2921
2922         this.setDragElPos(iPageX, iPageY);
2923
2924         Roo.fly(dragEl).show();
2925     },
2926
2927     /**
2928      * The proxy is automatically resized to the dimensions of the linked
2929      * element when a drag is initiated, unless resizeFrame is set to false
2930      * @method _resizeProxy
2931      * @private
2932      */
2933     _resizeProxy: function() {
2934         if (this.resizeFrame) {
2935             var el = this.getEl();
2936             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2937         }
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     b4MouseDown: function(e) {
2942         var x = e.getPageX();
2943         var y = e.getPageY();
2944         this.autoOffset(x, y);
2945         this.setDragElPos(x, y);
2946     },
2947
2948     // overrides Roo.dd.DragDrop
2949     b4StartDrag: function(x, y) {
2950         // show the drag frame
2951         this.showFrame(x, y);
2952     },
2953
2954     // overrides Roo.dd.DragDrop
2955     b4EndDrag: function(e) {
2956         Roo.fly(this.getDragEl()).hide();
2957     },
2958
2959     // overrides Roo.dd.DragDrop
2960     // By default we try to move the element to the last location of the frame.
2961     // This is so that the default behavior mirrors that of Roo.dd.DD.
2962     endDrag: function(e) {
2963
2964         var lel = this.getEl();
2965         var del = this.getDragEl();
2966
2967         // Show the drag frame briefly so we can get its position
2968         del.style.visibility = "";
2969
2970         this.beforeMove();
2971         // Hide the linked element before the move to get around a Safari
2972         // rendering bug.
2973         lel.style.visibility = "hidden";
2974         Roo.dd.DDM.moveToEl(lel, del);
2975         del.style.visibility = "hidden";
2976         lel.style.visibility = "";
2977
2978         this.afterDrag();
2979     },
2980
2981     beforeMove : function(){
2982
2983     },
2984
2985     afterDrag : function(){
2986
2987     },
2988
2989     toString: function() {
2990         return ("DDProxy " + this.id);
2991     }
2992
2993 });
2994 /*
2995  * Based on:
2996  * Ext JS Library 1.1.1
2997  * Copyright(c) 2006-2007, Ext JS, LLC.
2998  *
2999  * Originally Released Under LGPL - original licence link has changed is not relivant.
3000  *
3001  * Fork - LGPL
3002  * <script type="text/javascript">
3003  */
3004
3005  /**
3006  * @class Roo.dd.DDTarget
3007  * A DragDrop implementation that does not move, but can be a drop
3008  * target.  You would get the same result by simply omitting implementation
3009  * for the event callbacks, but this way we reduce the processing cost of the
3010  * event listener and the callbacks.
3011  * @extends Roo.dd.DragDrop
3012  * @constructor
3013  * @param {String} id the id of the element that is a drop target
3014  * @param {String} sGroup the group of related DragDrop objects
3015  * @param {object} config an object containing configurable attributes
3016  *                 Valid properties for DDTarget in addition to those in
3017  *                 DragDrop:
3018  *                    none
3019  */
3020 Roo.dd.DDTarget = function(id, sGroup, config) {
3021     if (id) {
3022         this.initTarget(id, sGroup, config);
3023     }
3024     if (config.listeners || config.events) { 
3025        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3026             listeners : config.listeners || {}, 
3027             events : config.events || {} 
3028         });    
3029     }
3030 };
3031
3032 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3033 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3034     toString: function() {
3035         return ("DDTarget " + this.id);
3036     }
3037 });
3038 /*
3039  * Based on:
3040  * Ext JS Library 1.1.1
3041  * Copyright(c) 2006-2007, Ext JS, LLC.
3042  *
3043  * Originally Released Under LGPL - original licence link has changed is not relivant.
3044  *
3045  * Fork - LGPL
3046  * <script type="text/javascript">
3047  */
3048  
3049
3050 /**
3051  * @class Roo.dd.ScrollManager
3052  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3053  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3054  * @singleton
3055  */
3056 Roo.dd.ScrollManager = function(){
3057     var ddm = Roo.dd.DragDropMgr;
3058     var els = {};
3059     var dragEl = null;
3060     var proc = {};
3061     
3062     
3063     
3064     var onStop = function(e){
3065         dragEl = null;
3066         clearProc();
3067     };
3068     
3069     var triggerRefresh = function(){
3070         if(ddm.dragCurrent){
3071              ddm.refreshCache(ddm.dragCurrent.groups);
3072         }
3073     };
3074     
3075     var doScroll = function(){
3076         if(ddm.dragCurrent){
3077             var dds = Roo.dd.ScrollManager;
3078             if(!dds.animate){
3079                 if(proc.el.scroll(proc.dir, dds.increment)){
3080                     triggerRefresh();
3081                 }
3082             }else{
3083                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3084             }
3085         }
3086     };
3087     
3088     var clearProc = function(){
3089         if(proc.id){
3090             clearInterval(proc.id);
3091         }
3092         proc.id = 0;
3093         proc.el = null;
3094         proc.dir = "";
3095     };
3096     
3097     var startProc = function(el, dir){
3098          Roo.log('scroll startproc');
3099         clearProc();
3100         proc.el = el;
3101         proc.dir = dir;
3102         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3103     };
3104     
3105     var onFire = function(e, isDrop){
3106        
3107         if(isDrop || !ddm.dragCurrent){ return; }
3108         var dds = Roo.dd.ScrollManager;
3109         if(!dragEl || dragEl != ddm.dragCurrent){
3110             dragEl = ddm.dragCurrent;
3111             // refresh regions on drag start
3112             dds.refreshCache();
3113         }
3114         
3115         var xy = Roo.lib.Event.getXY(e);
3116         var pt = new Roo.lib.Point(xy[0], xy[1]);
3117         for(var id in els){
3118             var el = els[id], r = el._region;
3119             if(r && r.contains(pt) && el.isScrollable()){
3120                 if(r.bottom - pt.y <= dds.thresh){
3121                     if(proc.el != el){
3122                         startProc(el, "down");
3123                     }
3124                     return;
3125                 }else if(r.right - pt.x <= dds.thresh){
3126                     if(proc.el != el){
3127                         startProc(el, "left");
3128                     }
3129                     return;
3130                 }else if(pt.y - r.top <= dds.thresh){
3131                     if(proc.el != el){
3132                         startProc(el, "up");
3133                     }
3134                     return;
3135                 }else if(pt.x - r.left <= dds.thresh){
3136                     if(proc.el != el){
3137                         startProc(el, "right");
3138                     }
3139                     return;
3140                 }
3141             }
3142         }
3143         clearProc();
3144     };
3145     
3146     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3147     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3148     
3149     return {
3150         /**
3151          * Registers new overflow element(s) to auto scroll
3152          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3153          */
3154         register : function(el){
3155             if(el instanceof Array){
3156                 for(var i = 0, len = el.length; i < len; i++) {
3157                         this.register(el[i]);
3158                 }
3159             }else{
3160                 el = Roo.get(el);
3161                 els[el.id] = el;
3162             }
3163             Roo.dd.ScrollManager.els = els;
3164         },
3165         
3166         /**
3167          * Unregisters overflow element(s) so they are no longer scrolled
3168          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3169          */
3170         unregister : function(el){
3171             if(el instanceof Array){
3172                 for(var i = 0, len = el.length; i < len; i++) {
3173                         this.unregister(el[i]);
3174                 }
3175             }else{
3176                 el = Roo.get(el);
3177                 delete els[el.id];
3178             }
3179         },
3180         
3181         /**
3182          * The number of pixels from the edge of a container the pointer needs to be to 
3183          * trigger scrolling (defaults to 25)
3184          * @type Number
3185          */
3186         thresh : 25,
3187         
3188         /**
3189          * The number of pixels to scroll in each scroll increment (defaults to 50)
3190          * @type Number
3191          */
3192         increment : 100,
3193         
3194         /**
3195          * The frequency of scrolls in milliseconds (defaults to 500)
3196          * @type Number
3197          */
3198         frequency : 500,
3199         
3200         /**
3201          * True to animate the scroll (defaults to true)
3202          * @type Boolean
3203          */
3204         animate: true,
3205         
3206         /**
3207          * The animation duration in seconds - 
3208          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3209          * @type Number
3210          */
3211         animDuration: .4,
3212         
3213         /**
3214          * Manually trigger a cache refresh.
3215          */
3216         refreshCache : function(){
3217             for(var id in els){
3218                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3219                     els[id]._region = els[id].getRegion();
3220                 }
3221             }
3222         }
3223     };
3224 }();/*
3225  * Based on:
3226  * Ext JS Library 1.1.1
3227  * Copyright(c) 2006-2007, Ext JS, LLC.
3228  *
3229  * Originally Released Under LGPL - original licence link has changed is not relivant.
3230  *
3231  * Fork - LGPL
3232  * <script type="text/javascript">
3233  */
3234  
3235
3236 /**
3237  * @class Roo.dd.Registry
3238  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3239  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3240  * @singleton
3241  */
3242 Roo.dd.Registry = function(){
3243     var elements = {}; 
3244     var handles = {}; 
3245     var autoIdSeed = 0;
3246
3247     var getId = function(el, autogen){
3248         if(typeof el == "string"){
3249             return el;
3250         }
3251         var id = el.id;
3252         if(!id && autogen !== false){
3253             id = "roodd-" + (++autoIdSeed);
3254             el.id = id;
3255         }
3256         return id;
3257     };
3258     
3259     return {
3260     /**
3261      * Register a drag drop element
3262      * @param {String|HTMLElement} element The id or DOM node to register
3263      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3264      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3265      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3266      * populated in the data object (if applicable):
3267      * <pre>
3268 Value      Description<br />
3269 ---------  ------------------------------------------<br />
3270 handles    Array of DOM nodes that trigger dragging<br />
3271            for the element being registered<br />
3272 isHandle   True if the element passed in triggers<br />
3273            dragging itself, else false
3274 </pre>
3275      */
3276         register : function(el, data){
3277             data = data || {};
3278             if(typeof el == "string"){
3279                 el = document.getElementById(el);
3280             }
3281             data.ddel = el;
3282             elements[getId(el)] = data;
3283             if(data.isHandle !== false){
3284                 handles[data.ddel.id] = data;
3285             }
3286             if(data.handles){
3287                 var hs = data.handles;
3288                 for(var i = 0, len = hs.length; i < len; i++){
3289                         handles[getId(hs[i])] = data;
3290                 }
3291             }
3292         },
3293
3294     /**
3295      * Unregister a drag drop element
3296      * @param {String|HTMLElement}  element The id or DOM node to unregister
3297      */
3298         unregister : function(el){
3299             var id = getId(el, false);
3300             var data = elements[id];
3301             if(data){
3302                 delete elements[id];
3303                 if(data.handles){
3304                     var hs = data.handles;
3305                     for(var i = 0, len = hs.length; i < len; i++){
3306                         delete handles[getId(hs[i], false)];
3307                     }
3308                 }
3309             }
3310         },
3311
3312     /**
3313      * Returns the handle registered for a DOM Node by id
3314      * @param {String|HTMLElement} id The DOM node or id to look up
3315      * @return {Object} handle The custom handle data
3316      */
3317         getHandle : function(id){
3318             if(typeof id != "string"){ // must be element?
3319                 id = id.id;
3320             }
3321             return handles[id];
3322         },
3323
3324     /**
3325      * Returns the handle that is registered for the DOM node that is the target of the event
3326      * @param {Event} e The event
3327      * @return {Object} handle The custom handle data
3328      */
3329         getHandleFromEvent : function(e){
3330             var t = Roo.lib.Event.getTarget(e);
3331             return t ? handles[t.id] : null;
3332         },
3333
3334     /**
3335      * Returns a custom data object that is registered for a DOM node by id
3336      * @param {String|HTMLElement} id The DOM node or id to look up
3337      * @return {Object} data The custom data
3338      */
3339         getTarget : function(id){
3340             if(typeof id != "string"){ // must be element?
3341                 id = id.id;
3342             }
3343             return elements[id];
3344         },
3345
3346     /**
3347      * Returns a custom data object that is registered for the DOM node that is the target of the event
3348      * @param {Event} e The event
3349      * @return {Object} data The custom data
3350      */
3351         getTargetFromEvent : function(e){
3352             var t = Roo.lib.Event.getTarget(e);
3353             return t ? elements[t.id] || handles[t.id] : null;
3354         }
3355     };
3356 }();/*
3357  * Based on:
3358  * Ext JS Library 1.1.1
3359  * Copyright(c) 2006-2007, Ext JS, LLC.
3360  *
3361  * Originally Released Under LGPL - original licence link has changed is not relivant.
3362  *
3363  * Fork - LGPL
3364  * <script type="text/javascript">
3365  */
3366  
3367
3368 /**
3369  * @class Roo.dd.StatusProxy
3370  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3371  * default drag proxy used by all Roo.dd components.
3372  * @constructor
3373  * @param {Object} config
3374  */
3375 Roo.dd.StatusProxy = function(config){
3376     Roo.apply(this, config);
3377     this.id = this.id || Roo.id();
3378     this.el = new Roo.Layer({
3379         dh: {
3380             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3381                 {tag: "div", cls: "x-dd-drop-icon"},
3382                 {tag: "div", cls: "x-dd-drag-ghost"}
3383             ]
3384         }, 
3385         shadow: !config || config.shadow !== false
3386     });
3387     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3388     this.dropStatus = this.dropNotAllowed;
3389 };
3390
3391 Roo.dd.StatusProxy.prototype = {
3392     /**
3393      * @cfg {String} dropAllowed
3394      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3395      */
3396     dropAllowed : "x-dd-drop-ok",
3397     /**
3398      * @cfg {String} dropNotAllowed
3399      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3400      */
3401     dropNotAllowed : "x-dd-drop-nodrop",
3402
3403     /**
3404      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3405      * over the current target element.
3406      * @param {String} cssClass The css class for the new drop status indicator image
3407      */
3408     setStatus : function(cssClass){
3409         cssClass = cssClass || this.dropNotAllowed;
3410         if(this.dropStatus != cssClass){
3411             this.el.replaceClass(this.dropStatus, cssClass);
3412             this.dropStatus = cssClass;
3413         }
3414     },
3415
3416     /**
3417      * Resets the status indicator to the default dropNotAllowed value
3418      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3419      */
3420     reset : function(clearGhost){
3421         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3422         this.dropStatus = this.dropNotAllowed;
3423         if(clearGhost){
3424             this.ghost.update("");
3425         }
3426     },
3427
3428     /**
3429      * Updates the contents of the ghost element
3430      * @param {String} html The html that will replace the current innerHTML of the ghost element
3431      */
3432     update : function(html){
3433         if(typeof html == "string"){
3434             this.ghost.update(html);
3435         }else{
3436             this.ghost.update("");
3437             html.style.margin = "0";
3438             this.ghost.dom.appendChild(html);
3439         }
3440         // ensure float = none set?? cant remember why though.
3441         var el = this.ghost.dom.firstChild;
3442                 if(el){
3443                         Roo.fly(el).setStyle('float', 'none');
3444                 }
3445     },
3446     
3447     /**
3448      * Returns the underlying proxy {@link Roo.Layer}
3449      * @return {Roo.Layer} el
3450     */
3451     getEl : function(){
3452         return this.el;
3453     },
3454
3455     /**
3456      * Returns the ghost element
3457      * @return {Roo.Element} el
3458      */
3459     getGhost : function(){
3460         return this.ghost;
3461     },
3462
3463     /**
3464      * Hides the proxy
3465      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3466      */
3467     hide : function(clear){
3468         this.el.hide();
3469         if(clear){
3470             this.reset(true);
3471         }
3472     },
3473
3474     /**
3475      * Stops the repair animation if it's currently running
3476      */
3477     stop : function(){
3478         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3479             this.anim.stop();
3480         }
3481     },
3482
3483     /**
3484      * Displays this proxy
3485      */
3486     show : function(){
3487         this.el.show();
3488     },
3489
3490     /**
3491      * Force the Layer to sync its shadow and shim positions to the element
3492      */
3493     sync : function(){
3494         this.el.sync();
3495     },
3496
3497     /**
3498      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3499      * invalid drop operation by the item being dragged.
3500      * @param {Array} xy The XY position of the element ([x, y])
3501      * @param {Function} callback The function to call after the repair is complete
3502      * @param {Object} scope The scope in which to execute the callback
3503      */
3504     repair : function(xy, callback, scope){
3505         this.callback = callback;
3506         this.scope = scope;
3507         if(xy && this.animRepair !== false){
3508             this.el.addClass("x-dd-drag-repair");
3509             this.el.hideUnders(true);
3510             this.anim = this.el.shift({
3511                 duration: this.repairDuration || .5,
3512                 easing: 'easeOut',
3513                 xy: xy,
3514                 stopFx: true,
3515                 callback: this.afterRepair,
3516                 scope: this
3517             });
3518         }else{
3519             this.afterRepair();
3520         }
3521     },
3522
3523     // private
3524     afterRepair : function(){
3525         this.hide(true);
3526         if(typeof this.callback == "function"){
3527             this.callback.call(this.scope || this);
3528         }
3529         this.callback = null;
3530         this.scope = null;
3531     }
3532 };/*
3533  * Based on:
3534  * Ext JS Library 1.1.1
3535  * Copyright(c) 2006-2007, Ext JS, LLC.
3536  *
3537  * Originally Released Under LGPL - original licence link has changed is not relivant.
3538  *
3539  * Fork - LGPL
3540  * <script type="text/javascript">
3541  */
3542
3543 /**
3544  * @class Roo.dd.DragSource
3545  * @extends Roo.dd.DDProxy
3546  * A simple class that provides the basic implementation needed to make any element draggable.
3547  * @constructor
3548  * @param {String/HTMLElement/Element} el The container element
3549  * @param {Object} config
3550  */
3551 Roo.dd.DragSource = function(el, config){
3552     this.el = Roo.get(el);
3553     this.dragData = {};
3554     
3555     Roo.apply(this, config);
3556     
3557     if(!this.proxy){
3558         this.proxy = new Roo.dd.StatusProxy();
3559     }
3560
3561     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3562           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3563     
3564     this.dragging = false;
3565 };
3566
3567 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3568     /**
3569      * @cfg {String} dropAllowed
3570      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3571      */
3572     dropAllowed : "x-dd-drop-ok",
3573     /**
3574      * @cfg {String} dropNotAllowed
3575      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3576      */
3577     dropNotAllowed : "x-dd-drop-nodrop",
3578
3579     /**
3580      * Returns the data object associated with this drag source
3581      * @return {Object} data An object containing arbitrary data
3582      */
3583     getDragData : function(e){
3584         return this.dragData;
3585     },
3586
3587     // private
3588     onDragEnter : function(e, id){
3589         var target = Roo.dd.DragDropMgr.getDDById(id);
3590         this.cachedTarget = target;
3591         if(this.beforeDragEnter(target, e, id) !== false){
3592             if(target.isNotifyTarget){
3593                 var status = target.notifyEnter(this, e, this.dragData);
3594                 this.proxy.setStatus(status);
3595             }else{
3596                 this.proxy.setStatus(this.dropAllowed);
3597             }
3598             
3599             if(this.afterDragEnter){
3600                 /**
3601                  * An empty function by default, but provided so that you can perform a custom action
3602                  * when the dragged item enters the drop target by providing an implementation.
3603                  * @param {Roo.dd.DragDrop} target The drop target
3604                  * @param {Event} e The event object
3605                  * @param {String} id The id of the dragged element
3606                  * @method afterDragEnter
3607                  */
3608                 this.afterDragEnter(target, e, id);
3609             }
3610         }
3611     },
3612
3613     /**
3614      * An empty function by default, but provided so that you can perform a custom action
3615      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3616      * @param {Roo.dd.DragDrop} target The drop target
3617      * @param {Event} e The event object
3618      * @param {String} id The id of the dragged element
3619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3620      */
3621     beforeDragEnter : function(target, e, id){
3622         return true;
3623     },
3624
3625     // private
3626     alignElWithMouse: function() {
3627         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3628         this.proxy.sync();
3629     },
3630
3631     // private
3632     onDragOver : function(e, id){
3633         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3634         if(this.beforeDragOver(target, e, id) !== false){
3635             if(target.isNotifyTarget){
3636                 var status = target.notifyOver(this, e, this.dragData);
3637                 this.proxy.setStatus(status);
3638             }
3639
3640             if(this.afterDragOver){
3641                 /**
3642                  * An empty function by default, but provided so that you can perform a custom action
3643                  * while the dragged item is over the drop target by providing an implementation.
3644                  * @param {Roo.dd.DragDrop} target The drop target
3645                  * @param {Event} e The event object
3646                  * @param {String} id The id of the dragged element
3647                  * @method afterDragOver
3648                  */
3649                 this.afterDragOver(target, e, id);
3650             }
3651         }
3652     },
3653
3654     /**
3655      * An empty function by default, but provided so that you can perform a custom action
3656      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3657      * @param {Roo.dd.DragDrop} target The drop target
3658      * @param {Event} e The event object
3659      * @param {String} id The id of the dragged element
3660      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3661      */
3662     beforeDragOver : function(target, e, id){
3663         return true;
3664     },
3665
3666     // private
3667     onDragOut : function(e, id){
3668         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3669         if(this.beforeDragOut(target, e, id) !== false){
3670             if(target.isNotifyTarget){
3671                 target.notifyOut(this, e, this.dragData);
3672             }
3673             this.proxy.reset();
3674             if(this.afterDragOut){
3675                 /**
3676                  * An empty function by default, but provided so that you can perform a custom action
3677                  * after the dragged item is dragged out of the target without dropping.
3678                  * @param {Roo.dd.DragDrop} target The drop target
3679                  * @param {Event} e The event object
3680                  * @param {String} id The id of the dragged element
3681                  * @method afterDragOut
3682                  */
3683                 this.afterDragOut(target, e, id);
3684             }
3685         }
3686         this.cachedTarget = null;
3687     },
3688
3689     /**
3690      * An empty function by default, but provided so that you can perform a custom action before the dragged
3691      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3692      * @param {Roo.dd.DragDrop} target The drop target
3693      * @param {Event} e The event object
3694      * @param {String} id The id of the dragged element
3695      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3696      */
3697     beforeDragOut : function(target, e, id){
3698         return true;
3699     },
3700     
3701     // private
3702     onDragDrop : function(e, id){
3703         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3704         if(this.beforeDragDrop(target, e, id) !== false){
3705             if(target.isNotifyTarget){
3706                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3707                     this.onValidDrop(target, e, id);
3708                 }else{
3709                     this.onInvalidDrop(target, e, id);
3710                 }
3711             }else{
3712                 this.onValidDrop(target, e, id);
3713             }
3714             
3715             if(this.afterDragDrop){
3716                 /**
3717                  * An empty function by default, but provided so that you can perform a custom action
3718                  * after a valid drag drop has occurred by providing an implementation.
3719                  * @param {Roo.dd.DragDrop} target The drop target
3720                  * @param {Event} e The event object
3721                  * @param {String} id The id of the dropped element
3722                  * @method afterDragDrop
3723                  */
3724                 this.afterDragDrop(target, e, id);
3725             }
3726         }
3727         delete this.cachedTarget;
3728     },
3729
3730     /**
3731      * An empty function by default, but provided so that you can perform a custom action before the dragged
3732      * item is dropped onto the target and optionally cancel the onDragDrop.
3733      * @param {Roo.dd.DragDrop} target The drop target
3734      * @param {Event} e The event object
3735      * @param {String} id The id of the dragged element
3736      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3737      */
3738     beforeDragDrop : function(target, e, id){
3739         return true;
3740     },
3741
3742     // private
3743     onValidDrop : function(target, e, id){
3744         this.hideProxy();
3745         if(this.afterValidDrop){
3746             /**
3747              * An empty function by default, but provided so that you can perform a custom action
3748              * after a valid drop has occurred by providing an implementation.
3749              * @param {Object} target The target DD 
3750              * @param {Event} e The event object
3751              * @param {String} id The id of the dropped element
3752              * @method afterInvalidDrop
3753              */
3754             this.afterValidDrop(target, e, id);
3755         }
3756     },
3757
3758     // private
3759     getRepairXY : function(e, data){
3760         return this.el.getXY();  
3761     },
3762
3763     // private
3764     onInvalidDrop : function(target, e, id){
3765         this.beforeInvalidDrop(target, e, id);
3766         if(this.cachedTarget){
3767             if(this.cachedTarget.isNotifyTarget){
3768                 this.cachedTarget.notifyOut(this, e, this.dragData);
3769             }
3770             this.cacheTarget = null;
3771         }
3772         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3773
3774         if(this.afterInvalidDrop){
3775             /**
3776              * An empty function by default, but provided so that you can perform a custom action
3777              * after an invalid drop has occurred by providing an implementation.
3778              * @param {Event} e The event object
3779              * @param {String} id The id of the dropped element
3780              * @method afterInvalidDrop
3781              */
3782             this.afterInvalidDrop(e, id);
3783         }
3784     },
3785
3786     // private
3787     afterRepair : function(){
3788         if(Roo.enableFx){
3789             this.el.highlight(this.hlColor || "c3daf9");
3790         }
3791         this.dragging = false;
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action after an invalid
3796      * drop has occurred.
3797      * @param {Roo.dd.DragDrop} target The drop target
3798      * @param {Event} e The event object
3799      * @param {String} id The id of the dragged element
3800      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3801      */
3802     beforeInvalidDrop : function(target, e, id){
3803         return true;
3804     },
3805
3806     // private
3807     handleMouseDown : function(e){
3808         if(this.dragging) {
3809             return;
3810         }
3811         var data = this.getDragData(e);
3812         if(data && this.onBeforeDrag(data, e) !== false){
3813             this.dragData = data;
3814             this.proxy.stop();
3815             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3816         } 
3817     },
3818
3819     /**
3820      * An empty function by default, but provided so that you can perform a custom action before the initial
3821      * drag event begins and optionally cancel it.
3822      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3823      * @param {Event} e The event object
3824      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3825      */
3826     onBeforeDrag : function(data, e){
3827         return true;
3828     },
3829
3830     /**
3831      * An empty function by default, but provided so that you can perform a custom action once the initial
3832      * drag event has begun.  The drag cannot be canceled from this function.
3833      * @param {Number} x The x position of the click on the dragged object
3834      * @param {Number} y The y position of the click on the dragged object
3835      */
3836     onStartDrag : Roo.emptyFn,
3837
3838     // private - YUI override
3839     startDrag : function(x, y){
3840         this.proxy.reset();
3841         this.dragging = true;
3842         this.proxy.update("");
3843         this.onInitDrag(x, y);
3844         this.proxy.show();
3845     },
3846
3847     // private
3848     onInitDrag : function(x, y){
3849         var clone = this.el.dom.cloneNode(true);
3850         clone.id = Roo.id(); // prevent duplicate ids
3851         this.proxy.update(clone);
3852         this.onStartDrag(x, y);
3853         return true;
3854     },
3855
3856     /**
3857      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3858      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3859      */
3860     getProxy : function(){
3861         return this.proxy;  
3862     },
3863
3864     /**
3865      * Hides the drag source's {@link Roo.dd.StatusProxy}
3866      */
3867     hideProxy : function(){
3868         this.proxy.hide();  
3869         this.proxy.reset(true);
3870         this.dragging = false;
3871     },
3872
3873     // private
3874     triggerCacheRefresh : function(){
3875         Roo.dd.DDM.refreshCache(this.groups);
3876     },
3877
3878     // private - override to prevent hiding
3879     b4EndDrag: function(e) {
3880     },
3881
3882     // private - override to prevent moving
3883     endDrag : function(e){
3884         this.onEndDrag(this.dragData, e);
3885     },
3886
3887     // private
3888     onEndDrag : function(data, e){
3889     },
3890     
3891     // private - pin to cursor
3892     autoOffset : function(x, y) {
3893         this.setDelta(-12, -20);
3894     }    
3895 });/*
3896  * Based on:
3897  * Ext JS Library 1.1.1
3898  * Copyright(c) 2006-2007, Ext JS, LLC.
3899  *
3900  * Originally Released Under LGPL - original licence link has changed is not relivant.
3901  *
3902  * Fork - LGPL
3903  * <script type="text/javascript">
3904  */
3905
3906
3907 /**
3908  * @class Roo.dd.DropTarget
3909  * @extends Roo.dd.DDTarget
3910  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3911  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3912  * @constructor
3913  * @param {String/HTMLElement/Element} el The container element
3914  * @param {Object} config
3915  */
3916 Roo.dd.DropTarget = function(el, config){
3917     this.el = Roo.get(el);
3918     
3919     var listeners = false; ;
3920     if (config && config.listeners) {
3921         listeners= config.listeners;
3922         delete config.listeners;
3923     }
3924     Roo.apply(this, config);
3925     
3926     if(this.containerScroll){
3927         Roo.dd.ScrollManager.register(this.el);
3928     }
3929     this.addEvents( {
3930          /**
3931          * @scope Roo.dd.DropTarget
3932          */
3933          
3934          /**
3935          * @event enter
3936          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3937          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3938          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3939          * 
3940          * IMPORTANT : it should set this.overClass and this.dropAllowed
3941          * 
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946         "enter" : true,
3947         
3948          /**
3949          * @event over
3950          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3951          * This method will be called on every mouse movement while the drag source is over the drop target.
3952          * This default implementation simply returns the dropAllowed config value.
3953          * 
3954          * IMPORTANT : it should set this.dropAllowed
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959          
3960          */
3961         "over" : true,
3962         /**
3963          * @event out
3964          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3965          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3966          * overClass (if any) from the drop element.
3967          * 
3968          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3969          * @param {Event} e The event
3970          * @param {Object} data An object containing arbitrary data supplied by the drag source
3971          */
3972          "out" : true,
3973          
3974         /**
3975          * @event drop
3976          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3977          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3978          * implementation that does something to process the drop event and returns true so that the drag source's
3979          * repair action does not run.
3980          * 
3981          * IMPORTANT : it should set this.success
3982          * 
3983          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3984          * @param {Event} e The event
3985          * @param {Object} data An object containing arbitrary data supplied by the drag source
3986         */
3987          "drop" : true
3988     });
3989             
3990      
3991     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3992         this.el.dom, 
3993         this.ddGroup || this.group,
3994         {
3995             isTarget: true,
3996             listeners : listeners || {} 
3997            
3998         
3999         }
4000     );
4001
4002 };
4003
4004 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4005     /**
4006      * @cfg {String} overClass
4007      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4008      */
4009      /**
4010      * @cfg {String} ddGroup
4011      * The drag drop group to handle drop events for
4012      */
4013      
4014     /**
4015      * @cfg {String} dropAllowed
4016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4017      */
4018     dropAllowed : "x-dd-drop-ok",
4019     /**
4020      * @cfg {String} dropNotAllowed
4021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4022      */
4023     dropNotAllowed : "x-dd-drop-nodrop",
4024     /**
4025      * @cfg {boolean} success
4026      * set this after drop listener.. 
4027      */
4028     success : false,
4029     /**
4030      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4031      * if the drop point is valid for over/enter..
4032      */
4033     valid : false,
4034     // private
4035     isTarget : true,
4036
4037     // private
4038     isNotifyTarget : true,
4039     
4040     /**
4041      * @hide
4042      */
4043     notifyEnter : function(dd, e, data)
4044     {
4045         this.valid = true;
4046         this.fireEvent('enter', dd, e, data);
4047         if(this.overClass){
4048             this.el.addClass(this.overClass);
4049         }
4050         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4051             this.valid ? this.dropAllowed : this.dropNotAllowed
4052         );
4053     },
4054
4055     /**
4056      * @hide
4057      */
4058     notifyOver : function(dd, e, data)
4059     {
4060         this.valid = true;
4061         this.fireEvent('over', dd, e, data);
4062         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4063             this.valid ? this.dropAllowed : this.dropNotAllowed
4064         );
4065     },
4066
4067     /**
4068      * @hide
4069      */
4070     notifyOut : function(dd, e, data)
4071     {
4072         this.fireEvent('out', dd, e, data);
4073         if(this.overClass){
4074             this.el.removeClass(this.overClass);
4075         }
4076     },
4077
4078     /**
4079      * @hide
4080      */
4081     notifyDrop : function(dd, e, data)
4082     {
4083         this.success = false;
4084         this.fireEvent('drop', dd, e, data);
4085         return this.success;
4086     }
4087 });/*
4088  * Based on:
4089  * Ext JS Library 1.1.1
4090  * Copyright(c) 2006-2007, Ext JS, LLC.
4091  *
4092  * Originally Released Under LGPL - original licence link has changed is not relivant.
4093  *
4094  * Fork - LGPL
4095  * <script type="text/javascript">
4096  */
4097
4098
4099 /**
4100  * @class Roo.dd.DragZone
4101  * @extends Roo.dd.DragSource
4102  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4103  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4104  * @constructor
4105  * @param {String/HTMLElement/Element} el The container element
4106  * @param {Object} config
4107  */
4108 Roo.dd.DragZone = function(el, config){
4109     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4110     if(this.containerScroll){
4111         Roo.dd.ScrollManager.register(this.el);
4112     }
4113 };
4114
4115 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4116     /**
4117      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4118      * for auto scrolling during drag operations.
4119      */
4120     /**
4121      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4122      * method after a failed drop (defaults to "c3daf9" - light blue)
4123      */
4124
4125     /**
4126      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4127      * for a valid target to drag based on the mouse down. Override this method
4128      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4129      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4130      * @param {EventObject} e The mouse down event
4131      * @return {Object} The dragData
4132      */
4133     getDragData : function(e){
4134         return Roo.dd.Registry.getHandleFromEvent(e);
4135     },
4136     
4137     /**
4138      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4139      * this.dragData.ddel
4140      * @param {Number} x The x position of the click on the dragged object
4141      * @param {Number} y The y position of the click on the dragged object
4142      * @return {Boolean} true to continue the drag, false to cancel
4143      */
4144     onInitDrag : function(x, y){
4145         this.proxy.update(this.dragData.ddel.cloneNode(true));
4146         this.onStartDrag(x, y);
4147         return true;
4148     },
4149     
4150     /**
4151      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4152      */
4153     afterRepair : function(){
4154         if(Roo.enableFx){
4155             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4156         }
4157         this.dragging = false;
4158     },
4159
4160     /**
4161      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4162      * the XY of this.dragData.ddel
4163      * @param {EventObject} e The mouse up event
4164      * @return {Array} The xy location (e.g. [100, 200])
4165      */
4166     getRepairXY : function(e){
4167         return Roo.Element.fly(this.dragData.ddel).getXY();  
4168     }
4169 });/*
4170  * Based on:
4171  * Ext JS Library 1.1.1
4172  * Copyright(c) 2006-2007, Ext JS, LLC.
4173  *
4174  * Originally Released Under LGPL - original licence link has changed is not relivant.
4175  *
4176  * Fork - LGPL
4177  * <script type="text/javascript">
4178  */
4179 /**
4180  * @class Roo.dd.DropZone
4181  * @extends Roo.dd.DropTarget
4182  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4183  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4184  * @constructor
4185  * @param {String/HTMLElement/Element} el The container element
4186  * @param {Object} config
4187  */
4188 Roo.dd.DropZone = function(el, config){
4189     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4190 };
4191
4192 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4193     /**
4194      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4195      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4196      * provide your own custom lookup.
4197      * @param {Event} e The event
4198      * @return {Object} data The custom data
4199      */
4200     getTargetFromEvent : function(e){
4201         return Roo.dd.Registry.getTargetFromEvent(e);
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4206      * that it has registered.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeEnter : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4220      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4221      * overridden to provide the proper feedback.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4228      * underlying {@link Roo.dd.StatusProxy} can be updated
4229      */
4230     onNodeOver : function(n, dd, e, data){
4231         return this.dropAllowed;
4232     },
4233
4234     /**
4235      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4236      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4237      * node-specific processing if necessary.
4238      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4239      * {@link #getTargetFromEvent} for this node)
4240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4241      * @param {Event} e The event
4242      * @param {Object} data An object containing arbitrary data supplied by the drag source
4243      */
4244     onNodeOut : function(n, dd, e, data){
4245         
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4250      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4251      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4252      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4253      * {@link #getTargetFromEvent} for this node)
4254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4255      * @param {Event} e The event
4256      * @param {Object} data An object containing arbitrary data supplied by the drag source
4257      * @return {Boolean} True if the drop was valid, else false
4258      */
4259     onNodeDrop : function(n, dd, e, data){
4260         return false;
4261     },
4262
4263     /**
4264      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4265      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4266      * it should be overridden to provide the proper feedback if necessary.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     onContainerOver : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4279      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4280      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4281      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4282      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4283      * @param {Event} e The event
4284      * @param {Object} data An object containing arbitrary data supplied by the drag source
4285      * @return {Boolean} True if the drop was valid, else false
4286      */
4287     onContainerDrop : function(dd, e, data){
4288         return false;
4289     },
4290
4291     /**
4292      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4293      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4294      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4295      * you should override this method and provide a custom implementation.
4296      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4297      * @param {Event} e The event
4298      * @param {Object} data An object containing arbitrary data supplied by the drag source
4299      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4300      * underlying {@link Roo.dd.StatusProxy} can be updated
4301      */
4302     notifyEnter : function(dd, e, data){
4303         return this.dropNotAllowed;
4304     },
4305
4306     /**
4307      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4308      * This method will be called on every mouse movement while the drag source is over the drop zone.
4309      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4310      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4311      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4312      * registered node, it will call {@link #onContainerOver}.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag source
4316      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4317      * underlying {@link Roo.dd.StatusProxy} can be updated
4318      */
4319     notifyOver : function(dd, e, data){
4320         var n = this.getTargetFromEvent(e);
4321         if(!n){ // not over valid drop target
4322             if(this.lastOverNode){
4323                 this.onNodeOut(this.lastOverNode, dd, e, data);
4324                 this.lastOverNode = null;
4325             }
4326             return this.onContainerOver(dd, e, data);
4327         }
4328         if(this.lastOverNode != n){
4329             if(this.lastOverNode){
4330                 this.onNodeOut(this.lastOverNode, dd, e, data);
4331             }
4332             this.onNodeEnter(n, dd, e, data);
4333             this.lastOverNode = n;
4334         }
4335         return this.onNodeOver(n, dd, e, data);
4336     },
4337
4338     /**
4339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4340      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4341      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4342      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4343      * @param {Event} e The event
4344      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4345      */
4346     notifyOut : function(dd, e, data){
4347         if(this.lastOverNode){
4348             this.onNodeOut(this.lastOverNode, dd, e, data);
4349             this.lastOverNode = null;
4350         }
4351     },
4352
4353     /**
4354      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4355      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4356      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4357      * otherwise it will call {@link #onContainerDrop}.
4358      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4359      * @param {Event} e The event
4360      * @param {Object} data An object containing arbitrary data supplied by the drag source
4361      * @return {Boolean} True if the drop was valid, else false
4362      */
4363     notifyDrop : function(dd, e, data){
4364         if(this.lastOverNode){
4365             this.onNodeOut(this.lastOverNode, dd, e, data);
4366             this.lastOverNode = null;
4367         }
4368         var n = this.getTargetFromEvent(e);
4369         return n ?
4370             this.onNodeDrop(n, dd, e, data) :
4371             this.onContainerDrop(dd, e, data);
4372     },
4373
4374     // private
4375     triggerCacheRefresh : function(){
4376         Roo.dd.DDM.refreshCache(this.groups);
4377     }  
4378 });/*
4379  * Based on:
4380  * Ext JS Library 1.1.1
4381  * Copyright(c) 2006-2007, Ext JS, LLC.
4382  *
4383  * Originally Released Under LGPL - original licence link has changed is not relivant.
4384  *
4385  * Fork - LGPL
4386  * <script type="text/javascript">
4387  */
4388
4389
4390 /**
4391  * @class Roo.data.SortTypes
4392  * @singleton
4393  * Defines the default sorting (casting?) comparison functions used when sorting data.
4394  */
4395 Roo.data.SortTypes = {
4396     /**
4397      * Default sort that does nothing
4398      * @param {Mixed} s The value being converted
4399      * @return {Mixed} The comparison value
4400      */
4401     none : function(s){
4402         return s;
4403     },
4404     
4405     /**
4406      * The regular expression used to strip tags
4407      * @type {RegExp}
4408      * @property
4409      */
4410     stripTagsRE : /<\/?[^>]+>/gi,
4411     
4412     /**
4413      * Strips all HTML tags to sort on text only
4414      * @param {Mixed} s The value being converted
4415      * @return {String} The comparison value
4416      */
4417     asText : function(s){
4418         return String(s).replace(this.stripTagsRE, "");
4419     },
4420     
4421     /**
4422      * Strips all HTML tags to sort on text only - Case insensitive
4423      * @param {Mixed} s The value being converted
4424      * @return {String} The comparison value
4425      */
4426     asUCText : function(s){
4427         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4428     },
4429     
4430     /**
4431      * Case insensitive string
4432      * @param {Mixed} s The value being converted
4433      * @return {String} The comparison value
4434      */
4435     asUCString : function(s) {
4436         return String(s).toUpperCase();
4437     },
4438     
4439     /**
4440      * Date sorting
4441      * @param {Mixed} s The value being converted
4442      * @return {Number} The comparison value
4443      */
4444     asDate : function(s) {
4445         if(!s){
4446             return 0;
4447         }
4448         if(s instanceof Date){
4449             return s.getTime();
4450         }
4451         return Date.parse(String(s));
4452     },
4453     
4454     /**
4455      * Float sorting
4456      * @param {Mixed} s The value being converted
4457      * @return {Float} The comparison value
4458      */
4459     asFloat : function(s) {
4460         var val = parseFloat(String(s).replace(/,/g, ""));
4461         if(isNaN(val)) val = 0;
4462         return val;
4463     },
4464     
4465     /**
4466      * Integer sorting
4467      * @param {Mixed} s The value being converted
4468      * @return {Number} The comparison value
4469      */
4470     asInt : function(s) {
4471         var val = parseInt(String(s).replace(/,/g, ""));
4472         if(isNaN(val)) val = 0;
4473         return val;
4474     }
4475 };/*
4476  * Based on:
4477  * Ext JS Library 1.1.1
4478  * Copyright(c) 2006-2007, Ext JS, LLC.
4479  *
4480  * Originally Released Under LGPL - original licence link has changed is not relivant.
4481  *
4482  * Fork - LGPL
4483  * <script type="text/javascript">
4484  */
4485
4486 /**
4487 * @class Roo.data.Record
4488  * Instances of this class encapsulate both record <em>definition</em> information, and record
4489  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4490  * to access Records cached in an {@link Roo.data.Store} object.<br>
4491  * <p>
4492  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4493  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4494  * objects.<br>
4495  * <p>
4496  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4497  * @constructor
4498  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4499  * {@link #create}. The parameters are the same.
4500  * @param {Array} data An associative Array of data values keyed by the field name.
4501  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4502  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4503  * not specified an integer id is generated.
4504  */
4505 Roo.data.Record = function(data, id){
4506     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4507     this.data = data;
4508 };
4509
4510 /**
4511  * Generate a constructor for a specific record layout.
4512  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4513  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4514  * Each field definition object may contain the following properties: <ul>
4515  * <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,
4516  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4517  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4518  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4519  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4520  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4521  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4522  * this may be omitted.</p></li>
4523  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4524  * <ul><li>auto (Default, implies no conversion)</li>
4525  * <li>string</li>
4526  * <li>int</li>
4527  * <li>float</li>
4528  * <li>boolean</li>
4529  * <li>date</li></ul></p></li>
4530  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4531  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4532  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4533  * by the Reader into an object that will be stored in the Record. It is passed the
4534  * following parameters:<ul>
4535  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4536  * </ul></p></li>
4537  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4538  * </ul>
4539  * <br>usage:<br><pre><code>
4540 var TopicRecord = Roo.data.Record.create(
4541     {name: 'title', mapping: 'topic_title'},
4542     {name: 'author', mapping: 'username'},
4543     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4544     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4545     {name: 'lastPoster', mapping: 'user2'},
4546     {name: 'excerpt', mapping: 'post_text'}
4547 );
4548
4549 var myNewRecord = new TopicRecord({
4550     title: 'Do my job please',
4551     author: 'noobie',
4552     totalPosts: 1,
4553     lastPost: new Date(),
4554     lastPoster: 'Animal',
4555     excerpt: 'No way dude!'
4556 });
4557 myStore.add(myNewRecord);
4558 </code></pre>
4559  * @method create
4560  * @static
4561  */
4562 Roo.data.Record.create = function(o){
4563     var f = function(){
4564         f.superclass.constructor.apply(this, arguments);
4565     };
4566     Roo.extend(f, Roo.data.Record);
4567     var p = f.prototype;
4568     p.fields = new Roo.util.MixedCollection(false, function(field){
4569         return field.name;
4570     });
4571     for(var i = 0, len = o.length; i < len; i++){
4572         p.fields.add(new Roo.data.Field(o[i]));
4573     }
4574     f.getField = function(name){
4575         return p.fields.get(name);  
4576     };
4577     return f;
4578 };
4579
4580 Roo.data.Record.AUTO_ID = 1000;
4581 Roo.data.Record.EDIT = 'edit';
4582 Roo.data.Record.REJECT = 'reject';
4583 Roo.data.Record.COMMIT = 'commit';
4584
4585 Roo.data.Record.prototype = {
4586     /**
4587      * Readonly flag - true if this record has been modified.
4588      * @type Boolean
4589      */
4590     dirty : false,
4591     editing : false,
4592     error: null,
4593     modified: null,
4594
4595     // private
4596     join : function(store){
4597         this.store = store;
4598     },
4599
4600     /**
4601      * Set the named field to the specified value.
4602      * @param {String} name The name of the field to set.
4603      * @param {Object} value The value to set the field to.
4604      */
4605     set : function(name, value){
4606         if(this.data[name] == value){
4607             return;
4608         }
4609         this.dirty = true;
4610         if(!this.modified){
4611             this.modified = {};
4612         }
4613         if(typeof this.modified[name] == 'undefined'){
4614             this.modified[name] = this.data[name];
4615         }
4616         this.data[name] = value;
4617         if(!this.editing && this.store){
4618             this.store.afterEdit(this);
4619         }       
4620     },
4621
4622     /**
4623      * Get the value of the named field.
4624      * @param {String} name The name of the field to get the value of.
4625      * @return {Object} The value of the field.
4626      */
4627     get : function(name){
4628         return this.data[name]; 
4629     },
4630
4631     // private
4632     beginEdit : function(){
4633         this.editing = true;
4634         this.modified = {}; 
4635     },
4636
4637     // private
4638     cancelEdit : function(){
4639         this.editing = false;
4640         delete this.modified;
4641     },
4642
4643     // private
4644     endEdit : function(){
4645         this.editing = false;
4646         if(this.dirty && this.store){
4647             this.store.afterEdit(this);
4648         }
4649     },
4650
4651     /**
4652      * Usually called by the {@link Roo.data.Store} which owns the Record.
4653      * Rejects all changes made to the Record since either creation, or the last commit operation.
4654      * Modified fields are reverted to their original values.
4655      * <p>
4656      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4657      * of reject operations.
4658      */
4659     reject : function(){
4660         var m = this.modified;
4661         for(var n in m){
4662             if(typeof m[n] != "function"){
4663                 this.data[n] = m[n];
4664             }
4665         }
4666         this.dirty = false;
4667         delete this.modified;
4668         this.editing = false;
4669         if(this.store){
4670             this.store.afterReject(this);
4671         }
4672     },
4673
4674     /**
4675      * Usually called by the {@link Roo.data.Store} which owns the Record.
4676      * Commits all changes made to the Record since either creation, or the last commit operation.
4677      * <p>
4678      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4679      * of commit operations.
4680      */
4681     commit : function(){
4682         this.dirty = false;
4683         delete this.modified;
4684         this.editing = false;
4685         if(this.store){
4686             this.store.afterCommit(this);
4687         }
4688     },
4689
4690     // private
4691     hasError : function(){
4692         return this.error != null;
4693     },
4694
4695     // private
4696     clearError : function(){
4697         this.error = null;
4698     },
4699
4700     /**
4701      * Creates a copy of this record.
4702      * @param {String} id (optional) A new record id if you don't want to use this record's id
4703      * @return {Record}
4704      */
4705     copy : function(newId) {
4706         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4707     }
4708 };/*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718
4719
4720
4721 /**
4722  * @class Roo.data.Store
4723  * @extends Roo.util.Observable
4724  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4725  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4726  * <p>
4727  * 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
4728  * has no knowledge of the format of the data returned by the Proxy.<br>
4729  * <p>
4730  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4731  * instances from the data object. These records are cached and made available through accessor functions.
4732  * @constructor
4733  * Creates a new Store.
4734  * @param {Object} config A config object containing the objects needed for the Store to access data,
4735  * and read the data into Records.
4736  */
4737 Roo.data.Store = function(config){
4738     this.data = new Roo.util.MixedCollection(false);
4739     this.data.getKey = function(o){
4740         return o.id;
4741     };
4742     this.baseParams = {};
4743     // private
4744     this.paramNames = {
4745         "start" : "start",
4746         "limit" : "limit",
4747         "sort" : "sort",
4748         "dir" : "dir",
4749         "multisort" : "_multisort"
4750     };
4751
4752     if(config && config.data){
4753         this.inlineData = config.data;
4754         delete config.data;
4755     }
4756
4757     Roo.apply(this, config);
4758     
4759     if(this.reader){ // reader passed
4760         this.reader = Roo.factory(this.reader, Roo.data);
4761         this.reader.xmodule = this.xmodule || false;
4762         if(!this.recordType){
4763             this.recordType = this.reader.recordType;
4764         }
4765         if(this.reader.onMetaChange){
4766             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4767         }
4768     }
4769
4770     if(this.recordType){
4771         this.fields = this.recordType.prototype.fields;
4772     }
4773     this.modified = [];
4774
4775     this.addEvents({
4776         /**
4777          * @event datachanged
4778          * Fires when the data cache has changed, and a widget which is using this Store
4779          * as a Record cache should refresh its view.
4780          * @param {Store} this
4781          */
4782         datachanged : true,
4783         /**
4784          * @event metachange
4785          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4786          * @param {Store} this
4787          * @param {Object} meta The JSON metadata
4788          */
4789         metachange : true,
4790         /**
4791          * @event add
4792          * Fires when Records have been added to the Store
4793          * @param {Store} this
4794          * @param {Roo.data.Record[]} records The array of Records added
4795          * @param {Number} index The index at which the record(s) were added
4796          */
4797         add : true,
4798         /**
4799          * @event remove
4800          * Fires when a Record has been removed from the Store
4801          * @param {Store} this
4802          * @param {Roo.data.Record} record The Record that was removed
4803          * @param {Number} index The index at which the record was removed
4804          */
4805         remove : true,
4806         /**
4807          * @event update
4808          * Fires when a Record has been updated
4809          * @param {Store} this
4810          * @param {Roo.data.Record} record The Record that was updated
4811          * @param {String} operation The update operation being performed.  Value may be one of:
4812          * <pre><code>
4813  Roo.data.Record.EDIT
4814  Roo.data.Record.REJECT
4815  Roo.data.Record.COMMIT
4816          * </code></pre>
4817          */
4818         update : true,
4819         /**
4820          * @event clear
4821          * Fires when the data cache has been cleared.
4822          * @param {Store} this
4823          */
4824         clear : true,
4825         /**
4826          * @event beforeload
4827          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4828          * the load action will be canceled.
4829          * @param {Store} this
4830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4831          */
4832         beforeload : true,
4833         /**
4834          * @event beforeloadadd
4835          * Fires after a new set of Records has been loaded.
4836          * @param {Store} this
4837          * @param {Roo.data.Record[]} records The Records that were loaded
4838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4839          */
4840         beforeloadadd : true,
4841         /**
4842          * @event load
4843          * Fires after a new set of Records has been loaded, before they are added to the store.
4844          * @param {Store} this
4845          * @param {Roo.data.Record[]} records The Records that were loaded
4846          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4847          * @params {Object} return from reader
4848          */
4849         load : true,
4850         /**
4851          * @event loadexception
4852          * Fires if an exception occurs in the Proxy during loading.
4853          * Called with the signature of the Proxy's "loadexception" event.
4854          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4855          * 
4856          * @param {Proxy} 
4857          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4858          * @param {Object} load options 
4859          * @param {Object} jsonData from your request (normally this contains the Exception)
4860          */
4861         loadexception : true
4862     });
4863     
4864     if(this.proxy){
4865         this.proxy = Roo.factory(this.proxy, Roo.data);
4866         this.proxy.xmodule = this.xmodule || false;
4867         this.relayEvents(this.proxy,  ["loadexception"]);
4868     }
4869     this.sortToggle = {};
4870     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4871
4872     Roo.data.Store.superclass.constructor.call(this);
4873
4874     if(this.inlineData){
4875         this.loadData(this.inlineData);
4876         delete this.inlineData;
4877     }
4878 };
4879
4880 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4881      /**
4882     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4883     * without a remote query - used by combo/forms at present.
4884     */
4885     
4886     /**
4887     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4888     */
4889     /**
4890     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4891     */
4892     /**
4893     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4894     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4895     */
4896     /**
4897     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4898     * on any HTTP request
4899     */
4900     /**
4901     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4902     */
4903     /**
4904     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4905     */
4906     multiSort: false,
4907     /**
4908     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4909     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4910     */
4911     remoteSort : false,
4912
4913     /**
4914     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4915      * loaded or when a record is removed. (defaults to false).
4916     */
4917     pruneModifiedRecords : false,
4918
4919     // private
4920     lastOptions : null,
4921
4922     /**
4923      * Add Records to the Store and fires the add event.
4924      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4925      */
4926     add : function(records){
4927         records = [].concat(records);
4928         for(var i = 0, len = records.length; i < len; i++){
4929             records[i].join(this);
4930         }
4931         var index = this.data.length;
4932         this.data.addAll(records);
4933         this.fireEvent("add", this, records, index);
4934     },
4935
4936     /**
4937      * Remove a Record from the Store and fires the remove event.
4938      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4939      */
4940     remove : function(record){
4941         var index = this.data.indexOf(record);
4942         this.data.removeAt(index);
4943         if(this.pruneModifiedRecords){
4944             this.modified.remove(record);
4945         }
4946         this.fireEvent("remove", this, record, index);
4947     },
4948
4949     /**
4950      * Remove all Records from the Store and fires the clear event.
4951      */
4952     removeAll : function(){
4953         this.data.clear();
4954         if(this.pruneModifiedRecords){
4955             this.modified = [];
4956         }
4957         this.fireEvent("clear", this);
4958     },
4959
4960     /**
4961      * Inserts Records to the Store at the given index and fires the add event.
4962      * @param {Number} index The start index at which to insert the passed Records.
4963      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4964      */
4965     insert : function(index, records){
4966         records = [].concat(records);
4967         for(var i = 0, len = records.length; i < len; i++){
4968             this.data.insert(index, records[i]);
4969             records[i].join(this);
4970         }
4971         this.fireEvent("add", this, records, index);
4972     },
4973
4974     /**
4975      * Get the index within the cache of the passed Record.
4976      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4977      * @return {Number} The index of the passed Record. Returns -1 if not found.
4978      */
4979     indexOf : function(record){
4980         return this.data.indexOf(record);
4981     },
4982
4983     /**
4984      * Get the index within the cache of the Record with the passed id.
4985      * @param {String} id The id of the Record to find.
4986      * @return {Number} The index of the Record. Returns -1 if not found.
4987      */
4988     indexOfId : function(id){
4989         return this.data.indexOfKey(id);
4990     },
4991
4992     /**
4993      * Get the Record with the specified id.
4994      * @param {String} id The id of the Record to find.
4995      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4996      */
4997     getById : function(id){
4998         return this.data.key(id);
4999     },
5000
5001     /**
5002      * Get the Record at the specified index.
5003      * @param {Number} index The index of the Record to find.
5004      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5005      */
5006     getAt : function(index){
5007         return this.data.itemAt(index);
5008     },
5009
5010     /**
5011      * Returns a range of Records between specified indices.
5012      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5013      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5014      * @return {Roo.data.Record[]} An array of Records
5015      */
5016     getRange : function(start, end){
5017         return this.data.getRange(start, end);
5018     },
5019
5020     // private
5021     storeOptions : function(o){
5022         o = Roo.apply({}, o);
5023         delete o.callback;
5024         delete o.scope;
5025         this.lastOptions = o;
5026     },
5027
5028     /**
5029      * Loads the Record cache from the configured Proxy using the configured Reader.
5030      * <p>
5031      * If using remote paging, then the first load call must specify the <em>start</em>
5032      * and <em>limit</em> properties in the options.params property to establish the initial
5033      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5034      * <p>
5035      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5036      * and this call will return before the new data has been loaded. Perform any post-processing
5037      * in a callback function, or in a "load" event handler.</strong>
5038      * <p>
5039      * @param {Object} options An object containing properties which control loading options:<ul>
5040      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5041      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5042      * passed the following arguments:<ul>
5043      * <li>r : Roo.data.Record[]</li>
5044      * <li>options: Options object from the load call</li>
5045      * <li>success: Boolean success indicator</li></ul></li>
5046      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5047      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5048      * </ul>
5049      */
5050     load : function(options){
5051         options = options || {};
5052         if(this.fireEvent("beforeload", this, options) !== false){
5053             this.storeOptions(options);
5054             var p = Roo.apply(options.params || {}, this.baseParams);
5055             // if meta was not loaded from remote source.. try requesting it.
5056             if (!this.reader.metaFromRemote) {
5057                 p._requestMeta = 1;
5058             }
5059             if(this.sortInfo && this.remoteSort){
5060                 var pn = this.paramNames;
5061                 p[pn["sort"]] = this.sortInfo.field;
5062                 p[pn["dir"]] = this.sortInfo.direction;
5063             }
5064             if (this.multiSort) {
5065                 var pn = this.paramNames;
5066                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5067             }
5068             
5069             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5070         }
5071     },
5072
5073     /**
5074      * Reloads the Record cache from the configured Proxy using the configured Reader and
5075      * the options from the last load operation performed.
5076      * @param {Object} options (optional) An object containing properties which may override the options
5077      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5078      * the most recently used options are reused).
5079      */
5080     reload : function(options){
5081         this.load(Roo.applyIf(options||{}, this.lastOptions));
5082     },
5083
5084     // private
5085     // Called as a callback by the Reader during a load operation.
5086     loadRecords : function(o, options, success){
5087         if(!o || success === false){
5088             if(success !== false){
5089                 this.fireEvent("load", this, [], options, o);
5090             }
5091             if(options.callback){
5092                 options.callback.call(options.scope || this, [], options, false);
5093             }
5094             return;
5095         }
5096         // if data returned failure - throw an exception.
5097         if (o.success === false) {
5098             // show a message if no listener is registered.
5099             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5100                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5101             }
5102             // loadmask wil be hooked into this..
5103             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5104             return;
5105         }
5106         var r = o.records, t = o.totalRecords || r.length;
5107         
5108         this.fireEvent("beforeloadadd", this, r, options, o);
5109         
5110         if(!options || options.add !== true){
5111             if(this.pruneModifiedRecords){
5112                 this.modified = [];
5113             }
5114             for(var i = 0, len = r.length; i < len; i++){
5115                 r[i].join(this);
5116             }
5117             if(this.snapshot){
5118                 this.data = this.snapshot;
5119                 delete this.snapshot;
5120             }
5121             this.data.clear();
5122             this.data.addAll(r);
5123             this.totalLength = t;
5124             this.applySort();
5125             this.fireEvent("datachanged", this);
5126         }else{
5127             this.totalLength = Math.max(t, this.data.length+r.length);
5128             this.add(r);
5129         }
5130         this.fireEvent("load", this, r, options, o);
5131         if(options.callback){
5132             options.callback.call(options.scope || this, r, options, true);
5133         }
5134     },
5135
5136
5137     /**
5138      * Loads data from a passed data block. A Reader which understands the format of the data
5139      * must have been configured in the constructor.
5140      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5141      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5142      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5143      */
5144     loadData : function(o, append){
5145         var r = this.reader.readRecords(o);
5146         this.loadRecords(r, {add: append}, true);
5147     },
5148
5149     /**
5150      * Gets the number of cached records.
5151      * <p>
5152      * <em>If using paging, this may not be the total size of the dataset. If the data object
5153      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5154      * the data set size</em>
5155      */
5156     getCount : function(){
5157         return this.data.length || 0;
5158     },
5159
5160     /**
5161      * Gets the total number of records in the dataset as returned by the server.
5162      * <p>
5163      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5164      * the dataset size</em>
5165      */
5166     getTotalCount : function(){
5167         return this.totalLength || 0;
5168     },
5169
5170     /**
5171      * Returns the sort state of the Store as an object with two properties:
5172      * <pre><code>
5173  field {String} The name of the field by which the Records are sorted
5174  direction {String} The sort order, "ASC" or "DESC"
5175      * </code></pre>
5176      */
5177     getSortState : function(){
5178         return this.sortInfo;
5179     },
5180
5181     // private
5182     applySort : function(){
5183         if(this.sortInfo && !this.remoteSort){
5184             var s = this.sortInfo, f = s.field;
5185             var st = this.fields.get(f).sortType;
5186             var fn = function(r1, r2){
5187                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5188                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5189             };
5190             this.data.sort(s.direction, fn);
5191             if(this.snapshot && this.snapshot != this.data){
5192                 this.snapshot.sort(s.direction, fn);
5193             }
5194         }
5195     },
5196
5197     /**
5198      * Sets the default sort column and order to be used by the next load operation.
5199      * @param {String} fieldName The name of the field to sort by.
5200      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5201      */
5202     setDefaultSort : function(field, dir){
5203         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5204     },
5205
5206     /**
5207      * Sort the Records.
5208      * If remote sorting is used, the sort is performed on the server, and the cache is
5209      * reloaded. If local sorting is used, the cache is sorted internally.
5210      * @param {String} fieldName The name of the field to sort by.
5211      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5212      */
5213     sort : function(fieldName, dir){
5214         var f = this.fields.get(fieldName);
5215         if(!dir){
5216             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5217             
5218             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5219                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5220             }else{
5221                 dir = f.sortDir;
5222             }
5223         }
5224         this.sortToggle[f.name] = dir;
5225         this.sortInfo = {field: f.name, direction: dir};
5226         if(!this.remoteSort){
5227             this.applySort();
5228             this.fireEvent("datachanged", this);
5229         }else{
5230             this.load(this.lastOptions);
5231         }
5232     },
5233
5234     /**
5235      * Calls the specified function for each of the Records in the cache.
5236      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5237      * Returning <em>false</em> aborts and exits the iteration.
5238      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5239      */
5240     each : function(fn, scope){
5241         this.data.each(fn, scope);
5242     },
5243
5244     /**
5245      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5246      * (e.g., during paging).
5247      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5248      */
5249     getModifiedRecords : function(){
5250         return this.modified;
5251     },
5252
5253     // private
5254     createFilterFn : function(property, value, anyMatch){
5255         if(!value.exec){ // not a regex
5256             value = String(value);
5257             if(value.length == 0){
5258                 return false;
5259             }
5260             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5261         }
5262         
5263         return function(r){
5264             return value.test(r.data[property]);
5265         };
5266     },
5267
5268     /**
5269      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5270      * @param {String} property A field on your records
5271      * @param {Number} start The record index to start at (defaults to 0)
5272      * @param {Number} end The last record index to include (defaults to length - 1)
5273      * @return {Number} The sum
5274      */
5275     sum : function(property, start, end){
5276         var rs = this.data.items, v = 0;
5277         start = start || 0;
5278         end = (end || end === 0) ? end : rs.length-1;
5279
5280         for(var i = start; i <= end; i++){
5281             v += (rs[i].data[property] || 0);
5282         }
5283         return v;
5284     },
5285
5286     /**
5287      * Filter the records by a specified property.
5288      * @param {String} field A field on your records
5289      * @param {String/RegExp} value Either a string that the field
5290      * should start with or a RegExp to test against the field
5291      * @param {Boolean} anyMatch True to match any part not just the beginning
5292      */
5293     filter : function(property, value, anyMatch){
5294         
5295         if(typeof(property) == 'string'){
5296             var fn = this.createFilterFn(property, value, anyMatch);
5297             return fn ? this.filterBy(fn) : this.clearFilter();
5298         }
5299         
5300         var fn = [];
5301         var afn = [];
5302         
5303         var _this = this;
5304         
5305         Roo.each(property, function(p){
5306             if(anyMatch == true){
5307                 afn.push(_this.createFilterFn(p, value, true));
5308             }
5309             
5310             fn.push(_this.createFilterFn(p, value, false));
5311         });
5312         
5313         if(!fn.length && !afn.length){
5314             return this.clearFilter();
5315         }
5316         
5317         this.snapshot = this.snapshot || this.data;
5318         
5319         var filterData = [];
5320         
5321         Roo.each(fn, function(f){
5322             filterData.push(_this.queryBy(f, _this));
5323         });
5324         
5325         Roo.each(afn, function(f){
5326             filterData.push(_this.queryBy(f, _this));
5327         });
5328         
5329         var data = this.snapshot || this.data;
5330         
5331         var r = new Roo.util.MixedCollection();
5332         r.getKey = data.getKey;
5333         
5334         var keys =[];
5335         
5336         Roo.each(filterData, function(d){
5337             var k = d.keys, it = d.items;
5338             for(var i = 0, len = it.length; i < len; i++){
5339                 if(keys.indexOf(k[i]) == -1){
5340                     r.add(k[i], it[i]);
5341                 }
5342             }
5343         });
5344         
5345         this.data = r;
5346         this.fireEvent("datachanged", this);
5347     },
5348
5349     /**
5350      * Filter by a function. The specified function will be called with each
5351      * record in this data source. If the function returns true the record is included,
5352      * otherwise it is filtered.
5353      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5354      * @param {Object} scope (optional) The scope of the function (defaults to this)
5355      */
5356     filterBy : function(fn, scope){
5357         this.snapshot = this.snapshot || this.data;
5358         this.data = this.queryBy(fn, scope||this);
5359         this.fireEvent("datachanged", this);
5360     },
5361
5362     /**
5363      * Query the records by a specified property.
5364      * @param {String} field A field on your records
5365      * @param {String/RegExp} value Either a string that the field
5366      * should start with or a RegExp to test against the field
5367      * @param {Boolean} anyMatch True to match any part not just the beginning
5368      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5369      */
5370     query : function(property, value, anyMatch){
5371         var fn = this.createFilterFn(property, value, anyMatch);
5372         return fn ? this.queryBy(fn) : this.data.clone();
5373     },
5374
5375     /**
5376      * Query by a function. The specified function will be called with each
5377      * record in this data source. If the function returns true the record is included
5378      * in the results.
5379      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5380      * @param {Object} scope (optional) The scope of the function (defaults to this)
5381       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5382      **/
5383     queryBy : function(fn, scope){
5384         var data = this.snapshot || this.data;
5385         return data.filterBy(fn, scope||this);
5386     },
5387
5388     /**
5389      * Collects unique values for a particular dataIndex from this store.
5390      * @param {String} dataIndex The property to collect
5391      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5392      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5393      * @return {Array} An array of the unique values
5394      **/
5395     collect : function(dataIndex, allowNull, bypassFilter){
5396         var d = (bypassFilter === true && this.snapshot) ?
5397                 this.snapshot.items : this.data.items;
5398         var v, sv, r = [], l = {};
5399         for(var i = 0, len = d.length; i < len; i++){
5400             v = d[i].data[dataIndex];
5401             sv = String(v);
5402             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5403                 l[sv] = true;
5404                 r[r.length] = v;
5405             }
5406         }
5407         return r;
5408     },
5409
5410     /**
5411      * Revert to a view of the Record cache with no filtering applied.
5412      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5413      */
5414     clearFilter : function(suppressEvent){
5415         if(this.snapshot && this.snapshot != this.data){
5416             this.data = this.snapshot;
5417             delete this.snapshot;
5418             if(suppressEvent !== true){
5419                 this.fireEvent("datachanged", this);
5420             }
5421         }
5422     },
5423
5424     // private
5425     afterEdit : function(record){
5426         if(this.modified.indexOf(record) == -1){
5427             this.modified.push(record);
5428         }
5429         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5430     },
5431     
5432     // private
5433     afterReject : function(record){
5434         this.modified.remove(record);
5435         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5436     },
5437
5438     // private
5439     afterCommit : function(record){
5440         this.modified.remove(record);
5441         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5442     },
5443
5444     /**
5445      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5446      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5447      */
5448     commitChanges : function(){
5449         var m = this.modified.slice(0);
5450         this.modified = [];
5451         for(var i = 0, len = m.length; i < len; i++){
5452             m[i].commit();
5453         }
5454     },
5455
5456     /**
5457      * Cancel outstanding changes on all changed records.
5458      */
5459     rejectChanges : function(){
5460         var m = this.modified.slice(0);
5461         this.modified = [];
5462         for(var i = 0, len = m.length; i < len; i++){
5463             m[i].reject();
5464         }
5465     },
5466
5467     onMetaChange : function(meta, rtype, o){
5468         this.recordType = rtype;
5469         this.fields = rtype.prototype.fields;
5470         delete this.snapshot;
5471         this.sortInfo = meta.sortInfo || this.sortInfo;
5472         this.modified = [];
5473         this.fireEvent('metachange', this, this.reader.meta);
5474     },
5475     
5476     moveIndex : function(data, type)
5477     {
5478         var index = this.indexOf(data);
5479         
5480         var newIndex = index + type;
5481         
5482         this.remove(data);
5483         
5484         this.insert(newIndex, data);
5485         
5486     }
5487 });/*
5488  * Based on:
5489  * Ext JS Library 1.1.1
5490  * Copyright(c) 2006-2007, Ext JS, LLC.
5491  *
5492  * Originally Released Under LGPL - original licence link has changed is not relivant.
5493  *
5494  * Fork - LGPL
5495  * <script type="text/javascript">
5496  */
5497
5498 /**
5499  * @class Roo.data.SimpleStore
5500  * @extends Roo.data.Store
5501  * Small helper class to make creating Stores from Array data easier.
5502  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5503  * @cfg {Array} fields An array of field definition objects, or field name strings.
5504  * @cfg {Array} data The multi-dimensional array of data
5505  * @constructor
5506  * @param {Object} config
5507  */
5508 Roo.data.SimpleStore = function(config){
5509     Roo.data.SimpleStore.superclass.constructor.call(this, {
5510         isLocal : true,
5511         reader: new Roo.data.ArrayReader({
5512                 id: config.id
5513             },
5514             Roo.data.Record.create(config.fields)
5515         ),
5516         proxy : new Roo.data.MemoryProxy(config.data)
5517     });
5518     this.load();
5519 };
5520 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5521  * Based on:
5522  * Ext JS Library 1.1.1
5523  * Copyright(c) 2006-2007, Ext JS, LLC.
5524  *
5525  * Originally Released Under LGPL - original licence link has changed is not relivant.
5526  *
5527  * Fork - LGPL
5528  * <script type="text/javascript">
5529  */
5530
5531 /**
5532 /**
5533  * @extends Roo.data.Store
5534  * @class Roo.data.JsonStore
5535  * Small helper class to make creating Stores for JSON data easier. <br/>
5536 <pre><code>
5537 var store = new Roo.data.JsonStore({
5538     url: 'get-images.php',
5539     root: 'images',
5540     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5541 });
5542 </code></pre>
5543  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5544  * JsonReader and HttpProxy (unless inline data is provided).</b>
5545  * @cfg {Array} fields An array of field definition objects, or field name strings.
5546  * @constructor
5547  * @param {Object} config
5548  */
5549 Roo.data.JsonStore = function(c){
5550     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5551         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5552         reader: new Roo.data.JsonReader(c, c.fields)
5553     }));
5554 };
5555 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5556  * Based on:
5557  * Ext JS Library 1.1.1
5558  * Copyright(c) 2006-2007, Ext JS, LLC.
5559  *
5560  * Originally Released Under LGPL - original licence link has changed is not relivant.
5561  *
5562  * Fork - LGPL
5563  * <script type="text/javascript">
5564  */
5565
5566  
5567 Roo.data.Field = function(config){
5568     if(typeof config == "string"){
5569         config = {name: config};
5570     }
5571     Roo.apply(this, config);
5572     
5573     if(!this.type){
5574         this.type = "auto";
5575     }
5576     
5577     var st = Roo.data.SortTypes;
5578     // named sortTypes are supported, here we look them up
5579     if(typeof this.sortType == "string"){
5580         this.sortType = st[this.sortType];
5581     }
5582     
5583     // set default sortType for strings and dates
5584     if(!this.sortType){
5585         switch(this.type){
5586             case "string":
5587                 this.sortType = st.asUCString;
5588                 break;
5589             case "date":
5590                 this.sortType = st.asDate;
5591                 break;
5592             default:
5593                 this.sortType = st.none;
5594         }
5595     }
5596
5597     // define once
5598     var stripRe = /[\$,%]/g;
5599
5600     // prebuilt conversion function for this field, instead of
5601     // switching every time we're reading a value
5602     if(!this.convert){
5603         var cv, dateFormat = this.dateFormat;
5604         switch(this.type){
5605             case "":
5606             case "auto":
5607             case undefined:
5608                 cv = function(v){ return v; };
5609                 break;
5610             case "string":
5611                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5612                 break;
5613             case "int":
5614                 cv = function(v){
5615                     return v !== undefined && v !== null && v !== '' ?
5616                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5617                     };
5618                 break;
5619             case "float":
5620                 cv = function(v){
5621                     return v !== undefined && v !== null && v !== '' ?
5622                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5623                     };
5624                 break;
5625             case "bool":
5626             case "boolean":
5627                 cv = function(v){ return v === true || v === "true" || v == 1; };
5628                 break;
5629             case "date":
5630                 cv = function(v){
5631                     if(!v){
5632                         return '';
5633                     }
5634                     if(v instanceof Date){
5635                         return v;
5636                     }
5637                     if(dateFormat){
5638                         if(dateFormat == "timestamp"){
5639                             return new Date(v*1000);
5640                         }
5641                         return Date.parseDate(v, dateFormat);
5642                     }
5643                     var parsed = Date.parse(v);
5644                     return parsed ? new Date(parsed) : null;
5645                 };
5646              break;
5647             
5648         }
5649         this.convert = cv;
5650     }
5651 };
5652
5653 Roo.data.Field.prototype = {
5654     dateFormat: null,
5655     defaultValue: "",
5656     mapping: null,
5657     sortType : null,
5658     sortDir : "ASC"
5659 };/*
5660  * Based on:
5661  * Ext JS Library 1.1.1
5662  * Copyright(c) 2006-2007, Ext JS, LLC.
5663  *
5664  * Originally Released Under LGPL - original licence link has changed is not relivant.
5665  *
5666  * Fork - LGPL
5667  * <script type="text/javascript">
5668  */
5669  
5670 // Base class for reading structured data from a data source.  This class is intended to be
5671 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5672
5673 /**
5674  * @class Roo.data.DataReader
5675  * Base class for reading structured data from a data source.  This class is intended to be
5676  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5677  */
5678
5679 Roo.data.DataReader = function(meta, recordType){
5680     
5681     this.meta = meta;
5682     
5683     this.recordType = recordType instanceof Array ? 
5684         Roo.data.Record.create(recordType) : recordType;
5685 };
5686
5687 Roo.data.DataReader.prototype = {
5688      /**
5689      * Create an empty record
5690      * @param {Object} data (optional) - overlay some values
5691      * @return {Roo.data.Record} record created.
5692      */
5693     newRow :  function(d) {
5694         var da =  {};
5695         this.recordType.prototype.fields.each(function(c) {
5696             switch( c.type) {
5697                 case 'int' : da[c.name] = 0; break;
5698                 case 'date' : da[c.name] = new Date(); break;
5699                 case 'float' : da[c.name] = 0.0; break;
5700                 case 'boolean' : da[c.name] = false; break;
5701                 default : da[c.name] = ""; break;
5702             }
5703             
5704         });
5705         return new this.recordType(Roo.apply(da, d));
5706     }
5707     
5708 };/*
5709  * Based on:
5710  * Ext JS Library 1.1.1
5711  * Copyright(c) 2006-2007, Ext JS, LLC.
5712  *
5713  * Originally Released Under LGPL - original licence link has changed is not relivant.
5714  *
5715  * Fork - LGPL
5716  * <script type="text/javascript">
5717  */
5718
5719 /**
5720  * @class Roo.data.DataProxy
5721  * @extends Roo.data.Observable
5722  * This class is an abstract base class for implementations which provide retrieval of
5723  * unformatted data objects.<br>
5724  * <p>
5725  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5726  * (of the appropriate type which knows how to parse the data object) to provide a block of
5727  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5728  * <p>
5729  * Custom implementations must implement the load method as described in
5730  * {@link Roo.data.HttpProxy#load}.
5731  */
5732 Roo.data.DataProxy = function(){
5733     this.addEvents({
5734         /**
5735          * @event beforeload
5736          * Fires before a network request is made to retrieve a data object.
5737          * @param {Object} This DataProxy object.
5738          * @param {Object} params The params parameter to the load function.
5739          */
5740         beforeload : true,
5741         /**
5742          * @event load
5743          * Fires before the load method's callback is called.
5744          * @param {Object} This DataProxy object.
5745          * @param {Object} o The data object.
5746          * @param {Object} arg The callback argument object passed to the load function.
5747          */
5748         load : true,
5749         /**
5750          * @event loadexception
5751          * Fires if an Exception occurs during data retrieval.
5752          * @param {Object} This DataProxy object.
5753          * @param {Object} o The data object.
5754          * @param {Object} arg The callback argument object passed to the load function.
5755          * @param {Object} e The Exception.
5756          */
5757         loadexception : true
5758     });
5759     Roo.data.DataProxy.superclass.constructor.call(this);
5760 };
5761
5762 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5763
5764     /**
5765      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5766      */
5767 /*
5768  * Based on:
5769  * Ext JS Library 1.1.1
5770  * Copyright(c) 2006-2007, Ext JS, LLC.
5771  *
5772  * Originally Released Under LGPL - original licence link has changed is not relivant.
5773  *
5774  * Fork - LGPL
5775  * <script type="text/javascript">
5776  */
5777 /**
5778  * @class Roo.data.MemoryProxy
5779  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5780  * to the Reader when its load method is called.
5781  * @constructor
5782  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5783  */
5784 Roo.data.MemoryProxy = function(data){
5785     if (data.data) {
5786         data = data.data;
5787     }
5788     Roo.data.MemoryProxy.superclass.constructor.call(this);
5789     this.data = data;
5790 };
5791
5792 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5793     /**
5794      * Load data from the requested source (in this case an in-memory
5795      * data object passed to the constructor), read the data object into
5796      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5797      * process that block using the passed callback.
5798      * @param {Object} params This parameter is not used by the MemoryProxy class.
5799      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5800      * object into a block of Roo.data.Records.
5801      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5802      * The function must be passed <ul>
5803      * <li>The Record block object</li>
5804      * <li>The "arg" argument from the load function</li>
5805      * <li>A boolean success indicator</li>
5806      * </ul>
5807      * @param {Object} scope The scope in which to call the callback
5808      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5809      */
5810     load : function(params, reader, callback, scope, arg){
5811         params = params || {};
5812         var result;
5813         try {
5814             result = reader.readRecords(this.data);
5815         }catch(e){
5816             this.fireEvent("loadexception", this, arg, null, e);
5817             callback.call(scope, null, arg, false);
5818             return;
5819         }
5820         callback.call(scope, result, arg, true);
5821     },
5822     
5823     // private
5824     update : function(params, records){
5825         
5826     }
5827 });/*
5828  * Based on:
5829  * Ext JS Library 1.1.1
5830  * Copyright(c) 2006-2007, Ext JS, LLC.
5831  *
5832  * Originally Released Under LGPL - original licence link has changed is not relivant.
5833  *
5834  * Fork - LGPL
5835  * <script type="text/javascript">
5836  */
5837 /**
5838  * @class Roo.data.HttpProxy
5839  * @extends Roo.data.DataProxy
5840  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5841  * configured to reference a certain URL.<br><br>
5842  * <p>
5843  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5844  * from which the running page was served.<br><br>
5845  * <p>
5846  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5847  * <p>
5848  * Be aware that to enable the browser to parse an XML document, the server must set
5849  * the Content-Type header in the HTTP response to "text/xml".
5850  * @constructor
5851  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5852  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5853  * will be used to make the request.
5854  */
5855 Roo.data.HttpProxy = function(conn){
5856     Roo.data.HttpProxy.superclass.constructor.call(this);
5857     // is conn a conn config or a real conn?
5858     this.conn = conn;
5859     this.useAjax = !conn || !conn.events;
5860   
5861 };
5862
5863 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5864     // thse are take from connection...
5865     
5866     /**
5867      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5868      */
5869     /**
5870      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5871      * extra parameters to each request made by this object. (defaults to undefined)
5872      */
5873     /**
5874      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5875      *  to each request made by this object. (defaults to undefined)
5876      */
5877     /**
5878      * @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)
5879      */
5880     /**
5881      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5882      */
5883      /**
5884      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5885      * @type Boolean
5886      */
5887   
5888
5889     /**
5890      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5891      * @type Boolean
5892      */
5893     /**
5894      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5895      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5896      * a finer-grained basis than the DataProxy events.
5897      */
5898     getConnection : function(){
5899         return this.useAjax ? Roo.Ajax : this.conn;
5900     },
5901
5902     /**
5903      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5904      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5905      * process that block using the passed callback.
5906      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5907      * for the request to the remote server.
5908      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5909      * object into a block of Roo.data.Records.
5910      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5911      * The function must be passed <ul>
5912      * <li>The Record block object</li>
5913      * <li>The "arg" argument from the load function</li>
5914      * <li>A boolean success indicator</li>
5915      * </ul>
5916      * @param {Object} scope The scope in which to call the callback
5917      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5918      */
5919     load : function(params, reader, callback, scope, arg){
5920         if(this.fireEvent("beforeload", this, params) !== false){
5921             var  o = {
5922                 params : params || {},
5923                 request: {
5924                     callback : callback,
5925                     scope : scope,
5926                     arg : arg
5927                 },
5928                 reader: reader,
5929                 callback : this.loadResponse,
5930                 scope: this
5931             };
5932             if(this.useAjax){
5933                 Roo.applyIf(o, this.conn);
5934                 if(this.activeRequest){
5935                     Roo.Ajax.abort(this.activeRequest);
5936                 }
5937                 this.activeRequest = Roo.Ajax.request(o);
5938             }else{
5939                 this.conn.request(o);
5940             }
5941         }else{
5942             callback.call(scope||this, null, arg, false);
5943         }
5944     },
5945
5946     // private
5947     loadResponse : function(o, success, response){
5948         delete this.activeRequest;
5949         if(!success){
5950             this.fireEvent("loadexception", this, o, response);
5951             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5952             return;
5953         }
5954         var result;
5955         try {
5956             result = o.reader.read(response);
5957         }catch(e){
5958             this.fireEvent("loadexception", this, o, response, e);
5959             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5960             return;
5961         }
5962         
5963         this.fireEvent("load", this, o, o.request.arg);
5964         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5965     },
5966
5967     // private
5968     update : function(dataSet){
5969
5970     },
5971
5972     // private
5973     updateResponse : function(dataSet){
5974
5975     }
5976 });/*
5977  * Based on:
5978  * Ext JS Library 1.1.1
5979  * Copyright(c) 2006-2007, Ext JS, LLC.
5980  *
5981  * Originally Released Under LGPL - original licence link has changed is not relivant.
5982  *
5983  * Fork - LGPL
5984  * <script type="text/javascript">
5985  */
5986
5987 /**
5988  * @class Roo.data.ScriptTagProxy
5989  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5990  * other than the originating domain of the running page.<br><br>
5991  * <p>
5992  * <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
5993  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5994  * <p>
5995  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5996  * source code that is used as the source inside a &lt;script> tag.<br><br>
5997  * <p>
5998  * In order for the browser to process the returned data, the server must wrap the data object
5999  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
6000  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
6001  * depending on whether the callback name was passed:
6002  * <p>
6003  * <pre><code>
6004 boolean scriptTag = false;
6005 String cb = request.getParameter("callback");
6006 if (cb != null) {
6007     scriptTag = true;
6008     response.setContentType("text/javascript");
6009 } else {
6010     response.setContentType("application/x-json");
6011 }
6012 Writer out = response.getWriter();
6013 if (scriptTag) {
6014     out.write(cb + "(");
6015 }
6016 out.print(dataBlock.toJsonString());
6017 if (scriptTag) {
6018     out.write(");");
6019 }
6020 </pre></code>
6021  *
6022  * @constructor
6023  * @param {Object} config A configuration object.
6024  */
6025 Roo.data.ScriptTagProxy = function(config){
6026     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
6027     Roo.apply(this, config);
6028     this.head = document.getElementsByTagName("head")[0];
6029 };
6030
6031 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
6032
6033 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
6034     /**
6035      * @cfg {String} url The URL from which to request the data object.
6036      */
6037     /**
6038      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
6039      */
6040     timeout : 30000,
6041     /**
6042      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
6043      * the server the name of the callback function set up by the load call to process the returned data object.
6044      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
6045      * javascript output which calls this named function passing the data object as its only parameter.
6046      */
6047     callbackParam : "callback",
6048     /**
6049      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
6050      * name to the request.
6051      */
6052     nocache : true,
6053
6054     /**
6055      * Load data from the configured URL, read the data object into
6056      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6057      * process that block using the passed callback.
6058      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6059      * for the request to the remote server.
6060      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6061      * object into a block of Roo.data.Records.
6062      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6063      * The function must be passed <ul>
6064      * <li>The Record block object</li>
6065      * <li>The "arg" argument from the load function</li>
6066      * <li>A boolean success indicator</li>
6067      * </ul>
6068      * @param {Object} scope The scope in which to call the callback
6069      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6070      */
6071     load : function(params, reader, callback, scope, arg){
6072         if(this.fireEvent("beforeload", this, params) !== false){
6073
6074             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6075
6076             var url = this.url;
6077             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6078             if(this.nocache){
6079                 url += "&_dc=" + (new Date().getTime());
6080             }
6081             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6082             var trans = {
6083                 id : transId,
6084                 cb : "stcCallback"+transId,
6085                 scriptId : "stcScript"+transId,
6086                 params : params,
6087                 arg : arg,
6088                 url : url,
6089                 callback : callback,
6090                 scope : scope,
6091                 reader : reader
6092             };
6093             var conn = this;
6094
6095             window[trans.cb] = function(o){
6096                 conn.handleResponse(o, trans);
6097             };
6098
6099             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6100
6101             if(this.autoAbort !== false){
6102                 this.abort();
6103             }
6104
6105             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6106
6107             var script = document.createElement("script");
6108             script.setAttribute("src", url);
6109             script.setAttribute("type", "text/javascript");
6110             script.setAttribute("id", trans.scriptId);
6111             this.head.appendChild(script);
6112
6113             this.trans = trans;
6114         }else{
6115             callback.call(scope||this, null, arg, false);
6116         }
6117     },
6118
6119     // private
6120     isLoading : function(){
6121         return this.trans ? true : false;
6122     },
6123
6124     /**
6125      * Abort the current server request.
6126      */
6127     abort : function(){
6128         if(this.isLoading()){
6129             this.destroyTrans(this.trans);
6130         }
6131     },
6132
6133     // private
6134     destroyTrans : function(trans, isLoaded){
6135         this.head.removeChild(document.getElementById(trans.scriptId));
6136         clearTimeout(trans.timeoutId);
6137         if(isLoaded){
6138             window[trans.cb] = undefined;
6139             try{
6140                 delete window[trans.cb];
6141             }catch(e){}
6142         }else{
6143             // if hasn't been loaded, wait for load to remove it to prevent script error
6144             window[trans.cb] = function(){
6145                 window[trans.cb] = undefined;
6146                 try{
6147                     delete window[trans.cb];
6148                 }catch(e){}
6149             };
6150         }
6151     },
6152
6153     // private
6154     handleResponse : function(o, trans){
6155         this.trans = false;
6156         this.destroyTrans(trans, true);
6157         var result;
6158         try {
6159             result = trans.reader.readRecords(o);
6160         }catch(e){
6161             this.fireEvent("loadexception", this, o, trans.arg, e);
6162             trans.callback.call(trans.scope||window, null, trans.arg, false);
6163             return;
6164         }
6165         this.fireEvent("load", this, o, trans.arg);
6166         trans.callback.call(trans.scope||window, result, trans.arg, true);
6167     },
6168
6169     // private
6170     handleFailure : function(trans){
6171         this.trans = false;
6172         this.destroyTrans(trans, false);
6173         this.fireEvent("loadexception", this, null, trans.arg);
6174         trans.callback.call(trans.scope||window, null, trans.arg, false);
6175     }
6176 });/*
6177  * Based on:
6178  * Ext JS Library 1.1.1
6179  * Copyright(c) 2006-2007, Ext JS, LLC.
6180  *
6181  * Originally Released Under LGPL - original licence link has changed is not relivant.
6182  *
6183  * Fork - LGPL
6184  * <script type="text/javascript">
6185  */
6186
6187 /**
6188  * @class Roo.data.JsonReader
6189  * @extends Roo.data.DataReader
6190  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6191  * based on mappings in a provided Roo.data.Record constructor.
6192  * 
6193  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6194  * in the reply previously. 
6195  * 
6196  * <p>
6197  * Example code:
6198  * <pre><code>
6199 var RecordDef = Roo.data.Record.create([
6200     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6201     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6202 ]);
6203 var myReader = new Roo.data.JsonReader({
6204     totalProperty: "results",    // The property which contains the total dataset size (optional)
6205     root: "rows",                // The property which contains an Array of row objects
6206     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6207 }, RecordDef);
6208 </code></pre>
6209  * <p>
6210  * This would consume a JSON file like this:
6211  * <pre><code>
6212 { 'results': 2, 'rows': [
6213     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6214     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6215 }
6216 </code></pre>
6217  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6218  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6219  * paged from the remote server.
6220  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6221  * @cfg {String} root name of the property which contains the Array of row objects.
6222  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6223  * @constructor
6224  * Create a new JsonReader
6225  * @param {Object} meta Metadata configuration options
6226  * @param {Object} recordType Either an Array of field definition objects,
6227  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6228  */
6229 Roo.data.JsonReader = function(meta, recordType){
6230     
6231     meta = meta || {};
6232     // set some defaults:
6233     Roo.applyIf(meta, {
6234         totalProperty: 'total',
6235         successProperty : 'success',
6236         root : 'data',
6237         id : 'id'
6238     });
6239     
6240     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6241 };
6242 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6243     
6244     /**
6245      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6246      * Used by Store query builder to append _requestMeta to params.
6247      * 
6248      */
6249     metaFromRemote : false,
6250     /**
6251      * This method is only used by a DataProxy which has retrieved data from a remote server.
6252      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6253      * @return {Object} data A data block which is used by an Roo.data.Store object as
6254      * a cache of Roo.data.Records.
6255      */
6256     read : function(response){
6257         var json = response.responseText;
6258        
6259         var o = /* eval:var:o */ eval("("+json+")");
6260         if(!o) {
6261             throw {message: "JsonReader.read: Json object not found"};
6262         }
6263         
6264         if(o.metaData){
6265             
6266             delete this.ef;
6267             this.metaFromRemote = true;
6268             this.meta = o.metaData;
6269             this.recordType = Roo.data.Record.create(o.metaData.fields);
6270             this.onMetaChange(this.meta, this.recordType, o);
6271         }
6272         return this.readRecords(o);
6273     },
6274
6275     // private function a store will implement
6276     onMetaChange : function(meta, recordType, o){
6277
6278     },
6279
6280     /**
6281          * @ignore
6282          */
6283     simpleAccess: function(obj, subsc) {
6284         return obj[subsc];
6285     },
6286
6287         /**
6288          * @ignore
6289          */
6290     getJsonAccessor: function(){
6291         var re = /[\[\.]/;
6292         return function(expr) {
6293             try {
6294                 return(re.test(expr))
6295                     ? new Function("obj", "return obj." + expr)
6296                     : function(obj){
6297                         return obj[expr];
6298                     };
6299             } catch(e){}
6300             return Roo.emptyFn;
6301         };
6302     }(),
6303
6304     /**
6305      * Create a data block containing Roo.data.Records from an XML document.
6306      * @param {Object} o An object which contains an Array of row objects in the property specified
6307      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6308      * which contains the total size of the dataset.
6309      * @return {Object} data A data block which is used by an Roo.data.Store object as
6310      * a cache of Roo.data.Records.
6311      */
6312     readRecords : function(o){
6313         /**
6314          * After any data loads, the raw JSON data is available for further custom processing.
6315          * @type Object
6316          */
6317         this.o = o;
6318         var s = this.meta, Record = this.recordType,
6319             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6320
6321 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6322         if (!this.ef) {
6323             if(s.totalProperty) {
6324                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6325                 }
6326                 if(s.successProperty) {
6327                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6328                 }
6329                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6330                 if (s.id) {
6331                         var g = this.getJsonAccessor(s.id);
6332                         this.getId = function(rec) {
6333                                 var r = g(rec);  
6334                                 return (r === undefined || r === "") ? null : r;
6335                         };
6336                 } else {
6337                         this.getId = function(){return null;};
6338                 }
6339             this.ef = [];
6340             for(var jj = 0; jj < fl; jj++){
6341                 f = fi[jj];
6342                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6343                 this.ef[jj] = this.getJsonAccessor(map);
6344             }
6345         }
6346
6347         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6348         if(s.totalProperty){
6349             var vt = parseInt(this.getTotal(o), 10);
6350             if(!isNaN(vt)){
6351                 totalRecords = vt;
6352             }
6353         }
6354         if(s.successProperty){
6355             var vs = this.getSuccess(o);
6356             if(vs === false || vs === 'false'){
6357                 success = false;
6358             }
6359         }
6360         var records = [];
6361         for(var i = 0; i < c; i++){
6362                 var n = root[i];
6363             var values = {};
6364             var id = this.getId(n);
6365             for(var j = 0; j < fl; j++){
6366                 f = fi[j];
6367             var v = this.ef[j](n);
6368             if (!f.convert) {
6369                 Roo.log('missing convert for ' + f.name);
6370                 Roo.log(f);
6371                 continue;
6372             }
6373             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6374             }
6375             var record = new Record(values, id);
6376             record.json = n;
6377             records[i] = record;
6378         }
6379         return {
6380             raw : o,
6381             success : success,
6382             records : records,
6383             totalRecords : totalRecords
6384         };
6385     }
6386 });/*
6387  * Based on:
6388  * Ext JS Library 1.1.1
6389  * Copyright(c) 2006-2007, Ext JS, LLC.
6390  *
6391  * Originally Released Under LGPL - original licence link has changed is not relivant.
6392  *
6393  * Fork - LGPL
6394  * <script type="text/javascript">
6395  */
6396
6397 /**
6398  * @class Roo.data.XmlReader
6399  * @extends Roo.data.DataReader
6400  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6401  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6402  * <p>
6403  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6404  * header in the HTTP response must be set to "text/xml".</em>
6405  * <p>
6406  * Example code:
6407  * <pre><code>
6408 var RecordDef = Roo.data.Record.create([
6409    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6410    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6411 ]);
6412 var myReader = new Roo.data.XmlReader({
6413    totalRecords: "results", // The element which contains the total dataset size (optional)
6414    record: "row",           // The repeated element which contains row information
6415    id: "id"                 // The element within the row that provides an ID for the record (optional)
6416 }, RecordDef);
6417 </code></pre>
6418  * <p>
6419  * This would consume an XML file like this:
6420  * <pre><code>
6421 &lt;?xml?>
6422 &lt;dataset>
6423  &lt;results>2&lt;/results>
6424  &lt;row>
6425    &lt;id>1&lt;/id>
6426    &lt;name>Bill&lt;/name>
6427    &lt;occupation>Gardener&lt;/occupation>
6428  &lt;/row>
6429  &lt;row>
6430    &lt;id>2&lt;/id>
6431    &lt;name>Ben&lt;/name>
6432    &lt;occupation>Horticulturalist&lt;/occupation>
6433  &lt;/row>
6434 &lt;/dataset>
6435 </code></pre>
6436  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6437  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6438  * paged from the remote server.
6439  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6440  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6441  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6442  * a record identifier value.
6443  * @constructor
6444  * Create a new XmlReader
6445  * @param {Object} meta Metadata configuration options
6446  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6447  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6448  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6449  */
6450 Roo.data.XmlReader = function(meta, recordType){
6451     meta = meta || {};
6452     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6453 };
6454 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6455     /**
6456      * This method is only used by a DataProxy which has retrieved data from a remote server.
6457          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6458          * to contain a method called 'responseXML' that returns an XML document object.
6459      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6460      * a cache of Roo.data.Records.
6461      */
6462     read : function(response){
6463         var doc = response.responseXML;
6464         if(!doc) {
6465             throw {message: "XmlReader.read: XML Document not available"};
6466         }
6467         return this.readRecords(doc);
6468     },
6469
6470     /**
6471      * Create a data block containing Roo.data.Records from an XML document.
6472          * @param {Object} doc A parsed XML document.
6473      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6474      * a cache of Roo.data.Records.
6475      */
6476     readRecords : function(doc){
6477         /**
6478          * After any data loads/reads, the raw XML Document is available for further custom processing.
6479          * @type XMLDocument
6480          */
6481         this.xmlData = doc;
6482         var root = doc.documentElement || doc;
6483         var q = Roo.DomQuery;
6484         var recordType = this.recordType, fields = recordType.prototype.fields;
6485         var sid = this.meta.id;
6486         var totalRecords = 0, success = true;
6487         if(this.meta.totalRecords){
6488             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6489         }
6490         
6491         if(this.meta.success){
6492             var sv = q.selectValue(this.meta.success, root, true);
6493             success = sv !== false && sv !== 'false';
6494         }
6495         var records = [];
6496         var ns = q.select(this.meta.record, root);
6497         for(var i = 0, len = ns.length; i < len; i++) {
6498                 var n = ns[i];
6499                 var values = {};
6500                 var id = sid ? q.selectValue(sid, n) : undefined;
6501                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6502                     var f = fields.items[j];
6503                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6504                     v = f.convert(v);
6505                     values[f.name] = v;
6506                 }
6507                 var record = new recordType(values, id);
6508                 record.node = n;
6509                 records[records.length] = record;
6510             }
6511
6512             return {
6513                 success : success,
6514                 records : records,
6515                 totalRecords : totalRecords || records.length
6516             };
6517     }
6518 });/*
6519  * Based on:
6520  * Ext JS Library 1.1.1
6521  * Copyright(c) 2006-2007, Ext JS, LLC.
6522  *
6523  * Originally Released Under LGPL - original licence link has changed is not relivant.
6524  *
6525  * Fork - LGPL
6526  * <script type="text/javascript">
6527  */
6528
6529 /**
6530  * @class Roo.data.ArrayReader
6531  * @extends Roo.data.DataReader
6532  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6533  * Each element of that Array represents a row of data fields. The
6534  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6535  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6536  * <p>
6537  * Example code:.
6538  * <pre><code>
6539 var RecordDef = Roo.data.Record.create([
6540     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6541     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6542 ]);
6543 var myReader = new Roo.data.ArrayReader({
6544     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6545 }, RecordDef);
6546 </code></pre>
6547  * <p>
6548  * This would consume an Array like this:
6549  * <pre><code>
6550 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6551   </code></pre>
6552  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6553  * @constructor
6554  * Create a new JsonReader
6555  * @param {Object} meta Metadata configuration options.
6556  * @param {Object} recordType Either an Array of field definition objects
6557  * as specified to {@link Roo.data.Record#create},
6558  * or an {@link Roo.data.Record} object
6559  * created using {@link Roo.data.Record#create}.
6560  */
6561 Roo.data.ArrayReader = function(meta, recordType){
6562     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6563 };
6564
6565 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6566     /**
6567      * Create a data block containing Roo.data.Records from an XML document.
6568      * @param {Object} o An Array of row objects which represents the dataset.
6569      * @return {Object} data A data block which is used by an Roo.data.Store object as
6570      * a cache of Roo.data.Records.
6571      */
6572     readRecords : function(o){
6573         var sid = this.meta ? this.meta.id : null;
6574         var recordType = this.recordType, fields = recordType.prototype.fields;
6575         var records = [];
6576         var root = o;
6577             for(var i = 0; i < root.length; i++){
6578                     var n = root[i];
6579                 var values = {};
6580                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6581                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6582                 var f = fields.items[j];
6583                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6584                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6585                 v = f.convert(v);
6586                 values[f.name] = v;
6587             }
6588                 var record = new recordType(values, id);
6589                 record.json = n;
6590                 records[records.length] = record;
6591             }
6592             return {
6593                 records : records,
6594                 totalRecords : records.length
6595             };
6596     }
6597 });/*
6598  * Based on:
6599  * Ext JS Library 1.1.1
6600  * Copyright(c) 2006-2007, Ext JS, LLC.
6601  *
6602  * Originally Released Under LGPL - original licence link has changed is not relivant.
6603  *
6604  * Fork - LGPL
6605  * <script type="text/javascript">
6606  */
6607
6608
6609 /**
6610  * @class Roo.data.Tree
6611  * @extends Roo.util.Observable
6612  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6613  * in the tree have most standard DOM functionality.
6614  * @constructor
6615  * @param {Node} root (optional) The root node
6616  */
6617 Roo.data.Tree = function(root){
6618    this.nodeHash = {};
6619    /**
6620     * The root node for this tree
6621     * @type Node
6622     */
6623    this.root = null;
6624    if(root){
6625        this.setRootNode(root);
6626    }
6627    this.addEvents({
6628        /**
6629         * @event append
6630         * Fires when a new child node is appended to a node in this tree.
6631         * @param {Tree} tree The owner tree
6632         * @param {Node} parent The parent node
6633         * @param {Node} node The newly appended node
6634         * @param {Number} index The index of the newly appended node
6635         */
6636        "append" : true,
6637        /**
6638         * @event remove
6639         * Fires when a child node is removed from a node in this tree.
6640         * @param {Tree} tree The owner tree
6641         * @param {Node} parent The parent node
6642         * @param {Node} node The child node removed
6643         */
6644        "remove" : true,
6645        /**
6646         * @event move
6647         * Fires when a node is moved to a new location in the tree
6648         * @param {Tree} tree The owner tree
6649         * @param {Node} node The node moved
6650         * @param {Node} oldParent The old parent of this node
6651         * @param {Node} newParent The new parent of this node
6652         * @param {Number} index The index it was moved to
6653         */
6654        "move" : true,
6655        /**
6656         * @event insert
6657         * Fires when a new child node is inserted in a node in this tree.
6658         * @param {Tree} tree The owner tree
6659         * @param {Node} parent The parent node
6660         * @param {Node} node The child node inserted
6661         * @param {Node} refNode The child node the node was inserted before
6662         */
6663        "insert" : true,
6664        /**
6665         * @event beforeappend
6666         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6667         * @param {Tree} tree The owner tree
6668         * @param {Node} parent The parent node
6669         * @param {Node} node The child node to be appended
6670         */
6671        "beforeappend" : true,
6672        /**
6673         * @event beforeremove
6674         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6675         * @param {Tree} tree The owner tree
6676         * @param {Node} parent The parent node
6677         * @param {Node} node The child node to be removed
6678         */
6679        "beforeremove" : true,
6680        /**
6681         * @event beforemove
6682         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6683         * @param {Tree} tree The owner tree
6684         * @param {Node} node The node being moved
6685         * @param {Node} oldParent The parent of the node
6686         * @param {Node} newParent The new parent the node is moving to
6687         * @param {Number} index The index it is being moved to
6688         */
6689        "beforemove" : true,
6690        /**
6691         * @event beforeinsert
6692         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6693         * @param {Tree} tree The owner tree
6694         * @param {Node} parent The parent node
6695         * @param {Node} node The child node to be inserted
6696         * @param {Node} refNode The child node the node is being inserted before
6697         */
6698        "beforeinsert" : true
6699    });
6700
6701     Roo.data.Tree.superclass.constructor.call(this);
6702 };
6703
6704 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6705     pathSeparator: "/",
6706
6707     proxyNodeEvent : function(){
6708         return this.fireEvent.apply(this, arguments);
6709     },
6710
6711     /**
6712      * Returns the root node for this tree.
6713      * @return {Node}
6714      */
6715     getRootNode : function(){
6716         return this.root;
6717     },
6718
6719     /**
6720      * Sets the root node for this tree.
6721      * @param {Node} node
6722      * @return {Node}
6723      */
6724     setRootNode : function(node){
6725         this.root = node;
6726         node.ownerTree = this;
6727         node.isRoot = true;
6728         this.registerNode(node);
6729         return node;
6730     },
6731
6732     /**
6733      * Gets a node in this tree by its id.
6734      * @param {String} id
6735      * @return {Node}
6736      */
6737     getNodeById : function(id){
6738         return this.nodeHash[id];
6739     },
6740
6741     registerNode : function(node){
6742         this.nodeHash[node.id] = node;
6743     },
6744
6745     unregisterNode : function(node){
6746         delete this.nodeHash[node.id];
6747     },
6748
6749     toString : function(){
6750         return "[Tree"+(this.id?" "+this.id:"")+"]";
6751     }
6752 });
6753
6754 /**
6755  * @class Roo.data.Node
6756  * @extends Roo.util.Observable
6757  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6758  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6759  * @constructor
6760  * @param {Object} attributes The attributes/config for the node
6761  */
6762 Roo.data.Node = function(attributes){
6763     /**
6764      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6765      * @type {Object}
6766      */
6767     this.attributes = attributes || {};
6768     this.leaf = this.attributes.leaf;
6769     /**
6770      * The node id. @type String
6771      */
6772     this.id = this.attributes.id;
6773     if(!this.id){
6774         this.id = Roo.id(null, "ynode-");
6775         this.attributes.id = this.id;
6776     }
6777      
6778     
6779     /**
6780      * All child nodes of this node. @type Array
6781      */
6782     this.childNodes = [];
6783     if(!this.childNodes.indexOf){ // indexOf is a must
6784         this.childNodes.indexOf = function(o){
6785             for(var i = 0, len = this.length; i < len; i++){
6786                 if(this[i] == o) {
6787                     return i;
6788                 }
6789             }
6790             return -1;
6791         };
6792     }
6793     /**
6794      * The parent node for this node. @type Node
6795      */
6796     this.parentNode = null;
6797     /**
6798      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6799      */
6800     this.firstChild = null;
6801     /**
6802      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6803      */
6804     this.lastChild = null;
6805     /**
6806      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6807      */
6808     this.previousSibling = null;
6809     /**
6810      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6811      */
6812     this.nextSibling = null;
6813
6814     this.addEvents({
6815        /**
6816         * @event append
6817         * Fires when a new child node is appended
6818         * @param {Tree} tree The owner tree
6819         * @param {Node} this This node
6820         * @param {Node} node The newly appended node
6821         * @param {Number} index The index of the newly appended node
6822         */
6823        "append" : true,
6824        /**
6825         * @event remove
6826         * Fires when a child node is removed
6827         * @param {Tree} tree The owner tree
6828         * @param {Node} this This node
6829         * @param {Node} node The removed node
6830         */
6831        "remove" : true,
6832        /**
6833         * @event move
6834         * Fires when this node is moved to a new location in the tree
6835         * @param {Tree} tree The owner tree
6836         * @param {Node} this This node
6837         * @param {Node} oldParent The old parent of this node
6838         * @param {Node} newParent The new parent of this node
6839         * @param {Number} index The index it was moved to
6840         */
6841        "move" : true,
6842        /**
6843         * @event insert
6844         * Fires when a new child node is inserted.
6845         * @param {Tree} tree The owner tree
6846         * @param {Node} this This node
6847         * @param {Node} node The child node inserted
6848         * @param {Node} refNode The child node the node was inserted before
6849         */
6850        "insert" : true,
6851        /**
6852         * @event beforeappend
6853         * Fires before a new child is appended, return false to cancel the append.
6854         * @param {Tree} tree The owner tree
6855         * @param {Node} this This node
6856         * @param {Node} node The child node to be appended
6857         */
6858        "beforeappend" : true,
6859        /**
6860         * @event beforeremove
6861         * Fires before a child is removed, return false to cancel the remove.
6862         * @param {Tree} tree The owner tree
6863         * @param {Node} this This node
6864         * @param {Node} node The child node to be removed
6865         */
6866        "beforeremove" : true,
6867        /**
6868         * @event beforemove
6869         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6870         * @param {Tree} tree The owner tree
6871         * @param {Node} this This node
6872         * @param {Node} oldParent The parent of this node
6873         * @param {Node} newParent The new parent this node is moving to
6874         * @param {Number} index The index it is being moved to
6875         */
6876        "beforemove" : true,
6877        /**
6878         * @event beforeinsert
6879         * Fires before a new child is inserted, return false to cancel the insert.
6880         * @param {Tree} tree The owner tree
6881         * @param {Node} this This node
6882         * @param {Node} node The child node to be inserted
6883         * @param {Node} refNode The child node the node is being inserted before
6884         */
6885        "beforeinsert" : true
6886    });
6887     this.listeners = this.attributes.listeners;
6888     Roo.data.Node.superclass.constructor.call(this);
6889 };
6890
6891 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6892     fireEvent : function(evtName){
6893         // first do standard event for this node
6894         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6895             return false;
6896         }
6897         // then bubble it up to the tree if the event wasn't cancelled
6898         var ot = this.getOwnerTree();
6899         if(ot){
6900             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6901                 return false;
6902             }
6903         }
6904         return true;
6905     },
6906
6907     /**
6908      * Returns true if this node is a leaf
6909      * @return {Boolean}
6910      */
6911     isLeaf : function(){
6912         return this.leaf === true;
6913     },
6914
6915     // private
6916     setFirstChild : function(node){
6917         this.firstChild = node;
6918     },
6919
6920     //private
6921     setLastChild : function(node){
6922         this.lastChild = node;
6923     },
6924
6925
6926     /**
6927      * Returns true if this node is the last child of its parent
6928      * @return {Boolean}
6929      */
6930     isLast : function(){
6931        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6932     },
6933
6934     /**
6935      * Returns true if this node is the first child of its parent
6936      * @return {Boolean}
6937      */
6938     isFirst : function(){
6939        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6940     },
6941
6942     hasChildNodes : function(){
6943         return !this.isLeaf() && this.childNodes.length > 0;
6944     },
6945
6946     /**
6947      * Insert node(s) as the last child node of this node.
6948      * @param {Node/Array} node The node or Array of nodes to append
6949      * @return {Node} The appended node if single append, or null if an array was passed
6950      */
6951     appendChild : function(node){
6952         var multi = false;
6953         if(node instanceof Array){
6954             multi = node;
6955         }else if(arguments.length > 1){
6956             multi = arguments;
6957         }
6958         // if passed an array or multiple args do them one by one
6959         if(multi){
6960             for(var i = 0, len = multi.length; i < len; i++) {
6961                 this.appendChild(multi[i]);
6962             }
6963         }else{
6964             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6965                 return false;
6966             }
6967             var index = this.childNodes.length;
6968             var oldParent = node.parentNode;
6969             // it's a move, make sure we move it cleanly
6970             if(oldParent){
6971                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6972                     return false;
6973                 }
6974                 oldParent.removeChild(node);
6975             }
6976             index = this.childNodes.length;
6977             if(index == 0){
6978                 this.setFirstChild(node);
6979             }
6980             this.childNodes.push(node);
6981             node.parentNode = this;
6982             var ps = this.childNodes[index-1];
6983             if(ps){
6984                 node.previousSibling = ps;
6985                 ps.nextSibling = node;
6986             }else{
6987                 node.previousSibling = null;
6988             }
6989             node.nextSibling = null;
6990             this.setLastChild(node);
6991             node.setOwnerTree(this.getOwnerTree());
6992             this.fireEvent("append", this.ownerTree, this, node, index);
6993             if(oldParent){
6994                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6995             }
6996             return node;
6997         }
6998     },
6999
7000     /**
7001      * Removes a child node from this node.
7002      * @param {Node} node The node to remove
7003      * @return {Node} The removed node
7004      */
7005     removeChild : function(node){
7006         var index = this.childNodes.indexOf(node);
7007         if(index == -1){
7008             return false;
7009         }
7010         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
7011             return false;
7012         }
7013
7014         // remove it from childNodes collection
7015         this.childNodes.splice(index, 1);
7016
7017         // update siblings
7018         if(node.previousSibling){
7019             node.previousSibling.nextSibling = node.nextSibling;
7020         }
7021         if(node.nextSibling){
7022             node.nextSibling.previousSibling = node.previousSibling;
7023         }
7024
7025         // update child refs
7026         if(this.firstChild == node){
7027             this.setFirstChild(node.nextSibling);
7028         }
7029         if(this.lastChild == node){
7030             this.setLastChild(node.previousSibling);
7031         }
7032
7033         node.setOwnerTree(null);
7034         // clear any references from the node
7035         node.parentNode = null;
7036         node.previousSibling = null;
7037         node.nextSibling = null;
7038         this.fireEvent("remove", this.ownerTree, this, node);
7039         return node;
7040     },
7041
7042     /**
7043      * Inserts the first node before the second node in this nodes childNodes collection.
7044      * @param {Node} node The node to insert
7045      * @param {Node} refNode The node to insert before (if null the node is appended)
7046      * @return {Node} The inserted node
7047      */
7048     insertBefore : function(node, refNode){
7049         if(!refNode){ // like standard Dom, refNode can be null for append
7050             return this.appendChild(node);
7051         }
7052         // nothing to do
7053         if(node == refNode){
7054             return false;
7055         }
7056
7057         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7058             return false;
7059         }
7060         var index = this.childNodes.indexOf(refNode);
7061         var oldParent = node.parentNode;
7062         var refIndex = index;
7063
7064         // when moving internally, indexes will change after remove
7065         if(oldParent == this && this.childNodes.indexOf(node) < index){
7066             refIndex--;
7067         }
7068
7069         // it's a move, make sure we move it cleanly
7070         if(oldParent){
7071             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7072                 return false;
7073             }
7074             oldParent.removeChild(node);
7075         }
7076         if(refIndex == 0){
7077             this.setFirstChild(node);
7078         }
7079         this.childNodes.splice(refIndex, 0, node);
7080         node.parentNode = this;
7081         var ps = this.childNodes[refIndex-1];
7082         if(ps){
7083             node.previousSibling = ps;
7084             ps.nextSibling = node;
7085         }else{
7086             node.previousSibling = null;
7087         }
7088         node.nextSibling = refNode;
7089         refNode.previousSibling = node;
7090         node.setOwnerTree(this.getOwnerTree());
7091         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7092         if(oldParent){
7093             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7094         }
7095         return node;
7096     },
7097
7098     /**
7099      * Returns the child node at the specified index.
7100      * @param {Number} index
7101      * @return {Node}
7102      */
7103     item : function(index){
7104         return this.childNodes[index];
7105     },
7106
7107     /**
7108      * Replaces one child node in this node with another.
7109      * @param {Node} newChild The replacement node
7110      * @param {Node} oldChild The node to replace
7111      * @return {Node} The replaced node
7112      */
7113     replaceChild : function(newChild, oldChild){
7114         this.insertBefore(newChild, oldChild);
7115         this.removeChild(oldChild);
7116         return oldChild;
7117     },
7118
7119     /**
7120      * Returns the index of a child node
7121      * @param {Node} node
7122      * @return {Number} The index of the node or -1 if it was not found
7123      */
7124     indexOf : function(child){
7125         return this.childNodes.indexOf(child);
7126     },
7127
7128     /**
7129      * Returns the tree this node is in.
7130      * @return {Tree}
7131      */
7132     getOwnerTree : function(){
7133         // if it doesn't have one, look for one
7134         if(!this.ownerTree){
7135             var p = this;
7136             while(p){
7137                 if(p.ownerTree){
7138                     this.ownerTree = p.ownerTree;
7139                     break;
7140                 }
7141                 p = p.parentNode;
7142             }
7143         }
7144         return this.ownerTree;
7145     },
7146
7147     /**
7148      * Returns depth of this node (the root node has a depth of 0)
7149      * @return {Number}
7150      */
7151     getDepth : function(){
7152         var depth = 0;
7153         var p = this;
7154         while(p.parentNode){
7155             ++depth;
7156             p = p.parentNode;
7157         }
7158         return depth;
7159     },
7160
7161     // private
7162     setOwnerTree : function(tree){
7163         // if it's move, we need to update everyone
7164         if(tree != this.ownerTree){
7165             if(this.ownerTree){
7166                 this.ownerTree.unregisterNode(this);
7167             }
7168             this.ownerTree = tree;
7169             var cs = this.childNodes;
7170             for(var i = 0, len = cs.length; i < len; i++) {
7171                 cs[i].setOwnerTree(tree);
7172             }
7173             if(tree){
7174                 tree.registerNode(this);
7175             }
7176         }
7177     },
7178
7179     /**
7180      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7181      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7182      * @return {String} The path
7183      */
7184     getPath : function(attr){
7185         attr = attr || "id";
7186         var p = this.parentNode;
7187         var b = [this.attributes[attr]];
7188         while(p){
7189             b.unshift(p.attributes[attr]);
7190             p = p.parentNode;
7191         }
7192         var sep = this.getOwnerTree().pathSeparator;
7193         return sep + b.join(sep);
7194     },
7195
7196     /**
7197      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7198      * function call will be the scope provided or the current node. The arguments to the function
7199      * will be the args provided or the current node. If the function returns false at any point,
7200      * the bubble is stopped.
7201      * @param {Function} fn The function to call
7202      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7203      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7204      */
7205     bubble : function(fn, scope, args){
7206         var p = this;
7207         while(p){
7208             if(fn.call(scope || p, args || p) === false){
7209                 break;
7210             }
7211             p = p.parentNode;
7212         }
7213     },
7214
7215     /**
7216      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7217      * function call will be the scope provided or the current node. The arguments to the function
7218      * will be the args provided or the current node. If the function returns false at any point,
7219      * the cascade is stopped on that branch.
7220      * @param {Function} fn The function to call
7221      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7222      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7223      */
7224     cascade : function(fn, scope, args){
7225         if(fn.call(scope || this, args || this) !== false){
7226             var cs = this.childNodes;
7227             for(var i = 0, len = cs.length; i < len; i++) {
7228                 cs[i].cascade(fn, scope, args);
7229             }
7230         }
7231     },
7232
7233     /**
7234      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7235      * function call will be the scope provided or the current node. The arguments to the function
7236      * will be the args provided or the current node. If the function returns false at any point,
7237      * the iteration stops.
7238      * @param {Function} fn The function to call
7239      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7240      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7241      */
7242     eachChild : function(fn, scope, args){
7243         var cs = this.childNodes;
7244         for(var i = 0, len = cs.length; i < len; i++) {
7245                 if(fn.call(scope || this, args || cs[i]) === false){
7246                     break;
7247                 }
7248         }
7249     },
7250
7251     /**
7252      * Finds the first child that has the attribute with the specified value.
7253      * @param {String} attribute The attribute name
7254      * @param {Mixed} value The value to search for
7255      * @return {Node} The found child or null if none was found
7256      */
7257     findChild : function(attribute, value){
7258         var cs = this.childNodes;
7259         for(var i = 0, len = cs.length; i < len; i++) {
7260                 if(cs[i].attributes[attribute] == value){
7261                     return cs[i];
7262                 }
7263         }
7264         return null;
7265     },
7266
7267     /**
7268      * Finds the first child by a custom function. The child matches if the function passed
7269      * returns true.
7270      * @param {Function} fn
7271      * @param {Object} scope (optional)
7272      * @return {Node} The found child or null if none was found
7273      */
7274     findChildBy : function(fn, scope){
7275         var cs = this.childNodes;
7276         for(var i = 0, len = cs.length; i < len; i++) {
7277                 if(fn.call(scope||cs[i], cs[i]) === true){
7278                     return cs[i];
7279                 }
7280         }
7281         return null;
7282     },
7283
7284     /**
7285      * Sorts this nodes children using the supplied sort function
7286      * @param {Function} fn
7287      * @param {Object} scope (optional)
7288      */
7289     sort : function(fn, scope){
7290         var cs = this.childNodes;
7291         var len = cs.length;
7292         if(len > 0){
7293             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7294             cs.sort(sortFn);
7295             for(var i = 0; i < len; i++){
7296                 var n = cs[i];
7297                 n.previousSibling = cs[i-1];
7298                 n.nextSibling = cs[i+1];
7299                 if(i == 0){
7300                     this.setFirstChild(n);
7301                 }
7302                 if(i == len-1){
7303                     this.setLastChild(n);
7304                 }
7305             }
7306         }
7307     },
7308
7309     /**
7310      * Returns true if this node is an ancestor (at any point) of the passed node.
7311      * @param {Node} node
7312      * @return {Boolean}
7313      */
7314     contains : function(node){
7315         return node.isAncestor(this);
7316     },
7317
7318     /**
7319      * Returns true if the passed node is an ancestor (at any point) of this node.
7320      * @param {Node} node
7321      * @return {Boolean}
7322      */
7323     isAncestor : function(node){
7324         var p = this.parentNode;
7325         while(p){
7326             if(p == node){
7327                 return true;
7328             }
7329             p = p.parentNode;
7330         }
7331         return false;
7332     },
7333
7334     toString : function(){
7335         return "[Node"+(this.id?" "+this.id:"")+"]";
7336     }
7337 });/*
7338  * Based on:
7339  * Ext JS Library 1.1.1
7340  * Copyright(c) 2006-2007, Ext JS, LLC.
7341  *
7342  * Originally Released Under LGPL - original licence link has changed is not relivant.
7343  *
7344  * Fork - LGPL
7345  * <script type="text/javascript">
7346  */
7347  (function(){ 
7348 /**
7349  * @class Roo.Layer
7350  * @extends Roo.Element
7351  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7352  * automatic maintaining of shadow/shim positions.
7353  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7354  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7355  * you can pass a string with a CSS class name. False turns off the shadow.
7356  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7357  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7358  * @cfg {String} cls CSS class to add to the element
7359  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7360  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7361  * @constructor
7362  * @param {Object} config An object with config options.
7363  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7364  */
7365
7366 Roo.Layer = function(config, existingEl){
7367     config = config || {};
7368     var dh = Roo.DomHelper;
7369     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7370     if(existingEl){
7371         this.dom = Roo.getDom(existingEl);
7372     }
7373     if(!this.dom){
7374         var o = config.dh || {tag: "div", cls: "x-layer"};
7375         this.dom = dh.append(pel, o);
7376     }
7377     if(config.cls){
7378         this.addClass(config.cls);
7379     }
7380     this.constrain = config.constrain !== false;
7381     this.visibilityMode = Roo.Element.VISIBILITY;
7382     if(config.id){
7383         this.id = this.dom.id = config.id;
7384     }else{
7385         this.id = Roo.id(this.dom);
7386     }
7387     this.zindex = config.zindex || this.getZIndex();
7388     this.position("absolute", this.zindex);
7389     if(config.shadow){
7390         this.shadowOffset = config.shadowOffset || 4;
7391         this.shadow = new Roo.Shadow({
7392             offset : this.shadowOffset,
7393             mode : config.shadow
7394         });
7395     }else{
7396         this.shadowOffset = 0;
7397     }
7398     this.useShim = config.shim !== false && Roo.useShims;
7399     this.useDisplay = config.useDisplay;
7400     this.hide();
7401 };
7402
7403 var supr = Roo.Element.prototype;
7404
7405 // shims are shared among layer to keep from having 100 iframes
7406 var shims = [];
7407
7408 Roo.extend(Roo.Layer, Roo.Element, {
7409
7410     getZIndex : function(){
7411         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7412     },
7413
7414     getShim : function(){
7415         if(!this.useShim){
7416             return null;
7417         }
7418         if(this.shim){
7419             return this.shim;
7420         }
7421         var shim = shims.shift();
7422         if(!shim){
7423             shim = this.createShim();
7424             shim.enableDisplayMode('block');
7425             shim.dom.style.display = 'none';
7426             shim.dom.style.visibility = 'visible';
7427         }
7428         var pn = this.dom.parentNode;
7429         if(shim.dom.parentNode != pn){
7430             pn.insertBefore(shim.dom, this.dom);
7431         }
7432         shim.setStyle('z-index', this.getZIndex()-2);
7433         this.shim = shim;
7434         return shim;
7435     },
7436
7437     hideShim : function(){
7438         if(this.shim){
7439             this.shim.setDisplayed(false);
7440             shims.push(this.shim);
7441             delete this.shim;
7442         }
7443     },
7444
7445     disableShadow : function(){
7446         if(this.shadow){
7447             this.shadowDisabled = true;
7448             this.shadow.hide();
7449             this.lastShadowOffset = this.shadowOffset;
7450             this.shadowOffset = 0;
7451         }
7452     },
7453
7454     enableShadow : function(show){
7455         if(this.shadow){
7456             this.shadowDisabled = false;
7457             this.shadowOffset = this.lastShadowOffset;
7458             delete this.lastShadowOffset;
7459             if(show){
7460                 this.sync(true);
7461             }
7462         }
7463     },
7464
7465     // private
7466     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7467     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7468     sync : function(doShow){
7469         var sw = this.shadow;
7470         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7471             var sh = this.getShim();
7472
7473             var w = this.getWidth(),
7474                 h = this.getHeight();
7475
7476             var l = this.getLeft(true),
7477                 t = this.getTop(true);
7478
7479             if(sw && !this.shadowDisabled){
7480                 if(doShow && !sw.isVisible()){
7481                     sw.show(this);
7482                 }else{
7483                     sw.realign(l, t, w, h);
7484                 }
7485                 if(sh){
7486                     if(doShow){
7487                        sh.show();
7488                     }
7489                     // fit the shim behind the shadow, so it is shimmed too
7490                     var a = sw.adjusts, s = sh.dom.style;
7491                     s.left = (Math.min(l, l+a.l))+"px";
7492                     s.top = (Math.min(t, t+a.t))+"px";
7493                     s.width = (w+a.w)+"px";
7494                     s.height = (h+a.h)+"px";
7495                 }
7496             }else if(sh){
7497                 if(doShow){
7498                    sh.show();
7499                 }
7500                 sh.setSize(w, h);
7501                 sh.setLeftTop(l, t);
7502             }
7503             
7504         }
7505     },
7506
7507     // private
7508     destroy : function(){
7509         this.hideShim();
7510         if(this.shadow){
7511             this.shadow.hide();
7512         }
7513         this.removeAllListeners();
7514         var pn = this.dom.parentNode;
7515         if(pn){
7516             pn.removeChild(this.dom);
7517         }
7518         Roo.Element.uncache(this.id);
7519     },
7520
7521     remove : function(){
7522         this.destroy();
7523     },
7524
7525     // private
7526     beginUpdate : function(){
7527         this.updating = true;
7528     },
7529
7530     // private
7531     endUpdate : function(){
7532         this.updating = false;
7533         this.sync(true);
7534     },
7535
7536     // private
7537     hideUnders : function(negOffset){
7538         if(this.shadow){
7539             this.shadow.hide();
7540         }
7541         this.hideShim();
7542     },
7543
7544     // private
7545     constrainXY : function(){
7546         if(this.constrain){
7547             var vw = Roo.lib.Dom.getViewWidth(),
7548                 vh = Roo.lib.Dom.getViewHeight();
7549             var s = Roo.get(document).getScroll();
7550
7551             var xy = this.getXY();
7552             var x = xy[0], y = xy[1];   
7553             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7554             // only move it if it needs it
7555             var moved = false;
7556             // first validate right/bottom
7557             if((x + w) > vw+s.left){
7558                 x = vw - w - this.shadowOffset;
7559                 moved = true;
7560             }
7561             if((y + h) > vh+s.top){
7562                 y = vh - h - this.shadowOffset;
7563                 moved = true;
7564             }
7565             // then make sure top/left isn't negative
7566             if(x < s.left){
7567                 x = s.left;
7568                 moved = true;
7569             }
7570             if(y < s.top){
7571                 y = s.top;
7572                 moved = true;
7573             }
7574             if(moved){
7575                 if(this.avoidY){
7576                     var ay = this.avoidY;
7577                     if(y <= ay && (y+h) >= ay){
7578                         y = ay-h-5;   
7579                     }
7580                 }
7581                 xy = [x, y];
7582                 this.storeXY(xy);
7583                 supr.setXY.call(this, xy);
7584                 this.sync();
7585             }
7586         }
7587     },
7588
7589     isVisible : function(){
7590         return this.visible;    
7591     },
7592
7593     // private
7594     showAction : function(){
7595         this.visible = true; // track visibility to prevent getStyle calls
7596         if(this.useDisplay === true){
7597             this.setDisplayed("");
7598         }else if(this.lastXY){
7599             supr.setXY.call(this, this.lastXY);
7600         }else if(this.lastLT){
7601             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7602         }
7603     },
7604
7605     // private
7606     hideAction : function(){
7607         this.visible = false;
7608         if(this.useDisplay === true){
7609             this.setDisplayed(false);
7610         }else{
7611             this.setLeftTop(-10000,-10000);
7612         }
7613     },
7614
7615     // overridden Element method
7616     setVisible : function(v, a, d, c, e){
7617         if(v){
7618             this.showAction();
7619         }
7620         if(a && v){
7621             var cb = function(){
7622                 this.sync(true);
7623                 if(c){
7624                     c();
7625                 }
7626             }.createDelegate(this);
7627             supr.setVisible.call(this, true, true, d, cb, e);
7628         }else{
7629             if(!v){
7630                 this.hideUnders(true);
7631             }
7632             var cb = c;
7633             if(a){
7634                 cb = function(){
7635                     this.hideAction();
7636                     if(c){
7637                         c();
7638                     }
7639                 }.createDelegate(this);
7640             }
7641             supr.setVisible.call(this, v, a, d, cb, e);
7642             if(v){
7643                 this.sync(true);
7644             }else if(!a){
7645                 this.hideAction();
7646             }
7647         }
7648     },
7649
7650     storeXY : function(xy){
7651         delete this.lastLT;
7652         this.lastXY = xy;
7653     },
7654
7655     storeLeftTop : function(left, top){
7656         delete this.lastXY;
7657         this.lastLT = [left, top];
7658     },
7659
7660     // private
7661     beforeFx : function(){
7662         this.beforeAction();
7663         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7664     },
7665
7666     // private
7667     afterFx : function(){
7668         Roo.Layer.superclass.afterFx.apply(this, arguments);
7669         this.sync(this.isVisible());
7670     },
7671
7672     // private
7673     beforeAction : function(){
7674         if(!this.updating && this.shadow){
7675             this.shadow.hide();
7676         }
7677     },
7678
7679     // overridden Element method
7680     setLeft : function(left){
7681         this.storeLeftTop(left, this.getTop(true));
7682         supr.setLeft.apply(this, arguments);
7683         this.sync();
7684     },
7685
7686     setTop : function(top){
7687         this.storeLeftTop(this.getLeft(true), top);
7688         supr.setTop.apply(this, arguments);
7689         this.sync();
7690     },
7691
7692     setLeftTop : function(left, top){
7693         this.storeLeftTop(left, top);
7694         supr.setLeftTop.apply(this, arguments);
7695         this.sync();
7696     },
7697
7698     setXY : function(xy, a, d, c, e){
7699         this.fixDisplay();
7700         this.beforeAction();
7701         this.storeXY(xy);
7702         var cb = this.createCB(c);
7703         supr.setXY.call(this, xy, a, d, cb, e);
7704         if(!a){
7705             cb();
7706         }
7707     },
7708
7709     // private
7710     createCB : function(c){
7711         var el = this;
7712         return function(){
7713             el.constrainXY();
7714             el.sync(true);
7715             if(c){
7716                 c();
7717             }
7718         };
7719     },
7720
7721     // overridden Element method
7722     setX : function(x, a, d, c, e){
7723         this.setXY([x, this.getY()], a, d, c, e);
7724     },
7725
7726     // overridden Element method
7727     setY : function(y, a, d, c, e){
7728         this.setXY([this.getX(), y], a, d, c, e);
7729     },
7730
7731     // overridden Element method
7732     setSize : function(w, h, a, d, c, e){
7733         this.beforeAction();
7734         var cb = this.createCB(c);
7735         supr.setSize.call(this, w, h, a, d, cb, e);
7736         if(!a){
7737             cb();
7738         }
7739     },
7740
7741     // overridden Element method
7742     setWidth : function(w, a, d, c, e){
7743         this.beforeAction();
7744         var cb = this.createCB(c);
7745         supr.setWidth.call(this, w, a, d, cb, e);
7746         if(!a){
7747             cb();
7748         }
7749     },
7750
7751     // overridden Element method
7752     setHeight : function(h, a, d, c, e){
7753         this.beforeAction();
7754         var cb = this.createCB(c);
7755         supr.setHeight.call(this, h, a, d, cb, e);
7756         if(!a){
7757             cb();
7758         }
7759     },
7760
7761     // overridden Element method
7762     setBounds : function(x, y, w, h, a, d, c, e){
7763         this.beforeAction();
7764         var cb = this.createCB(c);
7765         if(!a){
7766             this.storeXY([x, y]);
7767             supr.setXY.call(this, [x, y]);
7768             supr.setSize.call(this, w, h, a, d, cb, e);
7769             cb();
7770         }else{
7771             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7772         }
7773         return this;
7774     },
7775     
7776     /**
7777      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7778      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7779      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7780      * @param {Number} zindex The new z-index to set
7781      * @return {this} The Layer
7782      */
7783     setZIndex : function(zindex){
7784         this.zindex = zindex;
7785         this.setStyle("z-index", zindex + 2);
7786         if(this.shadow){
7787             this.shadow.setZIndex(zindex + 1);
7788         }
7789         if(this.shim){
7790             this.shim.setStyle("z-index", zindex);
7791         }
7792     }
7793 });
7794 })();/*
7795  * Based on:
7796  * Ext JS Library 1.1.1
7797  * Copyright(c) 2006-2007, Ext JS, LLC.
7798  *
7799  * Originally Released Under LGPL - original licence link has changed is not relivant.
7800  *
7801  * Fork - LGPL
7802  * <script type="text/javascript">
7803  */
7804
7805
7806 /**
7807  * @class Roo.Shadow
7808  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7809  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7810  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7811  * @constructor
7812  * Create a new Shadow
7813  * @param {Object} config The config object
7814  */
7815 Roo.Shadow = function(config){
7816     Roo.apply(this, config);
7817     if(typeof this.mode != "string"){
7818         this.mode = this.defaultMode;
7819     }
7820     var o = this.offset, a = {h: 0};
7821     var rad = Math.floor(this.offset/2);
7822     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7823         case "drop":
7824             a.w = 0;
7825             a.l = a.t = o;
7826             a.t -= 1;
7827             if(Roo.isIE){
7828                 a.l -= this.offset + rad;
7829                 a.t -= this.offset + rad;
7830                 a.w -= rad;
7831                 a.h -= rad;
7832                 a.t += 1;
7833             }
7834         break;
7835         case "sides":
7836             a.w = (o*2);
7837             a.l = -o;
7838             a.t = o-1;
7839             if(Roo.isIE){
7840                 a.l -= (this.offset - rad);
7841                 a.t -= this.offset + rad;
7842                 a.l += 1;
7843                 a.w -= (this.offset - rad)*2;
7844                 a.w -= rad + 1;
7845                 a.h -= 1;
7846             }
7847         break;
7848         case "frame":
7849             a.w = a.h = (o*2);
7850             a.l = a.t = -o;
7851             a.t += 1;
7852             a.h -= 2;
7853             if(Roo.isIE){
7854                 a.l -= (this.offset - rad);
7855                 a.t -= (this.offset - rad);
7856                 a.l += 1;
7857                 a.w -= (this.offset + rad + 1);
7858                 a.h -= (this.offset + rad);
7859                 a.h += 1;
7860             }
7861         break;
7862     };
7863
7864     this.adjusts = a;
7865 };
7866
7867 Roo.Shadow.prototype = {
7868     /**
7869      * @cfg {String} mode
7870      * The shadow display mode.  Supports the following options:<br />
7871      * sides: Shadow displays on both sides and bottom only<br />
7872      * frame: Shadow displays equally on all four sides<br />
7873      * drop: Traditional bottom-right drop shadow (default)
7874      */
7875     /**
7876      * @cfg {String} offset
7877      * The number of pixels to offset the shadow from the element (defaults to 4)
7878      */
7879     offset: 4,
7880
7881     // private
7882     defaultMode: "drop",
7883
7884     /**
7885      * Displays the shadow under the target element
7886      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7887      */
7888     show : function(target){
7889         target = Roo.get(target);
7890         if(!this.el){
7891             this.el = Roo.Shadow.Pool.pull();
7892             if(this.el.dom.nextSibling != target.dom){
7893                 this.el.insertBefore(target);
7894             }
7895         }
7896         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7897         if(Roo.isIE){
7898             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7899         }
7900         this.realign(
7901             target.getLeft(true),
7902             target.getTop(true),
7903             target.getWidth(),
7904             target.getHeight()
7905         );
7906         this.el.dom.style.display = "block";
7907     },
7908
7909     /**
7910      * Returns true if the shadow is visible, else false
7911      */
7912     isVisible : function(){
7913         return this.el ? true : false;  
7914     },
7915
7916     /**
7917      * Direct alignment when values are already available. Show must be called at least once before
7918      * calling this method to ensure it is initialized.
7919      * @param {Number} left The target element left position
7920      * @param {Number} top The target element top position
7921      * @param {Number} width The target element width
7922      * @param {Number} height The target element height
7923      */
7924     realign : function(l, t, w, h){
7925         if(!this.el){
7926             return;
7927         }
7928         var a = this.adjusts, d = this.el.dom, s = d.style;
7929         var iea = 0;
7930         s.left = (l+a.l)+"px";
7931         s.top = (t+a.t)+"px";
7932         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7933  
7934         if(s.width != sws || s.height != shs){
7935             s.width = sws;
7936             s.height = shs;
7937             if(!Roo.isIE){
7938                 var cn = d.childNodes;
7939                 var sww = Math.max(0, (sw-12))+"px";
7940                 cn[0].childNodes[1].style.width = sww;
7941                 cn[1].childNodes[1].style.width = sww;
7942                 cn[2].childNodes[1].style.width = sww;
7943                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7944             }
7945         }
7946     },
7947
7948     /**
7949      * Hides this shadow
7950      */
7951     hide : function(){
7952         if(this.el){
7953             this.el.dom.style.display = "none";
7954             Roo.Shadow.Pool.push(this.el);
7955             delete this.el;
7956         }
7957     },
7958
7959     /**
7960      * Adjust the z-index of this shadow
7961      * @param {Number} zindex The new z-index
7962      */
7963     setZIndex : function(z){
7964         this.zIndex = z;
7965         if(this.el){
7966             this.el.setStyle("z-index", z);
7967         }
7968     }
7969 };
7970
7971 // Private utility class that manages the internal Shadow cache
7972 Roo.Shadow.Pool = function(){
7973     var p = [];
7974     var markup = Roo.isIE ?
7975                  '<div class="x-ie-shadow"></div>' :
7976                  '<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>';
7977     return {
7978         pull : function(){
7979             var sh = p.shift();
7980             if(!sh){
7981                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7982                 sh.autoBoxAdjust = false;
7983             }
7984             return sh;
7985         },
7986
7987         push : function(sh){
7988             p.push(sh);
7989         }
7990     };
7991 }();/*
7992  * Based on:
7993  * Ext JS Library 1.1.1
7994  * Copyright(c) 2006-2007, Ext JS, LLC.
7995  *
7996  * Originally Released Under LGPL - original licence link has changed is not relivant.
7997  *
7998  * Fork - LGPL
7999  * <script type="text/javascript">
8000  */
8001
8002
8003 /**
8004  * @class Roo.SplitBar
8005  * @extends Roo.util.Observable
8006  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8007  * <br><br>
8008  * Usage:
8009  * <pre><code>
8010 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8011                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8012 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8013 split.minSize = 100;
8014 split.maxSize = 600;
8015 split.animate = true;
8016 split.on('moved', splitterMoved);
8017 </code></pre>
8018  * @constructor
8019  * Create a new SplitBar
8020  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8021  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8022  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8023  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8024                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8025                         position of the SplitBar).
8026  */
8027 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8028     
8029     /** @private */
8030     this.el = Roo.get(dragElement, true);
8031     this.el.dom.unselectable = "on";
8032     /** @private */
8033     this.resizingEl = Roo.get(resizingElement, true);
8034
8035     /**
8036      * @private
8037      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8038      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8039      * @type Number
8040      */
8041     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8042     
8043     /**
8044      * The minimum size of the resizing element. (Defaults to 0)
8045      * @type Number
8046      */
8047     this.minSize = 0;
8048     
8049     /**
8050      * The maximum size of the resizing element. (Defaults to 2000)
8051      * @type Number
8052      */
8053     this.maxSize = 2000;
8054     
8055     /**
8056      * Whether to animate the transition to the new size
8057      * @type Boolean
8058      */
8059     this.animate = false;
8060     
8061     /**
8062      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8063      * @type Boolean
8064      */
8065     this.useShim = false;
8066     
8067     /** @private */
8068     this.shim = null;
8069     
8070     if(!existingProxy){
8071         /** @private */
8072         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8073     }else{
8074         this.proxy = Roo.get(existingProxy).dom;
8075     }
8076     /** @private */
8077     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8078     
8079     /** @private */
8080     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8081     
8082     /** @private */
8083     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8084     
8085     /** @private */
8086     this.dragSpecs = {};
8087     
8088     /**
8089      * @private The adapter to use to positon and resize elements
8090      */
8091     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8092     this.adapter.init(this);
8093     
8094     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8095         /** @private */
8096         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8097         this.el.addClass("x-splitbar-h");
8098     }else{
8099         /** @private */
8100         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8101         this.el.addClass("x-splitbar-v");
8102     }
8103     
8104     this.addEvents({
8105         /**
8106          * @event resize
8107          * Fires when the splitter is moved (alias for {@link #event-moved})
8108          * @param {Roo.SplitBar} this
8109          * @param {Number} newSize the new width or height
8110          */
8111         "resize" : true,
8112         /**
8113          * @event moved
8114          * Fires when the splitter is moved
8115          * @param {Roo.SplitBar} this
8116          * @param {Number} newSize the new width or height
8117          */
8118         "moved" : true,
8119         /**
8120          * @event beforeresize
8121          * Fires before the splitter is dragged
8122          * @param {Roo.SplitBar} this
8123          */
8124         "beforeresize" : true,
8125
8126         "beforeapply" : true
8127     });
8128
8129     Roo.util.Observable.call(this);
8130 };
8131
8132 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8133     onStartProxyDrag : function(x, y){
8134         this.fireEvent("beforeresize", this);
8135         if(!this.overlay){
8136             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8137             o.unselectable();
8138             o.enableDisplayMode("block");
8139             // all splitbars share the same overlay
8140             Roo.SplitBar.prototype.overlay = o;
8141         }
8142         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8143         this.overlay.show();
8144         Roo.get(this.proxy).setDisplayed("block");
8145         var size = this.adapter.getElementSize(this);
8146         this.activeMinSize = this.getMinimumSize();;
8147         this.activeMaxSize = this.getMaximumSize();;
8148         var c1 = size - this.activeMinSize;
8149         var c2 = Math.max(this.activeMaxSize - size, 0);
8150         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8151             this.dd.resetConstraints();
8152             this.dd.setXConstraint(
8153                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8154                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8155             );
8156             this.dd.setYConstraint(0, 0);
8157         }else{
8158             this.dd.resetConstraints();
8159             this.dd.setXConstraint(0, 0);
8160             this.dd.setYConstraint(
8161                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8162                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8163             );
8164          }
8165         this.dragSpecs.startSize = size;
8166         this.dragSpecs.startPoint = [x, y];
8167         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8168     },
8169     
8170     /** 
8171      * @private Called after the drag operation by the DDProxy
8172      */
8173     onEndProxyDrag : function(e){
8174         Roo.get(this.proxy).setDisplayed(false);
8175         var endPoint = Roo.lib.Event.getXY(e);
8176         if(this.overlay){
8177             this.overlay.hide();
8178         }
8179         var newSize;
8180         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8181             newSize = this.dragSpecs.startSize + 
8182                 (this.placement == Roo.SplitBar.LEFT ?
8183                     endPoint[0] - this.dragSpecs.startPoint[0] :
8184                     this.dragSpecs.startPoint[0] - endPoint[0]
8185                 );
8186         }else{
8187             newSize = this.dragSpecs.startSize + 
8188                 (this.placement == Roo.SplitBar.TOP ?
8189                     endPoint[1] - this.dragSpecs.startPoint[1] :
8190                     this.dragSpecs.startPoint[1] - endPoint[1]
8191                 );
8192         }
8193         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8194         if(newSize != this.dragSpecs.startSize){
8195             if(this.fireEvent('beforeapply', this, newSize) !== false){
8196                 this.adapter.setElementSize(this, newSize);
8197                 this.fireEvent("moved", this, newSize);
8198                 this.fireEvent("resize", this, newSize);
8199             }
8200         }
8201     },
8202     
8203     /**
8204      * Get the adapter this SplitBar uses
8205      * @return The adapter object
8206      */
8207     getAdapter : function(){
8208         return this.adapter;
8209     },
8210     
8211     /**
8212      * Set the adapter this SplitBar uses
8213      * @param {Object} adapter A SplitBar adapter object
8214      */
8215     setAdapter : function(adapter){
8216         this.adapter = adapter;
8217         this.adapter.init(this);
8218     },
8219     
8220     /**
8221      * Gets the minimum size for the resizing element
8222      * @return {Number} The minimum size
8223      */
8224     getMinimumSize : function(){
8225         return this.minSize;
8226     },
8227     
8228     /**
8229      * Sets the minimum size for the resizing element
8230      * @param {Number} minSize The minimum size
8231      */
8232     setMinimumSize : function(minSize){
8233         this.minSize = minSize;
8234     },
8235     
8236     /**
8237      * Gets the maximum size for the resizing element
8238      * @return {Number} The maximum size
8239      */
8240     getMaximumSize : function(){
8241         return this.maxSize;
8242     },
8243     
8244     /**
8245      * Sets the maximum size for the resizing element
8246      * @param {Number} maxSize The maximum size
8247      */
8248     setMaximumSize : function(maxSize){
8249         this.maxSize = maxSize;
8250     },
8251     
8252     /**
8253      * Sets the initialize size for the resizing element
8254      * @param {Number} size The initial size
8255      */
8256     setCurrentSize : function(size){
8257         var oldAnimate = this.animate;
8258         this.animate = false;
8259         this.adapter.setElementSize(this, size);
8260         this.animate = oldAnimate;
8261     },
8262     
8263     /**
8264      * Destroy this splitbar. 
8265      * @param {Boolean} removeEl True to remove the element
8266      */
8267     destroy : function(removeEl){
8268         if(this.shim){
8269             this.shim.remove();
8270         }
8271         this.dd.unreg();
8272         this.proxy.parentNode.removeChild(this.proxy);
8273         if(removeEl){
8274             this.el.remove();
8275         }
8276     }
8277 });
8278
8279 /**
8280  * @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.
8281  */
8282 Roo.SplitBar.createProxy = function(dir){
8283     var proxy = new Roo.Element(document.createElement("div"));
8284     proxy.unselectable();
8285     var cls = 'x-splitbar-proxy';
8286     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8287     document.body.appendChild(proxy.dom);
8288     return proxy.dom;
8289 };
8290
8291 /** 
8292  * @class Roo.SplitBar.BasicLayoutAdapter
8293  * Default Adapter. It assumes the splitter and resizing element are not positioned
8294  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8295  */
8296 Roo.SplitBar.BasicLayoutAdapter = function(){
8297 };
8298
8299 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8300     // do nothing for now
8301     init : function(s){
8302     
8303     },
8304     /**
8305      * Called before drag operations to get the current size of the resizing element. 
8306      * @param {Roo.SplitBar} s The SplitBar using this adapter
8307      */
8308      getElementSize : function(s){
8309         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8310             return s.resizingEl.getWidth();
8311         }else{
8312             return s.resizingEl.getHeight();
8313         }
8314     },
8315     
8316     /**
8317      * Called after drag operations to set the size of the resizing element.
8318      * @param {Roo.SplitBar} s The SplitBar using this adapter
8319      * @param {Number} newSize The new size to set
8320      * @param {Function} onComplete A function to be invoked when resizing is complete
8321      */
8322     setElementSize : function(s, newSize, onComplete){
8323         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8324             if(!s.animate){
8325                 s.resizingEl.setWidth(newSize);
8326                 if(onComplete){
8327                     onComplete(s, newSize);
8328                 }
8329             }else{
8330                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8331             }
8332         }else{
8333             
8334             if(!s.animate){
8335                 s.resizingEl.setHeight(newSize);
8336                 if(onComplete){
8337                     onComplete(s, newSize);
8338                 }
8339             }else{
8340                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8341             }
8342         }
8343     }
8344 };
8345
8346 /** 
8347  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8348  * @extends Roo.SplitBar.BasicLayoutAdapter
8349  * Adapter that  moves the splitter element to align with the resized sizing element. 
8350  * Used with an absolute positioned SplitBar.
8351  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8352  * document.body, make sure you assign an id to the body element.
8353  */
8354 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8355     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8356     this.container = Roo.get(container);
8357 };
8358
8359 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8360     init : function(s){
8361         this.basic.init(s);
8362     },
8363     
8364     getElementSize : function(s){
8365         return this.basic.getElementSize(s);
8366     },
8367     
8368     setElementSize : function(s, newSize, onComplete){
8369         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8370     },
8371     
8372     moveSplitter : function(s){
8373         var yes = Roo.SplitBar;
8374         switch(s.placement){
8375             case yes.LEFT:
8376                 s.el.setX(s.resizingEl.getRight());
8377                 break;
8378             case yes.RIGHT:
8379                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8380                 break;
8381             case yes.TOP:
8382                 s.el.setY(s.resizingEl.getBottom());
8383                 break;
8384             case yes.BOTTOM:
8385                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8386                 break;
8387         }
8388     }
8389 };
8390
8391 /**
8392  * Orientation constant - Create a vertical SplitBar
8393  * @static
8394  * @type Number
8395  */
8396 Roo.SplitBar.VERTICAL = 1;
8397
8398 /**
8399  * Orientation constant - Create a horizontal SplitBar
8400  * @static
8401  * @type Number
8402  */
8403 Roo.SplitBar.HORIZONTAL = 2;
8404
8405 /**
8406  * Placement constant - The resizing element is to the left of the splitter element
8407  * @static
8408  * @type Number
8409  */
8410 Roo.SplitBar.LEFT = 1;
8411
8412 /**
8413  * Placement constant - The resizing element is to the right of the splitter element
8414  * @static
8415  * @type Number
8416  */
8417 Roo.SplitBar.RIGHT = 2;
8418
8419 /**
8420  * Placement constant - The resizing element is positioned above the splitter element
8421  * @static
8422  * @type Number
8423  */
8424 Roo.SplitBar.TOP = 3;
8425
8426 /**
8427  * Placement constant - The resizing element is positioned under splitter element
8428  * @static
8429  * @type Number
8430  */
8431 Roo.SplitBar.BOTTOM = 4;
8432 /*
8433  * Based on:
8434  * Ext JS Library 1.1.1
8435  * Copyright(c) 2006-2007, Ext JS, LLC.
8436  *
8437  * Originally Released Under LGPL - original licence link has changed is not relivant.
8438  *
8439  * Fork - LGPL
8440  * <script type="text/javascript">
8441  */
8442
8443 /**
8444  * @class Roo.View
8445  * @extends Roo.util.Observable
8446  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8447  * This class also supports single and multi selection modes. <br>
8448  * Create a data model bound view:
8449  <pre><code>
8450  var store = new Roo.data.Store(...);
8451
8452  var view = new Roo.View({
8453     el : "my-element",
8454     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8455  
8456     singleSelect: true,
8457     selectedClass: "ydataview-selected",
8458     store: store
8459  });
8460
8461  // listen for node click?
8462  view.on("click", function(vw, index, node, e){
8463  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8464  });
8465
8466  // load XML data
8467  dataModel.load("foobar.xml");
8468  </code></pre>
8469  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8470  * <br><br>
8471  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8472  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8473  * 
8474  * Note: old style constructor is still suported (container, template, config)
8475  * 
8476  * @constructor
8477  * Create a new View
8478  * @param {Object} config The config object
8479  * 
8480  */
8481 Roo.View = function(config, depreciated_tpl, depreciated_config){
8482     
8483     this.parent = false;
8484     
8485     if (typeof(depreciated_tpl) == 'undefined') {
8486         // new way.. - universal constructor.
8487         Roo.apply(this, config);
8488         this.el  = Roo.get(this.el);
8489     } else {
8490         // old format..
8491         this.el  = Roo.get(config);
8492         this.tpl = depreciated_tpl;
8493         Roo.apply(this, depreciated_config);
8494     }
8495     this.wrapEl  = this.el.wrap().wrap();
8496     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8497     
8498     
8499     if(typeof(this.tpl) == "string"){
8500         this.tpl = new Roo.Template(this.tpl);
8501     } else {
8502         // support xtype ctors..
8503         this.tpl = new Roo.factory(this.tpl, Roo);
8504     }
8505     
8506     
8507     this.tpl.compile();
8508     
8509     /** @private */
8510     this.addEvents({
8511         /**
8512          * @event beforeclick
8513          * Fires before a click is processed. Returns false to cancel the default action.
8514          * @param {Roo.View} this
8515          * @param {Number} index The index of the target node
8516          * @param {HTMLElement} node The target node
8517          * @param {Roo.EventObject} e The raw event object
8518          */
8519             "beforeclick" : true,
8520         /**
8521          * @event click
8522          * Fires when a template node is clicked.
8523          * @param {Roo.View} this
8524          * @param {Number} index The index of the target node
8525          * @param {HTMLElement} node The target node
8526          * @param {Roo.EventObject} e The raw event object
8527          */
8528             "click" : true,
8529         /**
8530          * @event dblclick
8531          * Fires when a template node is double clicked.
8532          * @param {Roo.View} this
8533          * @param {Number} index The index of the target node
8534          * @param {HTMLElement} node The target node
8535          * @param {Roo.EventObject} e The raw event object
8536          */
8537             "dblclick" : true,
8538         /**
8539          * @event contextmenu
8540          * Fires when a template node is right clicked.
8541          * @param {Roo.View} this
8542          * @param {Number} index The index of the target node
8543          * @param {HTMLElement} node The target node
8544          * @param {Roo.EventObject} e The raw event object
8545          */
8546             "contextmenu" : true,
8547         /**
8548          * @event selectionchange
8549          * Fires when the selected nodes change.
8550          * @param {Roo.View} this
8551          * @param {Array} selections Array of the selected nodes
8552          */
8553             "selectionchange" : true,
8554     
8555         /**
8556          * @event beforeselect
8557          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8558          * @param {Roo.View} this
8559          * @param {HTMLElement} node The node to be selected
8560          * @param {Array} selections Array of currently selected nodes
8561          */
8562             "beforeselect" : true,
8563         /**
8564          * @event preparedata
8565          * Fires on every row to render, to allow you to change the data.
8566          * @param {Roo.View} this
8567          * @param {Object} data to be rendered (change this)
8568          */
8569           "preparedata" : true
8570           
8571           
8572         });
8573
8574
8575
8576     this.el.on({
8577         "click": this.onClick,
8578         "dblclick": this.onDblClick,
8579         "contextmenu": this.onContextMenu,
8580         scope:this
8581     });
8582
8583     this.selections = [];
8584     this.nodes = [];
8585     this.cmp = new Roo.CompositeElementLite([]);
8586     if(this.store){
8587         this.store = Roo.factory(this.store, Roo.data);
8588         this.setStore(this.store, true);
8589     }
8590     
8591     if ( this.footer && this.footer.xtype) {
8592            
8593          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8594         
8595         this.footer.dataSource = this.store
8596         this.footer.container = fctr;
8597         this.footer = Roo.factory(this.footer, Roo);
8598         fctr.insertFirst(this.el);
8599         
8600         // this is a bit insane - as the paging toolbar seems to detach the el..
8601 //        dom.parentNode.parentNode.parentNode
8602          // they get detached?
8603     }
8604     
8605     
8606     Roo.View.superclass.constructor.call(this);
8607     
8608     
8609 };
8610
8611 Roo.extend(Roo.View, Roo.util.Observable, {
8612     
8613      /**
8614      * @cfg {Roo.data.Store} store Data store to load data from.
8615      */
8616     store : false,
8617     
8618     /**
8619      * @cfg {String|Roo.Element} el The container element.
8620      */
8621     el : '',
8622     
8623     /**
8624      * @cfg {String|Roo.Template} tpl The template used by this View 
8625      */
8626     tpl : false,
8627     /**
8628      * @cfg {String} dataName the named area of the template to use as the data area
8629      *                          Works with domtemplates roo-name="name"
8630      */
8631     dataName: false,
8632     /**
8633      * @cfg {String} selectedClass The css class to add to selected nodes
8634      */
8635     selectedClass : "x-view-selected",
8636      /**
8637      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8638      */
8639     emptyText : "",
8640     
8641     /**
8642      * @cfg {String} text to display on mask (default Loading)
8643      */
8644     mask : false,
8645     /**
8646      * @cfg {Boolean} multiSelect Allow multiple selection
8647      */
8648     multiSelect : false,
8649     /**
8650      * @cfg {Boolean} singleSelect Allow single selection
8651      */
8652     singleSelect:  false,
8653     
8654     /**
8655      * @cfg {Boolean} toggleSelect - selecting 
8656      */
8657     toggleSelect : false,
8658     
8659     /**
8660      * @cfg {Boolean} tickable - selecting 
8661      */
8662     tickable : false,
8663     
8664     /**
8665      * Returns the element this view is bound to.
8666      * @return {Roo.Element}
8667      */
8668     getEl : function(){
8669         return this.wrapEl;
8670     },
8671     
8672     
8673
8674     /**
8675      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8676      */
8677     refresh : function(){
8678         //Roo.log('refresh');
8679         var t = this.tpl;
8680         
8681         // if we are using something like 'domtemplate', then
8682         // the what gets used is:
8683         // t.applySubtemplate(NAME, data, wrapping data..)
8684         // the outer template then get' applied with
8685         //     the store 'extra data'
8686         // and the body get's added to the
8687         //      roo-name="data" node?
8688         //      <span class='roo-tpl-{name}'></span> ?????
8689         
8690         
8691         
8692         this.clearSelections();
8693         this.el.update("");
8694         var html = [];
8695         var records = this.store.getRange();
8696         if(records.length < 1) {
8697             
8698             // is this valid??  = should it render a template??
8699             
8700             this.el.update(this.emptyText);
8701             return;
8702         }
8703         var el = this.el;
8704         if (this.dataName) {
8705             this.el.update(t.apply(this.store.meta)); //????
8706             el = this.el.child('.roo-tpl-' + this.dataName);
8707         }
8708         
8709         for(var i = 0, len = records.length; i < len; i++){
8710             var data = this.prepareData(records[i].data, i, records[i]);
8711             this.fireEvent("preparedata", this, data, i, records[i]);
8712             
8713             var d = Roo.apply({}, data);
8714             
8715             if(this.tickable){
8716                 Roo.apply(d, {'roo-id' : Roo.id()});
8717                 
8718                 var _this = this;
8719             
8720                 Roo.each(this.parent.item, function(item){
8721                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8722                         return;
8723                     }
8724                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8725                 });
8726             }
8727             
8728             html[html.length] = Roo.util.Format.trim(
8729                 this.dataName ?
8730                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8731                     t.apply(d)
8732             );
8733         }
8734         
8735         
8736         
8737         el.update(html.join(""));
8738         this.nodes = el.dom.childNodes;
8739         this.updateIndexes(0);
8740     },
8741     
8742
8743     /**
8744      * Function to override to reformat the data that is sent to
8745      * the template for each node.
8746      * DEPRICATED - use the preparedata event handler.
8747      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8748      * a JSON object for an UpdateManager bound view).
8749      */
8750     prepareData : function(data, index, record)
8751     {
8752         this.fireEvent("preparedata", this, data, index, record);
8753         return data;
8754     },
8755
8756     onUpdate : function(ds, record){
8757         // Roo.log('on update');   
8758         this.clearSelections();
8759         var index = this.store.indexOf(record);
8760         var n = this.nodes[index];
8761         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8762         n.parentNode.removeChild(n);
8763         this.updateIndexes(index, index);
8764     },
8765
8766     
8767     
8768 // --------- FIXME     
8769     onAdd : function(ds, records, index)
8770     {
8771         //Roo.log(['on Add', ds, records, index] );        
8772         this.clearSelections();
8773         if(this.nodes.length == 0){
8774             this.refresh();
8775             return;
8776         }
8777         var n = this.nodes[index];
8778         for(var i = 0, len = records.length; i < len; i++){
8779             var d = this.prepareData(records[i].data, i, records[i]);
8780             if(n){
8781                 this.tpl.insertBefore(n, d);
8782             }else{
8783                 
8784                 this.tpl.append(this.el, d);
8785             }
8786         }
8787         this.updateIndexes(index);
8788     },
8789
8790     onRemove : function(ds, record, index){
8791        // Roo.log('onRemove');
8792         this.clearSelections();
8793         var el = this.dataName  ?
8794             this.el.child('.roo-tpl-' + this.dataName) :
8795             this.el; 
8796         
8797         el.dom.removeChild(this.nodes[index]);
8798         this.updateIndexes(index);
8799     },
8800
8801     /**
8802      * Refresh an individual node.
8803      * @param {Number} index
8804      */
8805     refreshNode : function(index){
8806         this.onUpdate(this.store, this.store.getAt(index));
8807     },
8808
8809     updateIndexes : function(startIndex, endIndex){
8810         var ns = this.nodes;
8811         startIndex = startIndex || 0;
8812         endIndex = endIndex || ns.length - 1;
8813         for(var i = startIndex; i <= endIndex; i++){
8814             ns[i].nodeIndex = i;
8815         }
8816     },
8817
8818     /**
8819      * Changes the data store this view uses and refresh the view.
8820      * @param {Store} store
8821      */
8822     setStore : function(store, initial){
8823         if(!initial && this.store){
8824             this.store.un("datachanged", this.refresh);
8825             this.store.un("add", this.onAdd);
8826             this.store.un("remove", this.onRemove);
8827             this.store.un("update", this.onUpdate);
8828             this.store.un("clear", this.refresh);
8829             this.store.un("beforeload", this.onBeforeLoad);
8830             this.store.un("load", this.onLoad);
8831             this.store.un("loadexception", this.onLoad);
8832         }
8833         if(store){
8834           
8835             store.on("datachanged", this.refresh, this);
8836             store.on("add", this.onAdd, this);
8837             store.on("remove", this.onRemove, this);
8838             store.on("update", this.onUpdate, this);
8839             store.on("clear", this.refresh, this);
8840             store.on("beforeload", this.onBeforeLoad, this);
8841             store.on("load", this.onLoad, this);
8842             store.on("loadexception", this.onLoad, this);
8843         }
8844         
8845         if(store){
8846             this.refresh();
8847         }
8848     },
8849     /**
8850      * onbeforeLoad - masks the loading area.
8851      *
8852      */
8853     onBeforeLoad : function(store,opts)
8854     {
8855          //Roo.log('onBeforeLoad');   
8856         if (!opts.add) {
8857             this.el.update("");
8858         }
8859         this.el.mask(this.mask ? this.mask : "Loading" ); 
8860     },
8861     onLoad : function ()
8862     {
8863         this.el.unmask();
8864     },
8865     
8866
8867     /**
8868      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8869      * @param {HTMLElement} node
8870      * @return {HTMLElement} The template node
8871      */
8872     findItemFromChild : function(node){
8873         var el = this.dataName  ?
8874             this.el.child('.roo-tpl-' + this.dataName,true) :
8875             this.el.dom; 
8876         
8877         if(!node || node.parentNode == el){
8878                     return node;
8879             }
8880             var p = node.parentNode;
8881             while(p && p != el){
8882             if(p.parentNode == el){
8883                 return p;
8884             }
8885             p = p.parentNode;
8886         }
8887             return null;
8888     },
8889
8890     /** @ignore */
8891     onClick : function(e){
8892         var item = this.findItemFromChild(e.getTarget());
8893         if(item){
8894             var index = this.indexOf(item);
8895             if(this.onItemClick(item, index, e) !== false){
8896                 this.fireEvent("click", this, index, item, e);
8897             }
8898         }else{
8899             this.clearSelections();
8900         }
8901     },
8902
8903     /** @ignore */
8904     onContextMenu : function(e){
8905         var item = this.findItemFromChild(e.getTarget());
8906         if(item){
8907             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8908         }
8909     },
8910
8911     /** @ignore */
8912     onDblClick : function(e){
8913         var item = this.findItemFromChild(e.getTarget());
8914         if(item){
8915             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8916         }
8917     },
8918
8919     onItemClick : function(item, index, e)
8920     {
8921         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8922             return false;
8923         }
8924         if (this.toggleSelect) {
8925             var m = this.isSelected(item) ? 'unselect' : 'select';
8926             //Roo.log(m);
8927             var _t = this;
8928             _t[m](item, true, false);
8929             return true;
8930         }
8931         if(this.multiSelect || this.singleSelect){
8932             if(this.multiSelect && e.shiftKey && this.lastSelection){
8933                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8934             }else{
8935                 this.select(item, this.multiSelect && e.ctrlKey);
8936                 this.lastSelection = item;
8937             }
8938             
8939             if(!this.tickable){
8940                 e.preventDefault();
8941             }
8942             
8943         }
8944         return true;
8945     },
8946
8947     /**
8948      * Get the number of selected nodes.
8949      * @return {Number}
8950      */
8951     getSelectionCount : function(){
8952         return this.selections.length;
8953     },
8954
8955     /**
8956      * Get the currently selected nodes.
8957      * @return {Array} An array of HTMLElements
8958      */
8959     getSelectedNodes : function(){
8960         return this.selections;
8961     },
8962
8963     /**
8964      * Get the indexes of the selected nodes.
8965      * @return {Array}
8966      */
8967     getSelectedIndexes : function(){
8968         var indexes = [], s = this.selections;
8969         for(var i = 0, len = s.length; i < len; i++){
8970             indexes.push(s[i].nodeIndex);
8971         }
8972         return indexes;
8973     },
8974
8975     /**
8976      * Clear all selections
8977      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8978      */
8979     clearSelections : function(suppressEvent){
8980         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8981             this.cmp.elements = this.selections;
8982             this.cmp.removeClass(this.selectedClass);
8983             this.selections = [];
8984             if(!suppressEvent){
8985                 this.fireEvent("selectionchange", this, this.selections);
8986             }
8987         }
8988     },
8989
8990     /**
8991      * Returns true if the passed node is selected
8992      * @param {HTMLElement/Number} node The node or node index
8993      * @return {Boolean}
8994      */
8995     isSelected : function(node){
8996         var s = this.selections;
8997         if(s.length < 1){
8998             return false;
8999         }
9000         node = this.getNode(node);
9001         return s.indexOf(node) !== -1;
9002     },
9003
9004     /**
9005      * Selects nodes.
9006      * @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
9007      * @param {Boolean} keepExisting (optional) true to keep existing selections
9008      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9009      */
9010     select : function(nodeInfo, keepExisting, suppressEvent){
9011         if(nodeInfo instanceof Array){
9012             if(!keepExisting){
9013                 this.clearSelections(true);
9014             }
9015             for(var i = 0, len = nodeInfo.length; i < len; i++){
9016                 this.select(nodeInfo[i], true, true);
9017             }
9018             return;
9019         } 
9020         var node = this.getNode(nodeInfo);
9021         if(!node || this.isSelected(node)){
9022             return; // already selected.
9023         }
9024         if(!keepExisting){
9025             this.clearSelections(true);
9026         }
9027         
9028         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9029             Roo.fly(node).addClass(this.selectedClass);
9030             this.selections.push(node);
9031             if(!suppressEvent){
9032                 this.fireEvent("selectionchange", this, this.selections);
9033             }
9034         }
9035         
9036         
9037     },
9038       /**
9039      * Unselects nodes.
9040      * @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
9041      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9042      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9043      */
9044     unselect : function(nodeInfo, keepExisting, suppressEvent)
9045     {
9046         if(nodeInfo instanceof Array){
9047             Roo.each(this.selections, function(s) {
9048                 this.unselect(s, nodeInfo);
9049             }, this);
9050             return;
9051         }
9052         var node = this.getNode(nodeInfo);
9053         if(!node || !this.isSelected(node)){
9054             //Roo.log("not selected");
9055             return; // not selected.
9056         }
9057         // fireevent???
9058         var ns = [];
9059         Roo.each(this.selections, function(s) {
9060             if (s == node ) {
9061                 Roo.fly(node).removeClass(this.selectedClass);
9062
9063                 return;
9064             }
9065             ns.push(s);
9066         },this);
9067         
9068         this.selections= ns;
9069         this.fireEvent("selectionchange", this, this.selections);
9070     },
9071
9072     /**
9073      * Gets a template node.
9074      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9075      * @return {HTMLElement} The node or null if it wasn't found
9076      */
9077     getNode : function(nodeInfo){
9078         if(typeof nodeInfo == "string"){
9079             return document.getElementById(nodeInfo);
9080         }else if(typeof nodeInfo == "number"){
9081             return this.nodes[nodeInfo];
9082         }
9083         return nodeInfo;
9084     },
9085
9086     /**
9087      * Gets a range template nodes.
9088      * @param {Number} startIndex
9089      * @param {Number} endIndex
9090      * @return {Array} An array of nodes
9091      */
9092     getNodes : function(start, end){
9093         var ns = this.nodes;
9094         start = start || 0;
9095         end = typeof end == "undefined" ? ns.length - 1 : end;
9096         var nodes = [];
9097         if(start <= end){
9098             for(var i = start; i <= end; i++){
9099                 nodes.push(ns[i]);
9100             }
9101         } else{
9102             for(var i = start; i >= end; i--){
9103                 nodes.push(ns[i]);
9104             }
9105         }
9106         return nodes;
9107     },
9108
9109     /**
9110      * Finds the index of the passed node
9111      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9112      * @return {Number} The index of the node or -1
9113      */
9114     indexOf : function(node){
9115         node = this.getNode(node);
9116         if(typeof node.nodeIndex == "number"){
9117             return node.nodeIndex;
9118         }
9119         var ns = this.nodes;
9120         for(var i = 0, len = ns.length; i < len; i++){
9121             if(ns[i] == node){
9122                 return i;
9123             }
9124         }
9125         return -1;
9126     }
9127 });
9128 /*
9129  * Based on:
9130  * Ext JS Library 1.1.1
9131  * Copyright(c) 2006-2007, Ext JS, LLC.
9132  *
9133  * Originally Released Under LGPL - original licence link has changed is not relivant.
9134  *
9135  * Fork - LGPL
9136  * <script type="text/javascript">
9137  */
9138
9139 /**
9140  * @class Roo.JsonView
9141  * @extends Roo.View
9142  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9143 <pre><code>
9144 var view = new Roo.JsonView({
9145     container: "my-element",
9146     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9147     multiSelect: true, 
9148     jsonRoot: "data" 
9149 });
9150
9151 // listen for node click?
9152 view.on("click", function(vw, index, node, e){
9153     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9154 });
9155
9156 // direct load of JSON data
9157 view.load("foobar.php");
9158
9159 // Example from my blog list
9160 var tpl = new Roo.Template(
9161     '&lt;div class="entry"&gt;' +
9162     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9163     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9164     "&lt;/div&gt;&lt;hr /&gt;"
9165 );
9166
9167 var moreView = new Roo.JsonView({
9168     container :  "entry-list", 
9169     template : tpl,
9170     jsonRoot: "posts"
9171 });
9172 moreView.on("beforerender", this.sortEntries, this);
9173 moreView.load({
9174     url: "/blog/get-posts.php",
9175     params: "allposts=true",
9176     text: "Loading Blog Entries..."
9177 });
9178 </code></pre>
9179
9180 * Note: old code is supported with arguments : (container, template, config)
9181
9182
9183  * @constructor
9184  * Create a new JsonView
9185  * 
9186  * @param {Object} config The config object
9187  * 
9188  */
9189 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9190     
9191     
9192     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9193
9194     var um = this.el.getUpdateManager();
9195     um.setRenderer(this);
9196     um.on("update", this.onLoad, this);
9197     um.on("failure", this.onLoadException, this);
9198
9199     /**
9200      * @event beforerender
9201      * Fires before rendering of the downloaded JSON data.
9202      * @param {Roo.JsonView} this
9203      * @param {Object} data The JSON data loaded
9204      */
9205     /**
9206      * @event load
9207      * Fires when data is loaded.
9208      * @param {Roo.JsonView} this
9209      * @param {Object} data The JSON data loaded
9210      * @param {Object} response The raw Connect response object
9211      */
9212     /**
9213      * @event loadexception
9214      * Fires when loading fails.
9215      * @param {Roo.JsonView} this
9216      * @param {Object} response The raw Connect response object
9217      */
9218     this.addEvents({
9219         'beforerender' : true,
9220         'load' : true,
9221         'loadexception' : true
9222     });
9223 };
9224 Roo.extend(Roo.JsonView, Roo.View, {
9225     /**
9226      * @type {String} The root property in the loaded JSON object that contains the data
9227      */
9228     jsonRoot : "",
9229
9230     /**
9231      * Refreshes the view.
9232      */
9233     refresh : function(){
9234         this.clearSelections();
9235         this.el.update("");
9236         var html = [];
9237         var o = this.jsonData;
9238         if(o && o.length > 0){
9239             for(var i = 0, len = o.length; i < len; i++){
9240                 var data = this.prepareData(o[i], i, o);
9241                 html[html.length] = this.tpl.apply(data);
9242             }
9243         }else{
9244             html.push(this.emptyText);
9245         }
9246         this.el.update(html.join(""));
9247         this.nodes = this.el.dom.childNodes;
9248         this.updateIndexes(0);
9249     },
9250
9251     /**
9252      * 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.
9253      * @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:
9254      <pre><code>
9255      view.load({
9256          url: "your-url.php",
9257          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9258          callback: yourFunction,
9259          scope: yourObject, //(optional scope)
9260          discardUrl: false,
9261          nocache: false,
9262          text: "Loading...",
9263          timeout: 30,
9264          scripts: false
9265      });
9266      </code></pre>
9267      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9268      * 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.
9269      * @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}
9270      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9271      * @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.
9272      */
9273     load : function(){
9274         var um = this.el.getUpdateManager();
9275         um.update.apply(um, arguments);
9276     },
9277
9278     render : function(el, response){
9279         this.clearSelections();
9280         this.el.update("");
9281         var o;
9282         try{
9283             o = Roo.util.JSON.decode(response.responseText);
9284             if(this.jsonRoot){
9285                 
9286                 o = o[this.jsonRoot];
9287             }
9288         } catch(e){
9289         }
9290         /**
9291          * The current JSON data or null
9292          */
9293         this.jsonData = o;
9294         this.beforeRender();
9295         this.refresh();
9296     },
9297
9298 /**
9299  * Get the number of records in the current JSON dataset
9300  * @return {Number}
9301  */
9302     getCount : function(){
9303         return this.jsonData ? this.jsonData.length : 0;
9304     },
9305
9306 /**
9307  * Returns the JSON object for the specified node(s)
9308  * @param {HTMLElement/Array} node The node or an array of nodes
9309  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9310  * you get the JSON object for the node
9311  */
9312     getNodeData : function(node){
9313         if(node instanceof Array){
9314             var data = [];
9315             for(var i = 0, len = node.length; i < len; i++){
9316                 data.push(this.getNodeData(node[i]));
9317             }
9318             return data;
9319         }
9320         return this.jsonData[this.indexOf(node)] || null;
9321     },
9322
9323     beforeRender : function(){
9324         this.snapshot = this.jsonData;
9325         if(this.sortInfo){
9326             this.sort.apply(this, this.sortInfo);
9327         }
9328         this.fireEvent("beforerender", this, this.jsonData);
9329     },
9330
9331     onLoad : function(el, o){
9332         this.fireEvent("load", this, this.jsonData, o);
9333     },
9334
9335     onLoadException : function(el, o){
9336         this.fireEvent("loadexception", this, o);
9337     },
9338
9339 /**
9340  * Filter the data by a specific property.
9341  * @param {String} property A property on your JSON objects
9342  * @param {String/RegExp} value Either string that the property values
9343  * should start with, or a RegExp to test against the property
9344  */
9345     filter : function(property, value){
9346         if(this.jsonData){
9347             var data = [];
9348             var ss = this.snapshot;
9349             if(typeof value == "string"){
9350                 var vlen = value.length;
9351                 if(vlen == 0){
9352                     this.clearFilter();
9353                     return;
9354                 }
9355                 value = value.toLowerCase();
9356                 for(var i = 0, len = ss.length; i < len; i++){
9357                     var o = ss[i];
9358                     if(o[property].substr(0, vlen).toLowerCase() == value){
9359                         data.push(o);
9360                     }
9361                 }
9362             } else if(value.exec){ // regex?
9363                 for(var i = 0, len = ss.length; i < len; i++){
9364                     var o = ss[i];
9365                     if(value.test(o[property])){
9366                         data.push(o);
9367                     }
9368                 }
9369             } else{
9370                 return;
9371             }
9372             this.jsonData = data;
9373             this.refresh();
9374         }
9375     },
9376
9377 /**
9378  * Filter by a function. The passed function will be called with each
9379  * object in the current dataset. If the function returns true the value is kept,
9380  * otherwise it is filtered.
9381  * @param {Function} fn
9382  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9383  */
9384     filterBy : function(fn, scope){
9385         if(this.jsonData){
9386             var data = [];
9387             var ss = this.snapshot;
9388             for(var i = 0, len = ss.length; i < len; i++){
9389                 var o = ss[i];
9390                 if(fn.call(scope || this, o)){
9391                     data.push(o);
9392                 }
9393             }
9394             this.jsonData = data;
9395             this.refresh();
9396         }
9397     },
9398
9399 /**
9400  * Clears the current filter.
9401  */
9402     clearFilter : function(){
9403         if(this.snapshot && this.jsonData != this.snapshot){
9404             this.jsonData = this.snapshot;
9405             this.refresh();
9406         }
9407     },
9408
9409
9410 /**
9411  * Sorts the data for this view and refreshes it.
9412  * @param {String} property A property on your JSON objects to sort on
9413  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9414  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9415  */
9416     sort : function(property, dir, sortType){
9417         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9418         if(this.jsonData){
9419             var p = property;
9420             var dsc = dir && dir.toLowerCase() == "desc";
9421             var f = function(o1, o2){
9422                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9423                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9424                 ;
9425                 if(v1 < v2){
9426                     return dsc ? +1 : -1;
9427                 } else if(v1 > v2){
9428                     return dsc ? -1 : +1;
9429                 } else{
9430                     return 0;
9431                 }
9432             };
9433             this.jsonData.sort(f);
9434             this.refresh();
9435             if(this.jsonData != this.snapshot){
9436                 this.snapshot.sort(f);
9437             }
9438         }
9439     }
9440 });/*
9441  * Based on:
9442  * Ext JS Library 1.1.1
9443  * Copyright(c) 2006-2007, Ext JS, LLC.
9444  *
9445  * Originally Released Under LGPL - original licence link has changed is not relivant.
9446  *
9447  * Fork - LGPL
9448  * <script type="text/javascript">
9449  */
9450  
9451
9452 /**
9453  * @class Roo.ColorPalette
9454  * @extends Roo.Component
9455  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9456  * Here's an example of typical usage:
9457  * <pre><code>
9458 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9459 cp.render('my-div');
9460
9461 cp.on('select', function(palette, selColor){
9462     // do something with selColor
9463 });
9464 </code></pre>
9465  * @constructor
9466  * Create a new ColorPalette
9467  * @param {Object} config The config object
9468  */
9469 Roo.ColorPalette = function(config){
9470     Roo.ColorPalette.superclass.constructor.call(this, config);
9471     this.addEvents({
9472         /**
9473              * @event select
9474              * Fires when a color is selected
9475              * @param {ColorPalette} this
9476              * @param {String} color The 6-digit color hex code (without the # symbol)
9477              */
9478         select: true
9479     });
9480
9481     if(this.handler){
9482         this.on("select", this.handler, this.scope, true);
9483     }
9484 };
9485 Roo.extend(Roo.ColorPalette, Roo.Component, {
9486     /**
9487      * @cfg {String} itemCls
9488      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9489      */
9490     itemCls : "x-color-palette",
9491     /**
9492      * @cfg {String} value
9493      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9494      * the hex codes are case-sensitive.
9495      */
9496     value : null,
9497     clickEvent:'click',
9498     // private
9499     ctype: "Roo.ColorPalette",
9500
9501     /**
9502      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9503      */
9504     allowReselect : false,
9505
9506     /**
9507      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9508      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9509      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9510      * of colors with the width setting until the box is symmetrical.</p>
9511      * <p>You can override individual colors if needed:</p>
9512      * <pre><code>
9513 var cp = new Roo.ColorPalette();
9514 cp.colors[0] = "FF0000";  // change the first box to red
9515 </code></pre>
9516
9517 Or you can provide a custom array of your own for complete control:
9518 <pre><code>
9519 var cp = new Roo.ColorPalette();
9520 cp.colors = ["000000", "993300", "333300"];
9521 </code></pre>
9522      * @type Array
9523      */
9524     colors : [
9525         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9526         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9527         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9528         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9529         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9530     ],
9531
9532     // private
9533     onRender : function(container, position){
9534         var t = new Roo.MasterTemplate(
9535             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9536         );
9537         var c = this.colors;
9538         for(var i = 0, len = c.length; i < len; i++){
9539             t.add([c[i]]);
9540         }
9541         var el = document.createElement("div");
9542         el.className = this.itemCls;
9543         t.overwrite(el);
9544         container.dom.insertBefore(el, position);
9545         this.el = Roo.get(el);
9546         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9547         if(this.clickEvent != 'click'){
9548             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9549         }
9550     },
9551
9552     // private
9553     afterRender : function(){
9554         Roo.ColorPalette.superclass.afterRender.call(this);
9555         if(this.value){
9556             var s = this.value;
9557             this.value = null;
9558             this.select(s);
9559         }
9560     },
9561
9562     // private
9563     handleClick : function(e, t){
9564         e.preventDefault();
9565         if(!this.disabled){
9566             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9567             this.select(c.toUpperCase());
9568         }
9569     },
9570
9571     /**
9572      * Selects the specified color in the palette (fires the select event)
9573      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9574      */
9575     select : function(color){
9576         color = color.replace("#", "");
9577         if(color != this.value || this.allowReselect){
9578             var el = this.el;
9579             if(this.value){
9580                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9581             }
9582             el.child("a.color-"+color).addClass("x-color-palette-sel");
9583             this.value = color;
9584             this.fireEvent("select", this, color);
9585         }
9586     }
9587 });/*
9588  * Based on:
9589  * Ext JS Library 1.1.1
9590  * Copyright(c) 2006-2007, Ext JS, LLC.
9591  *
9592  * Originally Released Under LGPL - original licence link has changed is not relivant.
9593  *
9594  * Fork - LGPL
9595  * <script type="text/javascript">
9596  */
9597  
9598 /**
9599  * @class Roo.DatePicker
9600  * @extends Roo.Component
9601  * Simple date picker class.
9602  * @constructor
9603  * Create a new DatePicker
9604  * @param {Object} config The config object
9605  */
9606 Roo.DatePicker = function(config){
9607     Roo.DatePicker.superclass.constructor.call(this, config);
9608
9609     this.value = config && config.value ?
9610                  config.value.clearTime() : new Date().clearTime();
9611
9612     this.addEvents({
9613         /**
9614              * @event select
9615              * Fires when a date is selected
9616              * @param {DatePicker} this
9617              * @param {Date} date The selected date
9618              */
9619         'select': true,
9620         /**
9621              * @event monthchange
9622              * Fires when the displayed month changes 
9623              * @param {DatePicker} this
9624              * @param {Date} date The selected month
9625              */
9626         'monthchange': true
9627     });
9628
9629     if(this.handler){
9630         this.on("select", this.handler,  this.scope || this);
9631     }
9632     // build the disabledDatesRE
9633     if(!this.disabledDatesRE && this.disabledDates){
9634         var dd = this.disabledDates;
9635         var re = "(?:";
9636         for(var i = 0; i < dd.length; i++){
9637             re += dd[i];
9638             if(i != dd.length-1) re += "|";
9639         }
9640         this.disabledDatesRE = new RegExp(re + ")");
9641     }
9642 };
9643
9644 Roo.extend(Roo.DatePicker, Roo.Component, {
9645     /**
9646      * @cfg {String} todayText
9647      * The text to display on the button that selects the current date (defaults to "Today")
9648      */
9649     todayText : "Today",
9650     /**
9651      * @cfg {String} okText
9652      * The text to display on the ok button
9653      */
9654     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9655     /**
9656      * @cfg {String} cancelText
9657      * The text to display on the cancel button
9658      */
9659     cancelText : "Cancel",
9660     /**
9661      * @cfg {String} todayTip
9662      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9663      */
9664     todayTip : "{0} (Spacebar)",
9665     /**
9666      * @cfg {Date} minDate
9667      * Minimum allowable date (JavaScript date object, defaults to null)
9668      */
9669     minDate : null,
9670     /**
9671      * @cfg {Date} maxDate
9672      * Maximum allowable date (JavaScript date object, defaults to null)
9673      */
9674     maxDate : null,
9675     /**
9676      * @cfg {String} minText
9677      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9678      */
9679     minText : "This date is before the minimum date",
9680     /**
9681      * @cfg {String} maxText
9682      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9683      */
9684     maxText : "This date is after the maximum date",
9685     /**
9686      * @cfg {String} format
9687      * The default date format string which can be overriden for localization support.  The format must be
9688      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9689      */
9690     format : "m/d/y",
9691     /**
9692      * @cfg {Array} disabledDays
9693      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9694      */
9695     disabledDays : null,
9696     /**
9697      * @cfg {String} disabledDaysText
9698      * The tooltip to display when the date falls on a disabled day (defaults to "")
9699      */
9700     disabledDaysText : "",
9701     /**
9702      * @cfg {RegExp} disabledDatesRE
9703      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9704      */
9705     disabledDatesRE : null,
9706     /**
9707      * @cfg {String} disabledDatesText
9708      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9709      */
9710     disabledDatesText : "",
9711     /**
9712      * @cfg {Boolean} constrainToViewport
9713      * True to constrain the date picker to the viewport (defaults to true)
9714      */
9715     constrainToViewport : true,
9716     /**
9717      * @cfg {Array} monthNames
9718      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9719      */
9720     monthNames : Date.monthNames,
9721     /**
9722      * @cfg {Array} dayNames
9723      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9724      */
9725     dayNames : Date.dayNames,
9726     /**
9727      * @cfg {String} nextText
9728      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9729      */
9730     nextText: 'Next Month (Control+Right)',
9731     /**
9732      * @cfg {String} prevText
9733      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9734      */
9735     prevText: 'Previous Month (Control+Left)',
9736     /**
9737      * @cfg {String} monthYearText
9738      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9739      */
9740     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9741     /**
9742      * @cfg {Number} startDay
9743      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9744      */
9745     startDay : 0,
9746     /**
9747      * @cfg {Bool} showClear
9748      * Show a clear button (usefull for date form elements that can be blank.)
9749      */
9750     
9751     showClear: false,
9752     
9753     /**
9754      * Sets the value of the date field
9755      * @param {Date} value The date to set
9756      */
9757     setValue : function(value){
9758         var old = this.value;
9759         
9760         if (typeof(value) == 'string') {
9761          
9762             value = Date.parseDate(value, this.format);
9763         }
9764         if (!value) {
9765             value = new Date();
9766         }
9767         
9768         this.value = value.clearTime(true);
9769         if(this.el){
9770             this.update(this.value);
9771         }
9772     },
9773
9774     /**
9775      * Gets the current selected value of the date field
9776      * @return {Date} The selected date
9777      */
9778     getValue : function(){
9779         return this.value;
9780     },
9781
9782     // private
9783     focus : function(){
9784         if(this.el){
9785             this.update(this.activeDate);
9786         }
9787     },
9788
9789     // privateval
9790     onRender : function(container, position){
9791         
9792         var m = [
9793              '<table cellspacing="0">',
9794                 '<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>',
9795                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9796         var dn = this.dayNames;
9797         for(var i = 0; i < 7; i++){
9798             var d = this.startDay+i;
9799             if(d > 6){
9800                 d = d-7;
9801             }
9802             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9803         }
9804         m[m.length] = "</tr></thead><tbody><tr>";
9805         for(var i = 0; i < 42; i++) {
9806             if(i % 7 == 0 && i != 0){
9807                 m[m.length] = "</tr><tr>";
9808             }
9809             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9810         }
9811         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9812             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9813
9814         var el = document.createElement("div");
9815         el.className = "x-date-picker";
9816         el.innerHTML = m.join("");
9817
9818         container.dom.insertBefore(el, position);
9819
9820         this.el = Roo.get(el);
9821         this.eventEl = Roo.get(el.firstChild);
9822
9823         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9824             handler: this.showPrevMonth,
9825             scope: this,
9826             preventDefault:true,
9827             stopDefault:true
9828         });
9829
9830         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9831             handler: this.showNextMonth,
9832             scope: this,
9833             preventDefault:true,
9834             stopDefault:true
9835         });
9836
9837         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9838
9839         this.monthPicker = this.el.down('div.x-date-mp');
9840         this.monthPicker.enableDisplayMode('block');
9841         
9842         var kn = new Roo.KeyNav(this.eventEl, {
9843             "left" : function(e){
9844                 e.ctrlKey ?
9845                     this.showPrevMonth() :
9846                     this.update(this.activeDate.add("d", -1));
9847             },
9848
9849             "right" : function(e){
9850                 e.ctrlKey ?
9851                     this.showNextMonth() :
9852                     this.update(this.activeDate.add("d", 1));
9853             },
9854
9855             "up" : function(e){
9856                 e.ctrlKey ?
9857                     this.showNextYear() :
9858                     this.update(this.activeDate.add("d", -7));
9859             },
9860
9861             "down" : function(e){
9862                 e.ctrlKey ?
9863                     this.showPrevYear() :
9864                     this.update(this.activeDate.add("d", 7));
9865             },
9866
9867             "pageUp" : function(e){
9868                 this.showNextMonth();
9869             },
9870
9871             "pageDown" : function(e){
9872                 this.showPrevMonth();
9873             },
9874
9875             "enter" : function(e){
9876                 e.stopPropagation();
9877                 return true;
9878             },
9879
9880             scope : this
9881         });
9882
9883         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9884
9885         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9886
9887         this.el.unselectable();
9888         
9889         this.cells = this.el.select("table.x-date-inner tbody td");
9890         this.textNodes = this.el.query("table.x-date-inner tbody span");
9891
9892         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9893             text: "&#160;",
9894             tooltip: this.monthYearText
9895         });
9896
9897         this.mbtn.on('click', this.showMonthPicker, this);
9898         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9899
9900
9901         var today = (new Date()).dateFormat(this.format);
9902         
9903         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9904         if (this.showClear) {
9905             baseTb.add( new Roo.Toolbar.Fill());
9906         }
9907         baseTb.add({
9908             text: String.format(this.todayText, today),
9909             tooltip: String.format(this.todayTip, today),
9910             handler: this.selectToday,
9911             scope: this
9912         });
9913         
9914         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9915             
9916         //});
9917         if (this.showClear) {
9918             
9919             baseTb.add( new Roo.Toolbar.Fill());
9920             baseTb.add({
9921                 text: '&#160;',
9922                 cls: 'x-btn-icon x-btn-clear',
9923                 handler: function() {
9924                     //this.value = '';
9925                     this.fireEvent("select", this, '');
9926                 },
9927                 scope: this
9928             });
9929         }
9930         
9931         
9932         if(Roo.isIE){
9933             this.el.repaint();
9934         }
9935         this.update(this.value);
9936     },
9937
9938     createMonthPicker : function(){
9939         if(!this.monthPicker.dom.firstChild){
9940             var buf = ['<table border="0" cellspacing="0">'];
9941             for(var i = 0; i < 6; i++){
9942                 buf.push(
9943                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9944                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9945                     i == 0 ?
9946                     '<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>' :
9947                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9948                 );
9949             }
9950             buf.push(
9951                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9952                     this.okText,
9953                     '</button><button type="button" class="x-date-mp-cancel">',
9954                     this.cancelText,
9955                     '</button></td></tr>',
9956                 '</table>'
9957             );
9958             this.monthPicker.update(buf.join(''));
9959             this.monthPicker.on('click', this.onMonthClick, this);
9960             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9961
9962             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9963             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9964
9965             this.mpMonths.each(function(m, a, i){
9966                 i += 1;
9967                 if((i%2) == 0){
9968                     m.dom.xmonth = 5 + Math.round(i * .5);
9969                 }else{
9970                     m.dom.xmonth = Math.round((i-1) * .5);
9971                 }
9972             });
9973         }
9974     },
9975
9976     showMonthPicker : function(){
9977         this.createMonthPicker();
9978         var size = this.el.getSize();
9979         this.monthPicker.setSize(size);
9980         this.monthPicker.child('table').setSize(size);
9981
9982         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9983         this.updateMPMonth(this.mpSelMonth);
9984         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9985         this.updateMPYear(this.mpSelYear);
9986
9987         this.monthPicker.slideIn('t', {duration:.2});
9988     },
9989
9990     updateMPYear : function(y){
9991         this.mpyear = y;
9992         var ys = this.mpYears.elements;
9993         for(var i = 1; i <= 10; i++){
9994             var td = ys[i-1], y2;
9995             if((i%2) == 0){
9996                 y2 = y + Math.round(i * .5);
9997                 td.firstChild.innerHTML = y2;
9998                 td.xyear = y2;
9999             }else{
10000                 y2 = y - (5-Math.round(i * .5));
10001                 td.firstChild.innerHTML = y2;
10002                 td.xyear = y2;
10003             }
10004             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10005         }
10006     },
10007
10008     updateMPMonth : function(sm){
10009         this.mpMonths.each(function(m, a, i){
10010             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10011         });
10012     },
10013
10014     selectMPMonth: function(m){
10015         
10016     },
10017
10018     onMonthClick : function(e, t){
10019         e.stopEvent();
10020         var el = new Roo.Element(t), pn;
10021         if(el.is('button.x-date-mp-cancel')){
10022             this.hideMonthPicker();
10023         }
10024         else if(el.is('button.x-date-mp-ok')){
10025             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10026             this.hideMonthPicker();
10027         }
10028         else if(pn = el.up('td.x-date-mp-month', 2)){
10029             this.mpMonths.removeClass('x-date-mp-sel');
10030             pn.addClass('x-date-mp-sel');
10031             this.mpSelMonth = pn.dom.xmonth;
10032         }
10033         else if(pn = el.up('td.x-date-mp-year', 2)){
10034             this.mpYears.removeClass('x-date-mp-sel');
10035             pn.addClass('x-date-mp-sel');
10036             this.mpSelYear = pn.dom.xyear;
10037         }
10038         else if(el.is('a.x-date-mp-prev')){
10039             this.updateMPYear(this.mpyear-10);
10040         }
10041         else if(el.is('a.x-date-mp-next')){
10042             this.updateMPYear(this.mpyear+10);
10043         }
10044     },
10045
10046     onMonthDblClick : function(e, t){
10047         e.stopEvent();
10048         var el = new Roo.Element(t), pn;
10049         if(pn = el.up('td.x-date-mp-month', 2)){
10050             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10051             this.hideMonthPicker();
10052         }
10053         else if(pn = el.up('td.x-date-mp-year', 2)){
10054             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10055             this.hideMonthPicker();
10056         }
10057     },
10058
10059     hideMonthPicker : function(disableAnim){
10060         if(this.monthPicker){
10061             if(disableAnim === true){
10062                 this.monthPicker.hide();
10063             }else{
10064                 this.monthPicker.slideOut('t', {duration:.2});
10065             }
10066         }
10067     },
10068
10069     // private
10070     showPrevMonth : function(e){
10071         this.update(this.activeDate.add("mo", -1));
10072     },
10073
10074     // private
10075     showNextMonth : function(e){
10076         this.update(this.activeDate.add("mo", 1));
10077     },
10078
10079     // private
10080     showPrevYear : function(){
10081         this.update(this.activeDate.add("y", -1));
10082     },
10083
10084     // private
10085     showNextYear : function(){
10086         this.update(this.activeDate.add("y", 1));
10087     },
10088
10089     // private
10090     handleMouseWheel : function(e){
10091         var delta = e.getWheelDelta();
10092         if(delta > 0){
10093             this.showPrevMonth();
10094             e.stopEvent();
10095         } else if(delta < 0){
10096             this.showNextMonth();
10097             e.stopEvent();
10098         }
10099     },
10100
10101     // private
10102     handleDateClick : function(e, t){
10103         e.stopEvent();
10104         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10105             this.setValue(new Date(t.dateValue));
10106             this.fireEvent("select", this, this.value);
10107         }
10108     },
10109
10110     // private
10111     selectToday : function(){
10112         this.setValue(new Date().clearTime());
10113         this.fireEvent("select", this, this.value);
10114     },
10115
10116     // private
10117     update : function(date)
10118     {
10119         var vd = this.activeDate;
10120         this.activeDate = date;
10121         if(vd && this.el){
10122             var t = date.getTime();
10123             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10124                 this.cells.removeClass("x-date-selected");
10125                 this.cells.each(function(c){
10126                    if(c.dom.firstChild.dateValue == t){
10127                        c.addClass("x-date-selected");
10128                        setTimeout(function(){
10129                             try{c.dom.firstChild.focus();}catch(e){}
10130                        }, 50);
10131                        return false;
10132                    }
10133                 });
10134                 return;
10135             }
10136         }
10137         
10138         var days = date.getDaysInMonth();
10139         var firstOfMonth = date.getFirstDateOfMonth();
10140         var startingPos = firstOfMonth.getDay()-this.startDay;
10141
10142         if(startingPos <= this.startDay){
10143             startingPos += 7;
10144         }
10145
10146         var pm = date.add("mo", -1);
10147         var prevStart = pm.getDaysInMonth()-startingPos;
10148
10149         var cells = this.cells.elements;
10150         var textEls = this.textNodes;
10151         days += startingPos;
10152
10153         // convert everything to numbers so it's fast
10154         var day = 86400000;
10155         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10156         var today = new Date().clearTime().getTime();
10157         var sel = date.clearTime().getTime();
10158         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10159         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10160         var ddMatch = this.disabledDatesRE;
10161         var ddText = this.disabledDatesText;
10162         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10163         var ddaysText = this.disabledDaysText;
10164         var format = this.format;
10165
10166         var setCellClass = function(cal, cell){
10167             cell.title = "";
10168             var t = d.getTime();
10169             cell.firstChild.dateValue = t;
10170             if(t == today){
10171                 cell.className += " x-date-today";
10172                 cell.title = cal.todayText;
10173             }
10174             if(t == sel){
10175                 cell.className += " x-date-selected";
10176                 setTimeout(function(){
10177                     try{cell.firstChild.focus();}catch(e){}
10178                 }, 50);
10179             }
10180             // disabling
10181             if(t < min) {
10182                 cell.className = " x-date-disabled";
10183                 cell.title = cal.minText;
10184                 return;
10185             }
10186             if(t > max) {
10187                 cell.className = " x-date-disabled";
10188                 cell.title = cal.maxText;
10189                 return;
10190             }
10191             if(ddays){
10192                 if(ddays.indexOf(d.getDay()) != -1){
10193                     cell.title = ddaysText;
10194                     cell.className = " x-date-disabled";
10195                 }
10196             }
10197             if(ddMatch && format){
10198                 var fvalue = d.dateFormat(format);
10199                 if(ddMatch.test(fvalue)){
10200                     cell.title = ddText.replace("%0", fvalue);
10201                     cell.className = " x-date-disabled";
10202                 }
10203             }
10204         };
10205
10206         var i = 0;
10207         for(; i < startingPos; i++) {
10208             textEls[i].innerHTML = (++prevStart);
10209             d.setDate(d.getDate()+1);
10210             cells[i].className = "x-date-prevday";
10211             setCellClass(this, cells[i]);
10212         }
10213         for(; i < days; i++){
10214             intDay = i - startingPos + 1;
10215             textEls[i].innerHTML = (intDay);
10216             d.setDate(d.getDate()+1);
10217             cells[i].className = "x-date-active";
10218             setCellClass(this, cells[i]);
10219         }
10220         var extraDays = 0;
10221         for(; i < 42; i++) {
10222              textEls[i].innerHTML = (++extraDays);
10223              d.setDate(d.getDate()+1);
10224              cells[i].className = "x-date-nextday";
10225              setCellClass(this, cells[i]);
10226         }
10227
10228         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10229         this.fireEvent('monthchange', this, date);
10230         
10231         if(!this.internalRender){
10232             var main = this.el.dom.firstChild;
10233             var w = main.offsetWidth;
10234             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10235             Roo.fly(main).setWidth(w);
10236             this.internalRender = true;
10237             // opera does not respect the auto grow header center column
10238             // then, after it gets a width opera refuses to recalculate
10239             // without a second pass
10240             if(Roo.isOpera && !this.secondPass){
10241                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10242                 this.secondPass = true;
10243                 this.update.defer(10, this, [date]);
10244             }
10245         }
10246         
10247         
10248     }
10249 });        /*
10250  * Based on:
10251  * Ext JS Library 1.1.1
10252  * Copyright(c) 2006-2007, Ext JS, LLC.
10253  *
10254  * Originally Released Under LGPL - original licence link has changed is not relivant.
10255  *
10256  * Fork - LGPL
10257  * <script type="text/javascript">
10258  */
10259 /**
10260  * @class Roo.TabPanel
10261  * @extends Roo.util.Observable
10262  * A lightweight tab container.
10263  * <br><br>
10264  * Usage:
10265  * <pre><code>
10266 // basic tabs 1, built from existing content
10267 var tabs = new Roo.TabPanel("tabs1");
10268 tabs.addTab("script", "View Script");
10269 tabs.addTab("markup", "View Markup");
10270 tabs.activate("script");
10271
10272 // more advanced tabs, built from javascript
10273 var jtabs = new Roo.TabPanel("jtabs");
10274 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10275
10276 // set up the UpdateManager
10277 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10278 var updater = tab2.getUpdateManager();
10279 updater.setDefaultUrl("ajax1.htm");
10280 tab2.on('activate', updater.refresh, updater, true);
10281
10282 // Use setUrl for Ajax loading
10283 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10284 tab3.setUrl("ajax2.htm", null, true);
10285
10286 // Disabled tab
10287 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10288 tab4.disable();
10289
10290 jtabs.activate("jtabs-1");
10291  * </code></pre>
10292  * @constructor
10293  * Create a new TabPanel.
10294  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10295  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10296  */
10297 Roo.TabPanel = function(container, config){
10298     /**
10299     * The container element for this TabPanel.
10300     * @type Roo.Element
10301     */
10302     this.el = Roo.get(container, true);
10303     if(config){
10304         if(typeof config == "boolean"){
10305             this.tabPosition = config ? "bottom" : "top";
10306         }else{
10307             Roo.apply(this, config);
10308         }
10309     }
10310     if(this.tabPosition == "bottom"){
10311         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10312         this.el.addClass("x-tabs-bottom");
10313     }
10314     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10315     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10316     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10317     if(Roo.isIE){
10318         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10319     }
10320     if(this.tabPosition != "bottom"){
10321         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10322          * @type Roo.Element
10323          */
10324         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10325         this.el.addClass("x-tabs-top");
10326     }
10327     this.items = [];
10328
10329     this.bodyEl.setStyle("position", "relative");
10330
10331     this.active = null;
10332     this.activateDelegate = this.activate.createDelegate(this);
10333
10334     this.addEvents({
10335         /**
10336          * @event tabchange
10337          * Fires when the active tab changes
10338          * @param {Roo.TabPanel} this
10339          * @param {Roo.TabPanelItem} activePanel The new active tab
10340          */
10341         "tabchange": true,
10342         /**
10343          * @event beforetabchange
10344          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10345          * @param {Roo.TabPanel} this
10346          * @param {Object} e Set cancel to true on this object to cancel the tab change
10347          * @param {Roo.TabPanelItem} tab The tab being changed to
10348          */
10349         "beforetabchange" : true
10350     });
10351
10352     Roo.EventManager.onWindowResize(this.onResize, this);
10353     this.cpad = this.el.getPadding("lr");
10354     this.hiddenCount = 0;
10355
10356
10357     // toolbar on the tabbar support...
10358     if (this.toolbar) {
10359         var tcfg = this.toolbar;
10360         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10361         this.toolbar = new Roo.Toolbar(tcfg);
10362         if (Roo.isSafari) {
10363             var tbl = tcfg.container.child('table', true);
10364             tbl.setAttribute('width', '100%');
10365         }
10366         
10367     }
10368    
10369
10370
10371     Roo.TabPanel.superclass.constructor.call(this);
10372 };
10373
10374 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10375     /*
10376      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10377      */
10378     tabPosition : "top",
10379     /*
10380      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10381      */
10382     currentTabWidth : 0,
10383     /*
10384      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10385      */
10386     minTabWidth : 40,
10387     /*
10388      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10389      */
10390     maxTabWidth : 250,
10391     /*
10392      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10393      */
10394     preferredTabWidth : 175,
10395     /*
10396      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10397      */
10398     resizeTabs : false,
10399     /*
10400      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10401      */
10402     monitorResize : true,
10403     /*
10404      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10405      */
10406     toolbar : false,
10407
10408     /**
10409      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10410      * @param {String} id The id of the div to use <b>or create</b>
10411      * @param {String} text The text for the tab
10412      * @param {String} content (optional) Content to put in the TabPanelItem body
10413      * @param {Boolean} closable (optional) True to create a close icon on the tab
10414      * @return {Roo.TabPanelItem} The created TabPanelItem
10415      */
10416     addTab : function(id, text, content, closable){
10417         var item = new Roo.TabPanelItem(this, id, text, closable);
10418         this.addTabItem(item);
10419         if(content){
10420             item.setContent(content);
10421         }
10422         return item;
10423     },
10424
10425     /**
10426      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10427      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10428      * @return {Roo.TabPanelItem}
10429      */
10430     getTab : function(id){
10431         return this.items[id];
10432     },
10433
10434     /**
10435      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10436      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10437      */
10438     hideTab : function(id){
10439         var t = this.items[id];
10440         if(!t.isHidden()){
10441            t.setHidden(true);
10442            this.hiddenCount++;
10443            this.autoSizeTabs();
10444         }
10445     },
10446
10447     /**
10448      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10449      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10450      */
10451     unhideTab : function(id){
10452         var t = this.items[id];
10453         if(t.isHidden()){
10454            t.setHidden(false);
10455            this.hiddenCount--;
10456            this.autoSizeTabs();
10457         }
10458     },
10459
10460     /**
10461      * Adds an existing {@link Roo.TabPanelItem}.
10462      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10463      */
10464     addTabItem : function(item){
10465         this.items[item.id] = item;
10466         this.items.push(item);
10467         if(this.resizeTabs){
10468            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10469            this.autoSizeTabs();
10470         }else{
10471             item.autoSize();
10472         }
10473     },
10474
10475     /**
10476      * Removes a {@link Roo.TabPanelItem}.
10477      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10478      */
10479     removeTab : function(id){
10480         var items = this.items;
10481         var tab = items[id];
10482         if(!tab) { return; }
10483         var index = items.indexOf(tab);
10484         if(this.active == tab && items.length > 1){
10485             var newTab = this.getNextAvailable(index);
10486             if(newTab) {
10487                 newTab.activate();
10488             }
10489         }
10490         this.stripEl.dom.removeChild(tab.pnode.dom);
10491         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10492             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10493         }
10494         items.splice(index, 1);
10495         delete this.items[tab.id];
10496         tab.fireEvent("close", tab);
10497         tab.purgeListeners();
10498         this.autoSizeTabs();
10499     },
10500
10501     getNextAvailable : function(start){
10502         var items = this.items;
10503         var index = start;
10504         // look for a next tab that will slide over to
10505         // replace the one being removed
10506         while(index < items.length){
10507             var item = items[++index];
10508             if(item && !item.isHidden()){
10509                 return item;
10510             }
10511         }
10512         // if one isn't found select the previous tab (on the left)
10513         index = start;
10514         while(index >= 0){
10515             var item = items[--index];
10516             if(item && !item.isHidden()){
10517                 return item;
10518             }
10519         }
10520         return null;
10521     },
10522
10523     /**
10524      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10525      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10526      */
10527     disableTab : function(id){
10528         var tab = this.items[id];
10529         if(tab && this.active != tab){
10530             tab.disable();
10531         }
10532     },
10533
10534     /**
10535      * Enables a {@link Roo.TabPanelItem} that is disabled.
10536      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10537      */
10538     enableTab : function(id){
10539         var tab = this.items[id];
10540         tab.enable();
10541     },
10542
10543     /**
10544      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10545      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10546      * @return {Roo.TabPanelItem} The TabPanelItem.
10547      */
10548     activate : function(id){
10549         var tab = this.items[id];
10550         if(!tab){
10551             return null;
10552         }
10553         if(tab == this.active || tab.disabled){
10554             return tab;
10555         }
10556         var e = {};
10557         this.fireEvent("beforetabchange", this, e, tab);
10558         if(e.cancel !== true && !tab.disabled){
10559             if(this.active){
10560                 this.active.hide();
10561             }
10562             this.active = this.items[id];
10563             this.active.show();
10564             this.fireEvent("tabchange", this, this.active);
10565         }
10566         return tab;
10567     },
10568
10569     /**
10570      * Gets the active {@link Roo.TabPanelItem}.
10571      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10572      */
10573     getActiveTab : function(){
10574         return this.active;
10575     },
10576
10577     /**
10578      * Updates the tab body element to fit the height of the container element
10579      * for overflow scrolling
10580      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10581      */
10582     syncHeight : function(targetHeight){
10583         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10584         var bm = this.bodyEl.getMargins();
10585         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10586         this.bodyEl.setHeight(newHeight);
10587         return newHeight;
10588     },
10589
10590     onResize : function(){
10591         if(this.monitorResize){
10592             this.autoSizeTabs();
10593         }
10594     },
10595
10596     /**
10597      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10598      */
10599     beginUpdate : function(){
10600         this.updating = true;
10601     },
10602
10603     /**
10604      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10605      */
10606     endUpdate : function(){
10607         this.updating = false;
10608         this.autoSizeTabs();
10609     },
10610
10611     /**
10612      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10613      */
10614     autoSizeTabs : function(){
10615         var count = this.items.length;
10616         var vcount = count - this.hiddenCount;
10617         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10618         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10619         var availWidth = Math.floor(w / vcount);
10620         var b = this.stripBody;
10621         if(b.getWidth() > w){
10622             var tabs = this.items;
10623             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10624             if(availWidth < this.minTabWidth){
10625                 /*if(!this.sleft){    // incomplete scrolling code
10626                     this.createScrollButtons();
10627                 }
10628                 this.showScroll();
10629                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10630             }
10631         }else{
10632             if(this.currentTabWidth < this.preferredTabWidth){
10633                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10634             }
10635         }
10636     },
10637
10638     /**
10639      * Returns the number of tabs in this TabPanel.
10640      * @return {Number}
10641      */
10642      getCount : function(){
10643          return this.items.length;
10644      },
10645
10646     /**
10647      * Resizes all the tabs to the passed width
10648      * @param {Number} The new width
10649      */
10650     setTabWidth : function(width){
10651         this.currentTabWidth = width;
10652         for(var i = 0, len = this.items.length; i < len; i++) {
10653                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10654         }
10655     },
10656
10657     /**
10658      * Destroys this TabPanel
10659      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10660      */
10661     destroy : function(removeEl){
10662         Roo.EventManager.removeResizeListener(this.onResize, this);
10663         for(var i = 0, len = this.items.length; i < len; i++){
10664             this.items[i].purgeListeners();
10665         }
10666         if(removeEl === true){
10667             this.el.update("");
10668             this.el.remove();
10669         }
10670     }
10671 });
10672
10673 /**
10674  * @class Roo.TabPanelItem
10675  * @extends Roo.util.Observable
10676  * Represents an individual item (tab plus body) in a TabPanel.
10677  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10678  * @param {String} id The id of this TabPanelItem
10679  * @param {String} text The text for the tab of this TabPanelItem
10680  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10681  */
10682 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10683     /**
10684      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10685      * @type Roo.TabPanel
10686      */
10687     this.tabPanel = tabPanel;
10688     /**
10689      * The id for this TabPanelItem
10690      * @type String
10691      */
10692     this.id = id;
10693     /** @private */
10694     this.disabled = false;
10695     /** @private */
10696     this.text = text;
10697     /** @private */
10698     this.loaded = false;
10699     this.closable = closable;
10700
10701     /**
10702      * The body element for this TabPanelItem.
10703      * @type Roo.Element
10704      */
10705     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10706     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10707     this.bodyEl.setStyle("display", "block");
10708     this.bodyEl.setStyle("zoom", "1");
10709     this.hideAction();
10710
10711     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10712     /** @private */
10713     this.el = Roo.get(els.el, true);
10714     this.inner = Roo.get(els.inner, true);
10715     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10716     this.pnode = Roo.get(els.el.parentNode, true);
10717     this.el.on("mousedown", this.onTabMouseDown, this);
10718     this.el.on("click", this.onTabClick, this);
10719     /** @private */
10720     if(closable){
10721         var c = Roo.get(els.close, true);
10722         c.dom.title = this.closeText;
10723         c.addClassOnOver("close-over");
10724         c.on("click", this.closeClick, this);
10725      }
10726
10727     this.addEvents({
10728          /**
10729          * @event activate
10730          * Fires when this tab becomes the active tab.
10731          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10732          * @param {Roo.TabPanelItem} this
10733          */
10734         "activate": true,
10735         /**
10736          * @event beforeclose
10737          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10738          * @param {Roo.TabPanelItem} this
10739          * @param {Object} e Set cancel to true on this object to cancel the close.
10740          */
10741         "beforeclose": true,
10742         /**
10743          * @event close
10744          * Fires when this tab is closed.
10745          * @param {Roo.TabPanelItem} this
10746          */
10747          "close": true,
10748         /**
10749          * @event deactivate
10750          * Fires when this tab is no longer the active tab.
10751          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10752          * @param {Roo.TabPanelItem} this
10753          */
10754          "deactivate" : true
10755     });
10756     this.hidden = false;
10757
10758     Roo.TabPanelItem.superclass.constructor.call(this);
10759 };
10760
10761 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10762     purgeListeners : function(){
10763        Roo.util.Observable.prototype.purgeListeners.call(this);
10764        this.el.removeAllListeners();
10765     },
10766     /**
10767      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10768      */
10769     show : function(){
10770         this.pnode.addClass("on");
10771         this.showAction();
10772         if(Roo.isOpera){
10773             this.tabPanel.stripWrap.repaint();
10774         }
10775         this.fireEvent("activate", this.tabPanel, this);
10776     },
10777
10778     /**
10779      * Returns true if this tab is the active tab.
10780      * @return {Boolean}
10781      */
10782     isActive : function(){
10783         return this.tabPanel.getActiveTab() == this;
10784     },
10785
10786     /**
10787      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10788      */
10789     hide : function(){
10790         this.pnode.removeClass("on");
10791         this.hideAction();
10792         this.fireEvent("deactivate", this.tabPanel, this);
10793     },
10794
10795     hideAction : function(){
10796         this.bodyEl.hide();
10797         this.bodyEl.setStyle("position", "absolute");
10798         this.bodyEl.setLeft("-20000px");
10799         this.bodyEl.setTop("-20000px");
10800     },
10801
10802     showAction : function(){
10803         this.bodyEl.setStyle("position", "relative");
10804         this.bodyEl.setTop("");
10805         this.bodyEl.setLeft("");
10806         this.bodyEl.show();
10807     },
10808
10809     /**
10810      * Set the tooltip for the tab.
10811      * @param {String} tooltip The tab's tooltip
10812      */
10813     setTooltip : function(text){
10814         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10815             this.textEl.dom.qtip = text;
10816             this.textEl.dom.removeAttribute('title');
10817         }else{
10818             this.textEl.dom.title = text;
10819         }
10820     },
10821
10822     onTabClick : function(e){
10823         e.preventDefault();
10824         this.tabPanel.activate(this.id);
10825     },
10826
10827     onTabMouseDown : function(e){
10828         e.preventDefault();
10829         this.tabPanel.activate(this.id);
10830     },
10831
10832     getWidth : function(){
10833         return this.inner.getWidth();
10834     },
10835
10836     setWidth : function(width){
10837         var iwidth = width - this.pnode.getPadding("lr");
10838         this.inner.setWidth(iwidth);
10839         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10840         this.pnode.setWidth(width);
10841     },
10842
10843     /**
10844      * Show or hide the tab
10845      * @param {Boolean} hidden True to hide or false to show.
10846      */
10847     setHidden : function(hidden){
10848         this.hidden = hidden;
10849         this.pnode.setStyle("display", hidden ? "none" : "");
10850     },
10851
10852     /**
10853      * Returns true if this tab is "hidden"
10854      * @return {Boolean}
10855      */
10856     isHidden : function(){
10857         return this.hidden;
10858     },
10859
10860     /**
10861      * Returns the text for this tab
10862      * @return {String}
10863      */
10864     getText : function(){
10865         return this.text;
10866     },
10867
10868     autoSize : function(){
10869         //this.el.beginMeasure();
10870         this.textEl.setWidth(1);
10871         /*
10872          *  #2804 [new] Tabs in Roojs
10873          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10874          */
10875         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10876         //this.el.endMeasure();
10877     },
10878
10879     /**
10880      * Sets the text for the tab (Note: this also sets the tooltip text)
10881      * @param {String} text The tab's text and tooltip
10882      */
10883     setText : function(text){
10884         this.text = text;
10885         this.textEl.update(text);
10886         this.setTooltip(text);
10887         if(!this.tabPanel.resizeTabs){
10888             this.autoSize();
10889         }
10890     },
10891     /**
10892      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10893      */
10894     activate : function(){
10895         this.tabPanel.activate(this.id);
10896     },
10897
10898     /**
10899      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10900      */
10901     disable : function(){
10902         if(this.tabPanel.active != this){
10903             this.disabled = true;
10904             this.pnode.addClass("disabled");
10905         }
10906     },
10907
10908     /**
10909      * Enables this TabPanelItem if it was previously disabled.
10910      */
10911     enable : function(){
10912         this.disabled = false;
10913         this.pnode.removeClass("disabled");
10914     },
10915
10916     /**
10917      * Sets the content for this TabPanelItem.
10918      * @param {String} content The content
10919      * @param {Boolean} loadScripts true to look for and load scripts
10920      */
10921     setContent : function(content, loadScripts){
10922         this.bodyEl.update(content, loadScripts);
10923     },
10924
10925     /**
10926      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10927      * @return {Roo.UpdateManager} The UpdateManager
10928      */
10929     getUpdateManager : function(){
10930         return this.bodyEl.getUpdateManager();
10931     },
10932
10933     /**
10934      * Set a URL to be used to load the content for this TabPanelItem.
10935      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10936      * @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)
10937      * @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)
10938      * @return {Roo.UpdateManager} The UpdateManager
10939      */
10940     setUrl : function(url, params, loadOnce){
10941         if(this.refreshDelegate){
10942             this.un('activate', this.refreshDelegate);
10943         }
10944         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10945         this.on("activate", this.refreshDelegate);
10946         return this.bodyEl.getUpdateManager();
10947     },
10948
10949     /** @private */
10950     _handleRefresh : function(url, params, loadOnce){
10951         if(!loadOnce || !this.loaded){
10952             var updater = this.bodyEl.getUpdateManager();
10953             updater.update(url, params, this._setLoaded.createDelegate(this));
10954         }
10955     },
10956
10957     /**
10958      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10959      *   Will fail silently if the setUrl method has not been called.
10960      *   This does not activate the panel, just updates its content.
10961      */
10962     refresh : function(){
10963         if(this.refreshDelegate){
10964            this.loaded = false;
10965            this.refreshDelegate();
10966         }
10967     },
10968
10969     /** @private */
10970     _setLoaded : function(){
10971         this.loaded = true;
10972     },
10973
10974     /** @private */
10975     closeClick : function(e){
10976         var o = {};
10977         e.stopEvent();
10978         this.fireEvent("beforeclose", this, o);
10979         if(o.cancel !== true){
10980             this.tabPanel.removeTab(this.id);
10981         }
10982     },
10983     /**
10984      * The text displayed in the tooltip for the close icon.
10985      * @type String
10986      */
10987     closeText : "Close this tab"
10988 });
10989
10990 /** @private */
10991 Roo.TabPanel.prototype.createStrip = function(container){
10992     var strip = document.createElement("div");
10993     strip.className = "x-tabs-wrap";
10994     container.appendChild(strip);
10995     return strip;
10996 };
10997 /** @private */
10998 Roo.TabPanel.prototype.createStripList = function(strip){
10999     // div wrapper for retard IE
11000     // returns the "tr" element.
11001     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11002         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11003         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11004     return strip.firstChild.firstChild.firstChild.firstChild;
11005 };
11006 /** @private */
11007 Roo.TabPanel.prototype.createBody = function(container){
11008     var body = document.createElement("div");
11009     Roo.id(body, "tab-body");
11010     Roo.fly(body).addClass("x-tabs-body");
11011     container.appendChild(body);
11012     return body;
11013 };
11014 /** @private */
11015 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11016     var body = Roo.getDom(id);
11017     if(!body){
11018         body = document.createElement("div");
11019         body.id = id;
11020     }
11021     Roo.fly(body).addClass("x-tabs-item-body");
11022     bodyEl.insertBefore(body, bodyEl.firstChild);
11023     return body;
11024 };
11025 /** @private */
11026 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11027     var td = document.createElement("td");
11028     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11029     //stripEl.appendChild(td);
11030     if(closable){
11031         td.className = "x-tabs-closable";
11032         if(!this.closeTpl){
11033             this.closeTpl = new Roo.Template(
11034                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11035                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11036                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11037             );
11038         }
11039         var el = this.closeTpl.overwrite(td, {"text": text});
11040         var close = el.getElementsByTagName("div")[0];
11041         var inner = el.getElementsByTagName("em")[0];
11042         return {"el": el, "close": close, "inner": inner};
11043     } else {
11044         if(!this.tabTpl){
11045             this.tabTpl = new Roo.Template(
11046                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11047                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11048             );
11049         }
11050         var el = this.tabTpl.overwrite(td, {"text": text});
11051         var inner = el.getElementsByTagName("em")[0];
11052         return {"el": el, "inner": inner};
11053     }
11054 };/*
11055  * Based on:
11056  * Ext JS Library 1.1.1
11057  * Copyright(c) 2006-2007, Ext JS, LLC.
11058  *
11059  * Originally Released Under LGPL - original licence link has changed is not relivant.
11060  *
11061  * Fork - LGPL
11062  * <script type="text/javascript">
11063  */
11064
11065 /**
11066  * @class Roo.Button
11067  * @extends Roo.util.Observable
11068  * Simple Button class
11069  * @cfg {String} text The button text
11070  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11071  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11072  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11073  * @cfg {Object} scope The scope of the handler
11074  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11075  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11076  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11077  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11078  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11079  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11080    applies if enableToggle = true)
11081  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11082  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11083   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11084  * @constructor
11085  * Create a new button
11086  * @param {Object} config The config object
11087  */
11088 Roo.Button = function(renderTo, config)
11089 {
11090     if (!config) {
11091         config = renderTo;
11092         renderTo = config.renderTo || false;
11093     }
11094     
11095     Roo.apply(this, config);
11096     this.addEvents({
11097         /**
11098              * @event click
11099              * Fires when this button is clicked
11100              * @param {Button} this
11101              * @param {EventObject} e The click event
11102              */
11103             "click" : true,
11104         /**
11105              * @event toggle
11106              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11107              * @param {Button} this
11108              * @param {Boolean} pressed
11109              */
11110             "toggle" : true,
11111         /**
11112              * @event mouseover
11113              * Fires when the mouse hovers over the button
11114              * @param {Button} this
11115              * @param {Event} e The event object
11116              */
11117         'mouseover' : true,
11118         /**
11119              * @event mouseout
11120              * Fires when the mouse exits the button
11121              * @param {Button} this
11122              * @param {Event} e The event object
11123              */
11124         'mouseout': true,
11125          /**
11126              * @event render
11127              * Fires when the button is rendered
11128              * @param {Button} this
11129              */
11130         'render': true
11131     });
11132     if(this.menu){
11133         this.menu = Roo.menu.MenuMgr.get(this.menu);
11134     }
11135     // register listeners first!!  - so render can be captured..
11136     Roo.util.Observable.call(this);
11137     if(renderTo){
11138         this.render(renderTo);
11139     }
11140     
11141   
11142 };
11143
11144 Roo.extend(Roo.Button, Roo.util.Observable, {
11145     /**
11146      * 
11147      */
11148     
11149     /**
11150      * Read-only. True if this button is hidden
11151      * @type Boolean
11152      */
11153     hidden : false,
11154     /**
11155      * Read-only. True if this button is disabled
11156      * @type Boolean
11157      */
11158     disabled : false,
11159     /**
11160      * Read-only. True if this button is pressed (only if enableToggle = true)
11161      * @type Boolean
11162      */
11163     pressed : false,
11164
11165     /**
11166      * @cfg {Number} tabIndex 
11167      * The DOM tabIndex for this button (defaults to undefined)
11168      */
11169     tabIndex : undefined,
11170
11171     /**
11172      * @cfg {Boolean} enableToggle
11173      * True to enable pressed/not pressed toggling (defaults to false)
11174      */
11175     enableToggle: false,
11176     /**
11177      * @cfg {Mixed} menu
11178      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11179      */
11180     menu : undefined,
11181     /**
11182      * @cfg {String} menuAlign
11183      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11184      */
11185     menuAlign : "tl-bl?",
11186
11187     /**
11188      * @cfg {String} iconCls
11189      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11190      */
11191     iconCls : undefined,
11192     /**
11193      * @cfg {String} type
11194      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11195      */
11196     type : 'button',
11197
11198     // private
11199     menuClassTarget: 'tr',
11200
11201     /**
11202      * @cfg {String} clickEvent
11203      * The type of event to map to the button's event handler (defaults to 'click')
11204      */
11205     clickEvent : 'click',
11206
11207     /**
11208      * @cfg {Boolean} handleMouseEvents
11209      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11210      */
11211     handleMouseEvents : true,
11212
11213     /**
11214      * @cfg {String} tooltipType
11215      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11216      */
11217     tooltipType : 'qtip',
11218
11219     /**
11220      * @cfg {String} cls
11221      * A CSS class to apply to the button's main element.
11222      */
11223     
11224     /**
11225      * @cfg {Roo.Template} template (Optional)
11226      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11227      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11228      * require code modifications if required elements (e.g. a button) aren't present.
11229      */
11230
11231     // private
11232     render : function(renderTo){
11233         var btn;
11234         if(this.hideParent){
11235             this.parentEl = Roo.get(renderTo);
11236         }
11237         if(!this.dhconfig){
11238             if(!this.template){
11239                 if(!Roo.Button.buttonTemplate){
11240                     // hideous table template
11241                     Roo.Button.buttonTemplate = new Roo.Template(
11242                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11243                         '<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>',
11244                         "</tr></tbody></table>");
11245                 }
11246                 this.template = Roo.Button.buttonTemplate;
11247             }
11248             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11249             var btnEl = btn.child("button:first");
11250             btnEl.on('focus', this.onFocus, this);
11251             btnEl.on('blur', this.onBlur, this);
11252             if(this.cls){
11253                 btn.addClass(this.cls);
11254             }
11255             if(this.icon){
11256                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11257             }
11258             if(this.iconCls){
11259                 btnEl.addClass(this.iconCls);
11260                 if(!this.cls){
11261                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11262                 }
11263             }
11264             if(this.tabIndex !== undefined){
11265                 btnEl.dom.tabIndex = this.tabIndex;
11266             }
11267             if(this.tooltip){
11268                 if(typeof this.tooltip == 'object'){
11269                     Roo.QuickTips.tips(Roo.apply({
11270                           target: btnEl.id
11271                     }, this.tooltip));
11272                 } else {
11273                     btnEl.dom[this.tooltipType] = this.tooltip;
11274                 }
11275             }
11276         }else{
11277             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11278         }
11279         this.el = btn;
11280         if(this.id){
11281             this.el.dom.id = this.el.id = this.id;
11282         }
11283         if(this.menu){
11284             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11285             this.menu.on("show", this.onMenuShow, this);
11286             this.menu.on("hide", this.onMenuHide, this);
11287         }
11288         btn.addClass("x-btn");
11289         if(Roo.isIE && !Roo.isIE7){
11290             this.autoWidth.defer(1, this);
11291         }else{
11292             this.autoWidth();
11293         }
11294         if(this.handleMouseEvents){
11295             btn.on("mouseover", this.onMouseOver, this);
11296             btn.on("mouseout", this.onMouseOut, this);
11297             btn.on("mousedown", this.onMouseDown, this);
11298         }
11299         btn.on(this.clickEvent, this.onClick, this);
11300         //btn.on("mouseup", this.onMouseUp, this);
11301         if(this.hidden){
11302             this.hide();
11303         }
11304         if(this.disabled){
11305             this.disable();
11306         }
11307         Roo.ButtonToggleMgr.register(this);
11308         if(this.pressed){
11309             this.el.addClass("x-btn-pressed");
11310         }
11311         if(this.repeat){
11312             var repeater = new Roo.util.ClickRepeater(btn,
11313                 typeof this.repeat == "object" ? this.repeat : {}
11314             );
11315             repeater.on("click", this.onClick,  this);
11316         }
11317         
11318         this.fireEvent('render', this);
11319         
11320     },
11321     /**
11322      * Returns the button's underlying element
11323      * @return {Roo.Element} The element
11324      */
11325     getEl : function(){
11326         return this.el;  
11327     },
11328     
11329     /**
11330      * Destroys this Button and removes any listeners.
11331      */
11332     destroy : function(){
11333         Roo.ButtonToggleMgr.unregister(this);
11334         this.el.removeAllListeners();
11335         this.purgeListeners();
11336         this.el.remove();
11337     },
11338
11339     // private
11340     autoWidth : function(){
11341         if(this.el){
11342             this.el.setWidth("auto");
11343             if(Roo.isIE7 && Roo.isStrict){
11344                 var ib = this.el.child('button');
11345                 if(ib && ib.getWidth() > 20){
11346                     ib.clip();
11347                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11348                 }
11349             }
11350             if(this.minWidth){
11351                 if(this.hidden){
11352                     this.el.beginMeasure();
11353                 }
11354                 if(this.el.getWidth() < this.minWidth){
11355                     this.el.setWidth(this.minWidth);
11356                 }
11357                 if(this.hidden){
11358                     this.el.endMeasure();
11359                 }
11360             }
11361         }
11362     },
11363
11364     /**
11365      * Assigns this button's click handler
11366      * @param {Function} handler The function to call when the button is clicked
11367      * @param {Object} scope (optional) Scope for the function passed in
11368      */
11369     setHandler : function(handler, scope){
11370         this.handler = handler;
11371         this.scope = scope;  
11372     },
11373     
11374     /**
11375      * Sets this button's text
11376      * @param {String} text The button text
11377      */
11378     setText : function(text){
11379         this.text = text;
11380         if(this.el){
11381             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11382         }
11383         this.autoWidth();
11384     },
11385     
11386     /**
11387      * Gets the text for this button
11388      * @return {String} The button text
11389      */
11390     getText : function(){
11391         return this.text;  
11392     },
11393     
11394     /**
11395      * Show this button
11396      */
11397     show: function(){
11398         this.hidden = false;
11399         if(this.el){
11400             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11401         }
11402     },
11403     
11404     /**
11405      * Hide this button
11406      */
11407     hide: function(){
11408         this.hidden = true;
11409         if(this.el){
11410             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11411         }
11412     },
11413     
11414     /**
11415      * Convenience function for boolean show/hide
11416      * @param {Boolean} visible True to show, false to hide
11417      */
11418     setVisible: function(visible){
11419         if(visible) {
11420             this.show();
11421         }else{
11422             this.hide();
11423         }
11424     },
11425     
11426     /**
11427      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11428      * @param {Boolean} state (optional) Force a particular state
11429      */
11430     toggle : function(state){
11431         state = state === undefined ? !this.pressed : state;
11432         if(state != this.pressed){
11433             if(state){
11434                 this.el.addClass("x-btn-pressed");
11435                 this.pressed = true;
11436                 this.fireEvent("toggle", this, true);
11437             }else{
11438                 this.el.removeClass("x-btn-pressed");
11439                 this.pressed = false;
11440                 this.fireEvent("toggle", this, false);
11441             }
11442             if(this.toggleHandler){
11443                 this.toggleHandler.call(this.scope || this, this, state);
11444             }
11445         }
11446     },
11447     
11448     /**
11449      * Focus the button
11450      */
11451     focus : function(){
11452         this.el.child('button:first').focus();
11453     },
11454     
11455     /**
11456      * Disable this button
11457      */
11458     disable : function(){
11459         if(this.el){
11460             this.el.addClass("x-btn-disabled");
11461         }
11462         this.disabled = true;
11463     },
11464     
11465     /**
11466      * Enable this button
11467      */
11468     enable : function(){
11469         if(this.el){
11470             this.el.removeClass("x-btn-disabled");
11471         }
11472         this.disabled = false;
11473     },
11474
11475     /**
11476      * Convenience function for boolean enable/disable
11477      * @param {Boolean} enabled True to enable, false to disable
11478      */
11479     setDisabled : function(v){
11480         this[v !== true ? "enable" : "disable"]();
11481     },
11482
11483     // private
11484     onClick : function(e)
11485     {
11486         if(e){
11487             e.preventDefault();
11488         }
11489         if(e.button != 0){
11490             return;
11491         }
11492         if(!this.disabled){
11493             if(this.enableToggle){
11494                 this.toggle();
11495             }
11496             if(this.menu && !this.menu.isVisible()){
11497                 this.menu.show(this.el, this.menuAlign);
11498             }
11499             this.fireEvent("click", this, e);
11500             if(this.handler){
11501                 this.el.removeClass("x-btn-over");
11502                 this.handler.call(this.scope || this, this, e);
11503             }
11504         }
11505     },
11506     // private
11507     onMouseOver : function(e){
11508         if(!this.disabled){
11509             this.el.addClass("x-btn-over");
11510             this.fireEvent('mouseover', this, e);
11511         }
11512     },
11513     // private
11514     onMouseOut : function(e){
11515         if(!e.within(this.el,  true)){
11516             this.el.removeClass("x-btn-over");
11517             this.fireEvent('mouseout', this, e);
11518         }
11519     },
11520     // private
11521     onFocus : function(e){
11522         if(!this.disabled){
11523             this.el.addClass("x-btn-focus");
11524         }
11525     },
11526     // private
11527     onBlur : function(e){
11528         this.el.removeClass("x-btn-focus");
11529     },
11530     // private
11531     onMouseDown : function(e){
11532         if(!this.disabled && e.button == 0){
11533             this.el.addClass("x-btn-click");
11534             Roo.get(document).on('mouseup', this.onMouseUp, this);
11535         }
11536     },
11537     // private
11538     onMouseUp : function(e){
11539         if(e.button == 0){
11540             this.el.removeClass("x-btn-click");
11541             Roo.get(document).un('mouseup', this.onMouseUp, this);
11542         }
11543     },
11544     // private
11545     onMenuShow : function(e){
11546         this.el.addClass("x-btn-menu-active");
11547     },
11548     // private
11549     onMenuHide : function(e){
11550         this.el.removeClass("x-btn-menu-active");
11551     }   
11552 });
11553
11554 // Private utility class used by Button
11555 Roo.ButtonToggleMgr = function(){
11556    var groups = {};
11557    
11558    function toggleGroup(btn, state){
11559        if(state){
11560            var g = groups[btn.toggleGroup];
11561            for(var i = 0, l = g.length; i < l; i++){
11562                if(g[i] != btn){
11563                    g[i].toggle(false);
11564                }
11565            }
11566        }
11567    }
11568    
11569    return {
11570        register : function(btn){
11571            if(!btn.toggleGroup){
11572                return;
11573            }
11574            var g = groups[btn.toggleGroup];
11575            if(!g){
11576                g = groups[btn.toggleGroup] = [];
11577            }
11578            g.push(btn);
11579            btn.on("toggle", toggleGroup);
11580        },
11581        
11582        unregister : function(btn){
11583            if(!btn.toggleGroup){
11584                return;
11585            }
11586            var g = groups[btn.toggleGroup];
11587            if(g){
11588                g.remove(btn);
11589                btn.un("toggle", toggleGroup);
11590            }
11591        }
11592    };
11593 }();/*
11594  * Based on:
11595  * Ext JS Library 1.1.1
11596  * Copyright(c) 2006-2007, Ext JS, LLC.
11597  *
11598  * Originally Released Under LGPL - original licence link has changed is not relivant.
11599  *
11600  * Fork - LGPL
11601  * <script type="text/javascript">
11602  */
11603  
11604 /**
11605  * @class Roo.SplitButton
11606  * @extends Roo.Button
11607  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11608  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11609  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11610  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11611  * @cfg {String} arrowTooltip The title attribute of the arrow
11612  * @constructor
11613  * Create a new menu button
11614  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11615  * @param {Object} config The config object
11616  */
11617 Roo.SplitButton = function(renderTo, config){
11618     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11619     /**
11620      * @event arrowclick
11621      * Fires when this button's arrow is clicked
11622      * @param {SplitButton} this
11623      * @param {EventObject} e The click event
11624      */
11625     this.addEvents({"arrowclick":true});
11626 };
11627
11628 Roo.extend(Roo.SplitButton, Roo.Button, {
11629     render : function(renderTo){
11630         // this is one sweet looking template!
11631         var tpl = new Roo.Template(
11632             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11633             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11634             '<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>',
11635             "</tbody></table></td><td>",
11636             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11637             '<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>',
11638             "</tbody></table></td></tr></table>"
11639         );
11640         var btn = tpl.append(renderTo, [this.text, this.type], true);
11641         var btnEl = btn.child("button");
11642         if(this.cls){
11643             btn.addClass(this.cls);
11644         }
11645         if(this.icon){
11646             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11647         }
11648         if(this.iconCls){
11649             btnEl.addClass(this.iconCls);
11650             if(!this.cls){
11651                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11652             }
11653         }
11654         this.el = btn;
11655         if(this.handleMouseEvents){
11656             btn.on("mouseover", this.onMouseOver, this);
11657             btn.on("mouseout", this.onMouseOut, this);
11658             btn.on("mousedown", this.onMouseDown, this);
11659             btn.on("mouseup", this.onMouseUp, this);
11660         }
11661         btn.on(this.clickEvent, this.onClick, this);
11662         if(this.tooltip){
11663             if(typeof this.tooltip == 'object'){
11664                 Roo.QuickTips.tips(Roo.apply({
11665                       target: btnEl.id
11666                 }, this.tooltip));
11667             } else {
11668                 btnEl.dom[this.tooltipType] = this.tooltip;
11669             }
11670         }
11671         if(this.arrowTooltip){
11672             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11673         }
11674         if(this.hidden){
11675             this.hide();
11676         }
11677         if(this.disabled){
11678             this.disable();
11679         }
11680         if(this.pressed){
11681             this.el.addClass("x-btn-pressed");
11682         }
11683         if(Roo.isIE && !Roo.isIE7){
11684             this.autoWidth.defer(1, this);
11685         }else{
11686             this.autoWidth();
11687         }
11688         if(this.menu){
11689             this.menu.on("show", this.onMenuShow, this);
11690             this.menu.on("hide", this.onMenuHide, this);
11691         }
11692         this.fireEvent('render', this);
11693     },
11694
11695     // private
11696     autoWidth : function(){
11697         if(this.el){
11698             var tbl = this.el.child("table:first");
11699             var tbl2 = this.el.child("table:last");
11700             this.el.setWidth("auto");
11701             tbl.setWidth("auto");
11702             if(Roo.isIE7 && Roo.isStrict){
11703                 var ib = this.el.child('button:first');
11704                 if(ib && ib.getWidth() > 20){
11705                     ib.clip();
11706                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11707                 }
11708             }
11709             if(this.minWidth){
11710                 if(this.hidden){
11711                     this.el.beginMeasure();
11712                 }
11713                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11714                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11715                 }
11716                 if(this.hidden){
11717                     this.el.endMeasure();
11718                 }
11719             }
11720             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11721         } 
11722     },
11723     /**
11724      * Sets this button's click handler
11725      * @param {Function} handler The function to call when the button is clicked
11726      * @param {Object} scope (optional) Scope for the function passed above
11727      */
11728     setHandler : function(handler, scope){
11729         this.handler = handler;
11730         this.scope = scope;  
11731     },
11732     
11733     /**
11734      * Sets this button's arrow click handler
11735      * @param {Function} handler The function to call when the arrow is clicked
11736      * @param {Object} scope (optional) Scope for the function passed above
11737      */
11738     setArrowHandler : function(handler, scope){
11739         this.arrowHandler = handler;
11740         this.scope = scope;  
11741     },
11742     
11743     /**
11744      * Focus the button
11745      */
11746     focus : function(){
11747         if(this.el){
11748             this.el.child("button:first").focus();
11749         }
11750     },
11751
11752     // private
11753     onClick : function(e){
11754         e.preventDefault();
11755         if(!this.disabled){
11756             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11757                 if(this.menu && !this.menu.isVisible()){
11758                     this.menu.show(this.el, this.menuAlign);
11759                 }
11760                 this.fireEvent("arrowclick", this, e);
11761                 if(this.arrowHandler){
11762                     this.arrowHandler.call(this.scope || this, this, e);
11763                 }
11764             }else{
11765                 this.fireEvent("click", this, e);
11766                 if(this.handler){
11767                     this.handler.call(this.scope || this, this, e);
11768                 }
11769             }
11770         }
11771     },
11772     // private
11773     onMouseDown : function(e){
11774         if(!this.disabled){
11775             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11776         }
11777     },
11778     // private
11779     onMouseUp : function(e){
11780         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11781     }   
11782 });
11783
11784
11785 // backwards compat
11786 Roo.MenuButton = Roo.SplitButton;/*
11787  * Based on:
11788  * Ext JS Library 1.1.1
11789  * Copyright(c) 2006-2007, Ext JS, LLC.
11790  *
11791  * Originally Released Under LGPL - original licence link has changed is not relivant.
11792  *
11793  * Fork - LGPL
11794  * <script type="text/javascript">
11795  */
11796
11797 /**
11798  * @class Roo.Toolbar
11799  * Basic Toolbar class.
11800  * @constructor
11801  * Creates a new Toolbar
11802  * @param {Object} container The config object
11803  */ 
11804 Roo.Toolbar = function(container, buttons, config)
11805 {
11806     /// old consturctor format still supported..
11807     if(container instanceof Array){ // omit the container for later rendering
11808         buttons = container;
11809         config = buttons;
11810         container = null;
11811     }
11812     if (typeof(container) == 'object' && container.xtype) {
11813         config = container;
11814         container = config.container;
11815         buttons = config.buttons || []; // not really - use items!!
11816     }
11817     var xitems = [];
11818     if (config && config.items) {
11819         xitems = config.items;
11820         delete config.items;
11821     }
11822     Roo.apply(this, config);
11823     this.buttons = buttons;
11824     
11825     if(container){
11826         this.render(container);
11827     }
11828     this.xitems = xitems;
11829     Roo.each(xitems, function(b) {
11830         this.add(b);
11831     }, this);
11832     
11833 };
11834
11835 Roo.Toolbar.prototype = {
11836     /**
11837      * @cfg {Array} items
11838      * array of button configs or elements to add (will be converted to a MixedCollection)
11839      */
11840     
11841     /**
11842      * @cfg {String/HTMLElement/Element} container
11843      * The id or element that will contain the toolbar
11844      */
11845     // private
11846     render : function(ct){
11847         this.el = Roo.get(ct);
11848         if(this.cls){
11849             this.el.addClass(this.cls);
11850         }
11851         // using a table allows for vertical alignment
11852         // 100% width is needed by Safari...
11853         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11854         this.tr = this.el.child("tr", true);
11855         var autoId = 0;
11856         this.items = new Roo.util.MixedCollection(false, function(o){
11857             return o.id || ("item" + (++autoId));
11858         });
11859         if(this.buttons){
11860             this.add.apply(this, this.buttons);
11861             delete this.buttons;
11862         }
11863     },
11864
11865     /**
11866      * Adds element(s) to the toolbar -- this function takes a variable number of 
11867      * arguments of mixed type and adds them to the toolbar.
11868      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11869      * <ul>
11870      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11871      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11872      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11873      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11874      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11875      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11876      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11877      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11878      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11879      * </ul>
11880      * @param {Mixed} arg2
11881      * @param {Mixed} etc.
11882      */
11883     add : function(){
11884         var a = arguments, l = a.length;
11885         for(var i = 0; i < l; i++){
11886             this._add(a[i]);
11887         }
11888     },
11889     // private..
11890     _add : function(el) {
11891         
11892         if (el.xtype) {
11893             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11894         }
11895         
11896         if (el.applyTo){ // some kind of form field
11897             return this.addField(el);
11898         } 
11899         if (el.render){ // some kind of Toolbar.Item
11900             return this.addItem(el);
11901         }
11902         if (typeof el == "string"){ // string
11903             if(el == "separator" || el == "-"){
11904                 return this.addSeparator();
11905             }
11906             if (el == " "){
11907                 return this.addSpacer();
11908             }
11909             if(el == "->"){
11910                 return this.addFill();
11911             }
11912             return this.addText(el);
11913             
11914         }
11915         if(el.tagName){ // element
11916             return this.addElement(el);
11917         }
11918         if(typeof el == "object"){ // must be button config?
11919             return this.addButton(el);
11920         }
11921         // and now what?!?!
11922         return false;
11923         
11924     },
11925     
11926     /**
11927      * Add an Xtype element
11928      * @param {Object} xtype Xtype Object
11929      * @return {Object} created Object
11930      */
11931     addxtype : function(e){
11932         return this.add(e);  
11933     },
11934     
11935     /**
11936      * Returns the Element for this toolbar.
11937      * @return {Roo.Element}
11938      */
11939     getEl : function(){
11940         return this.el;  
11941     },
11942     
11943     /**
11944      * Adds a separator
11945      * @return {Roo.Toolbar.Item} The separator item
11946      */
11947     addSeparator : function(){
11948         return this.addItem(new Roo.Toolbar.Separator());
11949     },
11950
11951     /**
11952      * Adds a spacer element
11953      * @return {Roo.Toolbar.Spacer} The spacer item
11954      */
11955     addSpacer : function(){
11956         return this.addItem(new Roo.Toolbar.Spacer());
11957     },
11958
11959     /**
11960      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11961      * @return {Roo.Toolbar.Fill} The fill item
11962      */
11963     addFill : function(){
11964         return this.addItem(new Roo.Toolbar.Fill());
11965     },
11966
11967     /**
11968      * Adds any standard HTML element to the toolbar
11969      * @param {String/HTMLElement/Element} el The element or id of the element to add
11970      * @return {Roo.Toolbar.Item} The element's item
11971      */
11972     addElement : function(el){
11973         return this.addItem(new Roo.Toolbar.Item(el));
11974     },
11975     /**
11976      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11977      * @type Roo.util.MixedCollection  
11978      */
11979     items : false,
11980      
11981     /**
11982      * Adds any Toolbar.Item or subclass
11983      * @param {Roo.Toolbar.Item} item
11984      * @return {Roo.Toolbar.Item} The item
11985      */
11986     addItem : function(item){
11987         var td = this.nextBlock();
11988         item.render(td);
11989         this.items.add(item);
11990         return item;
11991     },
11992     
11993     /**
11994      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11995      * @param {Object/Array} config A button config or array of configs
11996      * @return {Roo.Toolbar.Button/Array}
11997      */
11998     addButton : function(config){
11999         if(config instanceof Array){
12000             var buttons = [];
12001             for(var i = 0, len = config.length; i < len; i++) {
12002                 buttons.push(this.addButton(config[i]));
12003             }
12004             return buttons;
12005         }
12006         var b = config;
12007         if(!(config instanceof Roo.Toolbar.Button)){
12008             b = config.split ?
12009                 new Roo.Toolbar.SplitButton(config) :
12010                 new Roo.Toolbar.Button(config);
12011         }
12012         var td = this.nextBlock();
12013         b.render(td);
12014         this.items.add(b);
12015         return b;
12016     },
12017     
12018     /**
12019      * Adds text to the toolbar
12020      * @param {String} text The text to add
12021      * @return {Roo.Toolbar.Item} The element's item
12022      */
12023     addText : function(text){
12024         return this.addItem(new Roo.Toolbar.TextItem(text));
12025     },
12026     
12027     /**
12028      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12029      * @param {Number} index The index where the item is to be inserted
12030      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12031      * @return {Roo.Toolbar.Button/Item}
12032      */
12033     insertButton : function(index, item){
12034         if(item instanceof Array){
12035             var buttons = [];
12036             for(var i = 0, len = item.length; i < len; i++) {
12037                buttons.push(this.insertButton(index + i, item[i]));
12038             }
12039             return buttons;
12040         }
12041         if (!(item instanceof Roo.Toolbar.Button)){
12042            item = new Roo.Toolbar.Button(item);
12043         }
12044         var td = document.createElement("td");
12045         this.tr.insertBefore(td, this.tr.childNodes[index]);
12046         item.render(td);
12047         this.items.insert(index, item);
12048         return item;
12049     },
12050     
12051     /**
12052      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12053      * @param {Object} config
12054      * @return {Roo.Toolbar.Item} The element's item
12055      */
12056     addDom : function(config, returnEl){
12057         var td = this.nextBlock();
12058         Roo.DomHelper.overwrite(td, config);
12059         var ti = new Roo.Toolbar.Item(td.firstChild);
12060         ti.render(td);
12061         this.items.add(ti);
12062         return ti;
12063     },
12064
12065     /**
12066      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12067      * @type Roo.util.MixedCollection  
12068      */
12069     fields : false,
12070     
12071     /**
12072      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12073      * Note: the field should not have been rendered yet. For a field that has already been
12074      * rendered, use {@link #addElement}.
12075      * @param {Roo.form.Field} field
12076      * @return {Roo.ToolbarItem}
12077      */
12078      
12079       
12080     addField : function(field) {
12081         if (!this.fields) {
12082             var autoId = 0;
12083             this.fields = new Roo.util.MixedCollection(false, function(o){
12084                 return o.id || ("item" + (++autoId));
12085             });
12086
12087         }
12088         
12089         var td = this.nextBlock();
12090         field.render(td);
12091         var ti = new Roo.Toolbar.Item(td.firstChild);
12092         ti.render(td);
12093         this.items.add(ti);
12094         this.fields.add(field);
12095         return ti;
12096     },
12097     /**
12098      * Hide the toolbar
12099      * @method hide
12100      */
12101      
12102       
12103     hide : function()
12104     {
12105         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12106         this.el.child('div').hide();
12107     },
12108     /**
12109      * Show the toolbar
12110      * @method show
12111      */
12112     show : function()
12113     {
12114         this.el.child('div').show();
12115     },
12116       
12117     // private
12118     nextBlock : function(){
12119         var td = document.createElement("td");
12120         this.tr.appendChild(td);
12121         return td;
12122     },
12123
12124     // private
12125     destroy : function(){
12126         if(this.items){ // rendered?
12127             Roo.destroy.apply(Roo, this.items.items);
12128         }
12129         if(this.fields){ // rendered?
12130             Roo.destroy.apply(Roo, this.fields.items);
12131         }
12132         Roo.Element.uncache(this.el, this.tr);
12133     }
12134 };
12135
12136 /**
12137  * @class Roo.Toolbar.Item
12138  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12139  * @constructor
12140  * Creates a new Item
12141  * @param {HTMLElement} el 
12142  */
12143 Roo.Toolbar.Item = function(el){
12144     var cfg = {};
12145     if (typeof (el.xtype) != 'undefined') {
12146         cfg = el;
12147         el = cfg.el;
12148     }
12149     
12150     this.el = Roo.getDom(el);
12151     this.id = Roo.id(this.el);
12152     this.hidden = false;
12153     
12154     this.addEvents({
12155          /**
12156              * @event render
12157              * Fires when the button is rendered
12158              * @param {Button} this
12159              */
12160         'render': true
12161     });
12162     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12163 };
12164 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12165 //Roo.Toolbar.Item.prototype = {
12166     
12167     /**
12168      * Get this item's HTML Element
12169      * @return {HTMLElement}
12170      */
12171     getEl : function(){
12172        return this.el;  
12173     },
12174
12175     // private
12176     render : function(td){
12177         
12178          this.td = td;
12179         td.appendChild(this.el);
12180         
12181         this.fireEvent('render', this);
12182     },
12183     
12184     /**
12185      * Removes and destroys this item.
12186      */
12187     destroy : function(){
12188         this.td.parentNode.removeChild(this.td);
12189     },
12190     
12191     /**
12192      * Shows this item.
12193      */
12194     show: function(){
12195         this.hidden = false;
12196         this.td.style.display = "";
12197     },
12198     
12199     /**
12200      * Hides this item.
12201      */
12202     hide: function(){
12203         this.hidden = true;
12204         this.td.style.display = "none";
12205     },
12206     
12207     /**
12208      * Convenience function for boolean show/hide.
12209      * @param {Boolean} visible true to show/false to hide
12210      */
12211     setVisible: function(visible){
12212         if(visible) {
12213             this.show();
12214         }else{
12215             this.hide();
12216         }
12217     },
12218     
12219     /**
12220      * Try to focus this item.
12221      */
12222     focus : function(){
12223         Roo.fly(this.el).focus();
12224     },
12225     
12226     /**
12227      * Disables this item.
12228      */
12229     disable : function(){
12230         Roo.fly(this.td).addClass("x-item-disabled");
12231         this.disabled = true;
12232         this.el.disabled = true;
12233     },
12234     
12235     /**
12236      * Enables this item.
12237      */
12238     enable : function(){
12239         Roo.fly(this.td).removeClass("x-item-disabled");
12240         this.disabled = false;
12241         this.el.disabled = false;
12242     }
12243 });
12244
12245
12246 /**
12247  * @class Roo.Toolbar.Separator
12248  * @extends Roo.Toolbar.Item
12249  * A simple toolbar separator class
12250  * @constructor
12251  * Creates a new Separator
12252  */
12253 Roo.Toolbar.Separator = function(cfg){
12254     
12255     var s = document.createElement("span");
12256     s.className = "ytb-sep";
12257     if (cfg) {
12258         cfg.el = s;
12259     }
12260     
12261     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12262 };
12263 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12264     enable:Roo.emptyFn,
12265     disable:Roo.emptyFn,
12266     focus:Roo.emptyFn
12267 });
12268
12269 /**
12270  * @class Roo.Toolbar.Spacer
12271  * @extends Roo.Toolbar.Item
12272  * A simple element that adds extra horizontal space to a toolbar.
12273  * @constructor
12274  * Creates a new Spacer
12275  */
12276 Roo.Toolbar.Spacer = function(cfg){
12277     var s = document.createElement("div");
12278     s.className = "ytb-spacer";
12279     if (cfg) {
12280         cfg.el = s;
12281     }
12282     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12283 };
12284 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12285     enable:Roo.emptyFn,
12286     disable:Roo.emptyFn,
12287     focus:Roo.emptyFn
12288 });
12289
12290 /**
12291  * @class Roo.Toolbar.Fill
12292  * @extends Roo.Toolbar.Spacer
12293  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12294  * @constructor
12295  * Creates a new Spacer
12296  */
12297 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12298     // private
12299     render : function(td){
12300         td.style.width = '100%';
12301         Roo.Toolbar.Fill.superclass.render.call(this, td);
12302     }
12303 });
12304
12305 /**
12306  * @class Roo.Toolbar.TextItem
12307  * @extends Roo.Toolbar.Item
12308  * A simple class that renders text directly into a toolbar.
12309  * @constructor
12310  * Creates a new TextItem
12311  * @param {String} text
12312  */
12313 Roo.Toolbar.TextItem = function(cfg){
12314     var  text = cfg || "";
12315     if (typeof(cfg) == 'object') {
12316         text = cfg.text || "";
12317     }  else {
12318         cfg = null;
12319     }
12320     var s = document.createElement("span");
12321     s.className = "ytb-text";
12322     s.innerHTML = text;
12323     if (cfg) {
12324         cfg.el  = s;
12325     }
12326     
12327     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
12328 };
12329 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12330     
12331      
12332     enable:Roo.emptyFn,
12333     disable:Roo.emptyFn,
12334     focus:Roo.emptyFn
12335 });
12336
12337 /**
12338  * @class Roo.Toolbar.Button
12339  * @extends Roo.Button
12340  * A button that renders into a toolbar.
12341  * @constructor
12342  * Creates a new Button
12343  * @param {Object} config A standard {@link Roo.Button} config object
12344  */
12345 Roo.Toolbar.Button = function(config){
12346     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12347 };
12348 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12349     render : function(td){
12350         this.td = td;
12351         Roo.Toolbar.Button.superclass.render.call(this, td);
12352     },
12353     
12354     /**
12355      * Removes and destroys this button
12356      */
12357     destroy : function(){
12358         Roo.Toolbar.Button.superclass.destroy.call(this);
12359         this.td.parentNode.removeChild(this.td);
12360     },
12361     
12362     /**
12363      * Shows this button
12364      */
12365     show: function(){
12366         this.hidden = false;
12367         this.td.style.display = "";
12368     },
12369     
12370     /**
12371      * Hides this button
12372      */
12373     hide: function(){
12374         this.hidden = true;
12375         this.td.style.display = "none";
12376     },
12377
12378     /**
12379      * Disables this item
12380      */
12381     disable : function(){
12382         Roo.fly(this.td).addClass("x-item-disabled");
12383         this.disabled = true;
12384     },
12385
12386     /**
12387      * Enables this item
12388      */
12389     enable : function(){
12390         Roo.fly(this.td).removeClass("x-item-disabled");
12391         this.disabled = false;
12392     }
12393 });
12394 // backwards compat
12395 Roo.ToolbarButton = Roo.Toolbar.Button;
12396
12397 /**
12398  * @class Roo.Toolbar.SplitButton
12399  * @extends Roo.SplitButton
12400  * A menu button that renders into a toolbar.
12401  * @constructor
12402  * Creates a new SplitButton
12403  * @param {Object} config A standard {@link Roo.SplitButton} config object
12404  */
12405 Roo.Toolbar.SplitButton = function(config){
12406     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12407 };
12408 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12409     render : function(td){
12410         this.td = td;
12411         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12412     },
12413     
12414     /**
12415      * Removes and destroys this button
12416      */
12417     destroy : function(){
12418         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12419         this.td.parentNode.removeChild(this.td);
12420     },
12421     
12422     /**
12423      * Shows this button
12424      */
12425     show: function(){
12426         this.hidden = false;
12427         this.td.style.display = "";
12428     },
12429     
12430     /**
12431      * Hides this button
12432      */
12433     hide: function(){
12434         this.hidden = true;
12435         this.td.style.display = "none";
12436     }
12437 });
12438
12439 // backwards compat
12440 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12441  * Based on:
12442  * Ext JS Library 1.1.1
12443  * Copyright(c) 2006-2007, Ext JS, LLC.
12444  *
12445  * Originally Released Under LGPL - original licence link has changed is not relivant.
12446  *
12447  * Fork - LGPL
12448  * <script type="text/javascript">
12449  */
12450  
12451 /**
12452  * @class Roo.PagingToolbar
12453  * @extends Roo.Toolbar
12454  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12455  * @constructor
12456  * Create a new PagingToolbar
12457  * @param {Object} config The config object
12458  */
12459 Roo.PagingToolbar = function(el, ds, config)
12460 {
12461     // old args format still supported... - xtype is prefered..
12462     if (typeof(el) == 'object' && el.xtype) {
12463         // created from xtype...
12464         config = el;
12465         ds = el.dataSource;
12466         el = config.container;
12467     }
12468     var items = [];
12469     if (config.items) {
12470         items = config.items;
12471         config.items = [];
12472     }
12473     
12474     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12475     this.ds = ds;
12476     this.cursor = 0;
12477     this.renderButtons(this.el);
12478     this.bind(ds);
12479     
12480     // supprot items array.
12481    
12482     Roo.each(items, function(e) {
12483         this.add(Roo.factory(e));
12484     },this);
12485     
12486 };
12487
12488 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12489     /**
12490      * @cfg {Roo.data.Store} dataSource
12491      * The underlying data store providing the paged data
12492      */
12493     /**
12494      * @cfg {String/HTMLElement/Element} container
12495      * container The id or element that will contain the toolbar
12496      */
12497     /**
12498      * @cfg {Boolean} displayInfo
12499      * True to display the displayMsg (defaults to false)
12500      */
12501     /**
12502      * @cfg {Number} pageSize
12503      * The number of records to display per page (defaults to 20)
12504      */
12505     pageSize: 20,
12506     /**
12507      * @cfg {String} displayMsg
12508      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12509      */
12510     displayMsg : 'Displaying {0} - {1} of {2}',
12511     /**
12512      * @cfg {String} emptyMsg
12513      * The message to display when no records are found (defaults to "No data to display")
12514      */
12515     emptyMsg : 'No data to display',
12516     /**
12517      * Customizable piece of the default paging text (defaults to "Page")
12518      * @type String
12519      */
12520     beforePageText : "Page",
12521     /**
12522      * Customizable piece of the default paging text (defaults to "of %0")
12523      * @type String
12524      */
12525     afterPageText : "of {0}",
12526     /**
12527      * Customizable piece of the default paging text (defaults to "First Page")
12528      * @type String
12529      */
12530     firstText : "First Page",
12531     /**
12532      * Customizable piece of the default paging text (defaults to "Previous Page")
12533      * @type String
12534      */
12535     prevText : "Previous Page",
12536     /**
12537      * Customizable piece of the default paging text (defaults to "Next Page")
12538      * @type String
12539      */
12540     nextText : "Next Page",
12541     /**
12542      * Customizable piece of the default paging text (defaults to "Last Page")
12543      * @type String
12544      */
12545     lastText : "Last Page",
12546     /**
12547      * Customizable piece of the default paging text (defaults to "Refresh")
12548      * @type String
12549      */
12550     refreshText : "Refresh",
12551
12552     // private
12553     renderButtons : function(el){
12554         Roo.PagingToolbar.superclass.render.call(this, el);
12555         this.first = this.addButton({
12556             tooltip: this.firstText,
12557             cls: "x-btn-icon x-grid-page-first",
12558             disabled: true,
12559             handler: this.onClick.createDelegate(this, ["first"])
12560         });
12561         this.prev = this.addButton({
12562             tooltip: this.prevText,
12563             cls: "x-btn-icon x-grid-page-prev",
12564             disabled: true,
12565             handler: this.onClick.createDelegate(this, ["prev"])
12566         });
12567         //this.addSeparator();
12568         this.add(this.beforePageText);
12569         this.field = Roo.get(this.addDom({
12570            tag: "input",
12571            type: "text",
12572            size: "3",
12573            value: "1",
12574            cls: "x-grid-page-number"
12575         }).el);
12576         this.field.on("keydown", this.onPagingKeydown, this);
12577         this.field.on("focus", function(){this.dom.select();});
12578         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12579         this.field.setHeight(18);
12580         //this.addSeparator();
12581         this.next = this.addButton({
12582             tooltip: this.nextText,
12583             cls: "x-btn-icon x-grid-page-next",
12584             disabled: true,
12585             handler: this.onClick.createDelegate(this, ["next"])
12586         });
12587         this.last = this.addButton({
12588             tooltip: this.lastText,
12589             cls: "x-btn-icon x-grid-page-last",
12590             disabled: true,
12591             handler: this.onClick.createDelegate(this, ["last"])
12592         });
12593         //this.addSeparator();
12594         this.loading = this.addButton({
12595             tooltip: this.refreshText,
12596             cls: "x-btn-icon x-grid-loading",
12597             handler: this.onClick.createDelegate(this, ["refresh"])
12598         });
12599
12600         if(this.displayInfo){
12601             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12602         }
12603     },
12604
12605     // private
12606     updateInfo : function(){
12607         if(this.displayEl){
12608             var count = this.ds.getCount();
12609             var msg = count == 0 ?
12610                 this.emptyMsg :
12611                 String.format(
12612                     this.displayMsg,
12613                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12614                 );
12615             this.displayEl.update(msg);
12616         }
12617     },
12618
12619     // private
12620     onLoad : function(ds, r, o){
12621        this.cursor = o.params ? o.params.start : 0;
12622        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12623
12624        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12625        this.field.dom.value = ap;
12626        this.first.setDisabled(ap == 1);
12627        this.prev.setDisabled(ap == 1);
12628        this.next.setDisabled(ap == ps);
12629        this.last.setDisabled(ap == ps);
12630        this.loading.enable();
12631        this.updateInfo();
12632     },
12633
12634     // private
12635     getPageData : function(){
12636         var total = this.ds.getTotalCount();
12637         return {
12638             total : total,
12639             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12640             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12641         };
12642     },
12643
12644     // private
12645     onLoadError : function(){
12646         this.loading.enable();
12647     },
12648
12649     // private
12650     onPagingKeydown : function(e){
12651         var k = e.getKey();
12652         var d = this.getPageData();
12653         if(k == e.RETURN){
12654             var v = this.field.dom.value, pageNum;
12655             if(!v || isNaN(pageNum = parseInt(v, 10))){
12656                 this.field.dom.value = d.activePage;
12657                 return;
12658             }
12659             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12660             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12661             e.stopEvent();
12662         }
12663         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))
12664         {
12665           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12666           this.field.dom.value = pageNum;
12667           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12668           e.stopEvent();
12669         }
12670         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12671         {
12672           var v = this.field.dom.value, pageNum; 
12673           var increment = (e.shiftKey) ? 10 : 1;
12674           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12675             increment *= -1;
12676           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12677             this.field.dom.value = d.activePage;
12678             return;
12679           }
12680           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12681           {
12682             this.field.dom.value = parseInt(v, 10) + increment;
12683             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12684             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12685           }
12686           e.stopEvent();
12687         }
12688     },
12689
12690     // private
12691     beforeLoad : function(){
12692         if(this.loading){
12693             this.loading.disable();
12694         }
12695     },
12696
12697     // private
12698     onClick : function(which){
12699         var ds = this.ds;
12700         switch(which){
12701             case "first":
12702                 ds.load({params:{start: 0, limit: this.pageSize}});
12703             break;
12704             case "prev":
12705                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12706             break;
12707             case "next":
12708                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12709             break;
12710             case "last":
12711                 var total = ds.getTotalCount();
12712                 var extra = total % this.pageSize;
12713                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12714                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12715             break;
12716             case "refresh":
12717                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12718             break;
12719         }
12720     },
12721
12722     /**
12723      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12724      * @param {Roo.data.Store} store The data store to unbind
12725      */
12726     unbind : function(ds){
12727         ds.un("beforeload", this.beforeLoad, this);
12728         ds.un("load", this.onLoad, this);
12729         ds.un("loadexception", this.onLoadError, this);
12730         ds.un("remove", this.updateInfo, this);
12731         ds.un("add", this.updateInfo, this);
12732         this.ds = undefined;
12733     },
12734
12735     /**
12736      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12737      * @param {Roo.data.Store} store The data store to bind
12738      */
12739     bind : function(ds){
12740         ds.on("beforeload", this.beforeLoad, this);
12741         ds.on("load", this.onLoad, this);
12742         ds.on("loadexception", this.onLoadError, this);
12743         ds.on("remove", this.updateInfo, this);
12744         ds.on("add", this.updateInfo, this);
12745         this.ds = ds;
12746     }
12747 });/*
12748  * Based on:
12749  * Ext JS Library 1.1.1
12750  * Copyright(c) 2006-2007, Ext JS, LLC.
12751  *
12752  * Originally Released Under LGPL - original licence link has changed is not relivant.
12753  *
12754  * Fork - LGPL
12755  * <script type="text/javascript">
12756  */
12757
12758 /**
12759  * @class Roo.Resizable
12760  * @extends Roo.util.Observable
12761  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12762  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12763  * 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
12764  * the element will be wrapped for you automatically.</p>
12765  * <p>Here is the list of valid resize handles:</p>
12766  * <pre>
12767 Value   Description
12768 ------  -------------------
12769  'n'     north
12770  's'     south
12771  'e'     east
12772  'w'     west
12773  'nw'    northwest
12774  'sw'    southwest
12775  'se'    southeast
12776  'ne'    northeast
12777  'hd'    horizontal drag
12778  'all'   all
12779 </pre>
12780  * <p>Here's an example showing the creation of a typical Resizable:</p>
12781  * <pre><code>
12782 var resizer = new Roo.Resizable("element-id", {
12783     handles: 'all',
12784     minWidth: 200,
12785     minHeight: 100,
12786     maxWidth: 500,
12787     maxHeight: 400,
12788     pinned: true
12789 });
12790 resizer.on("resize", myHandler);
12791 </code></pre>
12792  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12793  * resizer.east.setDisplayed(false);</p>
12794  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12795  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12796  * resize operation's new size (defaults to [0, 0])
12797  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12798  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12799  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12800  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12801  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12802  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12803  * @cfg {Number} width The width of the element in pixels (defaults to null)
12804  * @cfg {Number} height The height of the element in pixels (defaults to null)
12805  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12806  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12807  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12808  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12809  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12810  * in favor of the handles config option (defaults to false)
12811  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12812  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12813  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12814  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12815  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12816  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12817  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12818  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12819  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12820  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12821  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12822  * @constructor
12823  * Create a new resizable component
12824  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12825  * @param {Object} config configuration options
12826   */
12827 Roo.Resizable = function(el, config)
12828 {
12829     this.el = Roo.get(el);
12830
12831     if(config && config.wrap){
12832         config.resizeChild = this.el;
12833         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12834         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12835         this.el.setStyle("overflow", "hidden");
12836         this.el.setPositioning(config.resizeChild.getPositioning());
12837         config.resizeChild.clearPositioning();
12838         if(!config.width || !config.height){
12839             var csize = config.resizeChild.getSize();
12840             this.el.setSize(csize.width, csize.height);
12841         }
12842         if(config.pinned && !config.adjustments){
12843             config.adjustments = "auto";
12844         }
12845     }
12846
12847     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12848     this.proxy.unselectable();
12849     this.proxy.enableDisplayMode('block');
12850
12851     Roo.apply(this, config);
12852
12853     if(this.pinned){
12854         this.disableTrackOver = true;
12855         this.el.addClass("x-resizable-pinned");
12856     }
12857     // if the element isn't positioned, make it relative
12858     var position = this.el.getStyle("position");
12859     if(position != "absolute" && position != "fixed"){
12860         this.el.setStyle("position", "relative");
12861     }
12862     if(!this.handles){ // no handles passed, must be legacy style
12863         this.handles = 's,e,se';
12864         if(this.multiDirectional){
12865             this.handles += ',n,w';
12866         }
12867     }
12868     if(this.handles == "all"){
12869         this.handles = "n s e w ne nw se sw";
12870     }
12871     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12872     var ps = Roo.Resizable.positions;
12873     for(var i = 0, len = hs.length; i < len; i++){
12874         if(hs[i] && ps[hs[i]]){
12875             var pos = ps[hs[i]];
12876             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12877         }
12878     }
12879     // legacy
12880     this.corner = this.southeast;
12881     
12882     // updateBox = the box can move..
12883     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12884         this.updateBox = true;
12885     }
12886
12887     this.activeHandle = null;
12888
12889     if(this.resizeChild){
12890         if(typeof this.resizeChild == "boolean"){
12891             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12892         }else{
12893             this.resizeChild = Roo.get(this.resizeChild, true);
12894         }
12895     }
12896     
12897     if(this.adjustments == "auto"){
12898         var rc = this.resizeChild;
12899         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12900         if(rc && (hw || hn)){
12901             rc.position("relative");
12902             rc.setLeft(hw ? hw.el.getWidth() : 0);
12903             rc.setTop(hn ? hn.el.getHeight() : 0);
12904         }
12905         this.adjustments = [
12906             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12907             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12908         ];
12909     }
12910
12911     if(this.draggable){
12912         this.dd = this.dynamic ?
12913             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12914         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12915     }
12916
12917     // public events
12918     this.addEvents({
12919         /**
12920          * @event beforeresize
12921          * Fired before resize is allowed. Set enabled to false to cancel resize.
12922          * @param {Roo.Resizable} this
12923          * @param {Roo.EventObject} e The mousedown event
12924          */
12925         "beforeresize" : true,
12926         /**
12927          * @event resizing
12928          * Fired a resizing.
12929          * @param {Roo.Resizable} this
12930          * @param {Number} x The new x position
12931          * @param {Number} y The new y position
12932          * @param {Number} w The new w width
12933          * @param {Number} h The new h hight
12934          * @param {Roo.EventObject} e The mouseup event
12935          */
12936         "resizing" : true,
12937         /**
12938          * @event resize
12939          * Fired after a resize.
12940          * @param {Roo.Resizable} this
12941          * @param {Number} width The new width
12942          * @param {Number} height The new height
12943          * @param {Roo.EventObject} e The mouseup event
12944          */
12945         "resize" : true
12946     });
12947
12948     if(this.width !== null && this.height !== null){
12949         this.resizeTo(this.width, this.height);
12950     }else{
12951         this.updateChildSize();
12952     }
12953     if(Roo.isIE){
12954         this.el.dom.style.zoom = 1;
12955     }
12956     Roo.Resizable.superclass.constructor.call(this);
12957 };
12958
12959 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12960         resizeChild : false,
12961         adjustments : [0, 0],
12962         minWidth : 5,
12963         minHeight : 5,
12964         maxWidth : 10000,
12965         maxHeight : 10000,
12966         enabled : true,
12967         animate : false,
12968         duration : .35,
12969         dynamic : false,
12970         handles : false,
12971         multiDirectional : false,
12972         disableTrackOver : false,
12973         easing : 'easeOutStrong',
12974         widthIncrement : 0,
12975         heightIncrement : 0,
12976         pinned : false,
12977         width : null,
12978         height : null,
12979         preserveRatio : false,
12980         transparent: false,
12981         minX: 0,
12982         minY: 0,
12983         draggable: false,
12984
12985         /**
12986          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12987          */
12988         constrainTo: undefined,
12989         /**
12990          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12991          */
12992         resizeRegion: undefined,
12993
12994
12995     /**
12996      * Perform a manual resize
12997      * @param {Number} width
12998      * @param {Number} height
12999      */
13000     resizeTo : function(width, height){
13001         this.el.setSize(width, height);
13002         this.updateChildSize();
13003         this.fireEvent("resize", this, width, height, null);
13004     },
13005
13006     // private
13007     startSizing : function(e, handle){
13008         this.fireEvent("beforeresize", this, e);
13009         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13010
13011             if(!this.overlay){
13012                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13013                 this.overlay.unselectable();
13014                 this.overlay.enableDisplayMode("block");
13015                 this.overlay.on("mousemove", this.onMouseMove, this);
13016                 this.overlay.on("mouseup", this.onMouseUp, this);
13017             }
13018             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13019
13020             this.resizing = true;
13021             this.startBox = this.el.getBox();
13022             this.startPoint = e.getXY();
13023             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13024                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13025
13026             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13027             this.overlay.show();
13028
13029             if(this.constrainTo) {
13030                 var ct = Roo.get(this.constrainTo);
13031                 this.resizeRegion = ct.getRegion().adjust(
13032                     ct.getFrameWidth('t'),
13033                     ct.getFrameWidth('l'),
13034                     -ct.getFrameWidth('b'),
13035                     -ct.getFrameWidth('r')
13036                 );
13037             }
13038
13039             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13040             this.proxy.show();
13041             this.proxy.setBox(this.startBox);
13042             if(!this.dynamic){
13043                 this.proxy.setStyle('visibility', 'visible');
13044             }
13045         }
13046     },
13047
13048     // private
13049     onMouseDown : function(handle, e){
13050         if(this.enabled){
13051             e.stopEvent();
13052             this.activeHandle = handle;
13053             this.startSizing(e, handle);
13054         }
13055     },
13056
13057     // private
13058     onMouseUp : function(e){
13059         var size = this.resizeElement();
13060         this.resizing = false;
13061         this.handleOut();
13062         this.overlay.hide();
13063         this.proxy.hide();
13064         this.fireEvent("resize", this, size.width, size.height, e);
13065     },
13066
13067     // private
13068     updateChildSize : function(){
13069         
13070         if(this.resizeChild){
13071             var el = this.el;
13072             var child = this.resizeChild;
13073             var adj = this.adjustments;
13074             if(el.dom.offsetWidth){
13075                 var b = el.getSize(true);
13076                 child.setSize(b.width+adj[0], b.height+adj[1]);
13077             }
13078             // Second call here for IE
13079             // The first call enables instant resizing and
13080             // the second call corrects scroll bars if they
13081             // exist
13082             if(Roo.isIE){
13083                 setTimeout(function(){
13084                     if(el.dom.offsetWidth){
13085                         var b = el.getSize(true);
13086                         child.setSize(b.width+adj[0], b.height+adj[1]);
13087                     }
13088                 }, 10);
13089             }
13090         }
13091     },
13092
13093     // private
13094     snap : function(value, inc, min){
13095         if(!inc || !value) return value;
13096         var newValue = value;
13097         var m = value % inc;
13098         if(m > 0){
13099             if(m > (inc/2)){
13100                 newValue = value + (inc-m);
13101             }else{
13102                 newValue = value - m;
13103             }
13104         }
13105         return Math.max(min, newValue);
13106     },
13107
13108     // private
13109     resizeElement : function(){
13110         var box = this.proxy.getBox();
13111         if(this.updateBox){
13112             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13113         }else{
13114             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13115         }
13116         this.updateChildSize();
13117         if(!this.dynamic){
13118             this.proxy.hide();
13119         }
13120         return box;
13121     },
13122
13123     // private
13124     constrain : function(v, diff, m, mx){
13125         if(v - diff < m){
13126             diff = v - m;
13127         }else if(v - diff > mx){
13128             diff = mx - v;
13129         }
13130         return diff;
13131     },
13132
13133     // private
13134     onMouseMove : function(e){
13135         
13136         if(this.enabled){
13137             try{// try catch so if something goes wrong the user doesn't get hung
13138
13139             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13140                 return;
13141             }
13142
13143             //var curXY = this.startPoint;
13144             var curSize = this.curSize || this.startBox;
13145             var x = this.startBox.x, y = this.startBox.y;
13146             var ox = x, oy = y;
13147             var w = curSize.width, h = curSize.height;
13148             var ow = w, oh = h;
13149             var mw = this.minWidth, mh = this.minHeight;
13150             var mxw = this.maxWidth, mxh = this.maxHeight;
13151             var wi = this.widthIncrement;
13152             var hi = this.heightIncrement;
13153
13154             var eventXY = e.getXY();
13155             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13156             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13157
13158             var pos = this.activeHandle.position;
13159
13160             switch(pos){
13161                 case "east":
13162                     w += diffX;
13163                     w = Math.min(Math.max(mw, w), mxw);
13164                     break;
13165              
13166                 case "south":
13167                     h += diffY;
13168                     h = Math.min(Math.max(mh, h), mxh);
13169                     break;
13170                 case "southeast":
13171                     w += diffX;
13172                     h += diffY;
13173                     w = Math.min(Math.max(mw, w), mxw);
13174                     h = Math.min(Math.max(mh, h), mxh);
13175                     break;
13176                 case "north":
13177                     diffY = this.constrain(h, diffY, mh, mxh);
13178                     y += diffY;
13179                     h -= diffY;
13180                     break;
13181                 case "hdrag":
13182                     
13183                     if (wi) {
13184                         var adiffX = Math.abs(diffX);
13185                         var sub = (adiffX % wi); // how much 
13186                         if (sub > (wi/2)) { // far enough to snap
13187                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13188                         } else {
13189                             // remove difference.. 
13190                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13191                         }
13192                     }
13193                     x += diffX;
13194                     x = Math.max(this.minX, x);
13195                     break;
13196                 case "west":
13197                     diffX = this.constrain(w, diffX, mw, mxw);
13198                     x += diffX;
13199                     w -= diffX;
13200                     break;
13201                 case "northeast":
13202                     w += diffX;
13203                     w = Math.min(Math.max(mw, w), mxw);
13204                     diffY = this.constrain(h, diffY, mh, mxh);
13205                     y += diffY;
13206                     h -= diffY;
13207                     break;
13208                 case "northwest":
13209                     diffX = this.constrain(w, diffX, mw, mxw);
13210                     diffY = this.constrain(h, diffY, mh, mxh);
13211                     y += diffY;
13212                     h -= diffY;
13213                     x += diffX;
13214                     w -= diffX;
13215                     break;
13216                case "southwest":
13217                     diffX = this.constrain(w, diffX, mw, mxw);
13218                     h += diffY;
13219                     h = Math.min(Math.max(mh, h), mxh);
13220                     x += diffX;
13221                     w -= diffX;
13222                     break;
13223             }
13224
13225             var sw = this.snap(w, wi, mw);
13226             var sh = this.snap(h, hi, mh);
13227             if(sw != w || sh != h){
13228                 switch(pos){
13229                     case "northeast":
13230                         y -= sh - h;
13231                     break;
13232                     case "north":
13233                         y -= sh - h;
13234                         break;
13235                     case "southwest":
13236                         x -= sw - w;
13237                     break;
13238                     case "west":
13239                         x -= sw - w;
13240                         break;
13241                     case "northwest":
13242                         x -= sw - w;
13243                         y -= sh - h;
13244                     break;
13245                 }
13246                 w = sw;
13247                 h = sh;
13248             }
13249
13250             if(this.preserveRatio){
13251                 switch(pos){
13252                     case "southeast":
13253                     case "east":
13254                         h = oh * (w/ow);
13255                         h = Math.min(Math.max(mh, h), mxh);
13256                         w = ow * (h/oh);
13257                        break;
13258                     case "south":
13259                         w = ow * (h/oh);
13260                         w = Math.min(Math.max(mw, w), mxw);
13261                         h = oh * (w/ow);
13262                         break;
13263                     case "northeast":
13264                         w = ow * (h/oh);
13265                         w = Math.min(Math.max(mw, w), mxw);
13266                         h = oh * (w/ow);
13267                     break;
13268                     case "north":
13269                         var tw = w;
13270                         w = ow * (h/oh);
13271                         w = Math.min(Math.max(mw, w), mxw);
13272                         h = oh * (w/ow);
13273                         x += (tw - w) / 2;
13274                         break;
13275                     case "southwest":
13276                         h = oh * (w/ow);
13277                         h = Math.min(Math.max(mh, h), mxh);
13278                         var tw = w;
13279                         w = ow * (h/oh);
13280                         x += tw - w;
13281                         break;
13282                     case "west":
13283                         var th = h;
13284                         h = oh * (w/ow);
13285                         h = Math.min(Math.max(mh, h), mxh);
13286                         y += (th - h) / 2;
13287                         var tw = w;
13288                         w = ow * (h/oh);
13289                         x += tw - w;
13290                        break;
13291                     case "northwest":
13292                         var tw = w;
13293                         var th = h;
13294                         h = oh * (w/ow);
13295                         h = Math.min(Math.max(mh, h), mxh);
13296                         w = ow * (h/oh);
13297                         y += th - h;
13298                         x += tw - w;
13299                        break;
13300
13301                 }
13302             }
13303             if (pos == 'hdrag') {
13304                 w = ow;
13305             }
13306             this.proxy.setBounds(x, y, w, h);
13307             if(this.dynamic){
13308                 this.resizeElement();
13309             }
13310             }catch(e){}
13311         }
13312         this.fireEvent("resizing", this, x, y, w, h, e);
13313     },
13314
13315     // private
13316     handleOver : function(){
13317         if(this.enabled){
13318             this.el.addClass("x-resizable-over");
13319         }
13320     },
13321
13322     // private
13323     handleOut : function(){
13324         if(!this.resizing){
13325             this.el.removeClass("x-resizable-over");
13326         }
13327     },
13328
13329     /**
13330      * Returns the element this component is bound to.
13331      * @return {Roo.Element}
13332      */
13333     getEl : function(){
13334         return this.el;
13335     },
13336
13337     /**
13338      * Returns the resizeChild element (or null).
13339      * @return {Roo.Element}
13340      */
13341     getResizeChild : function(){
13342         return this.resizeChild;
13343     },
13344     groupHandler : function()
13345     {
13346         
13347     },
13348     /**
13349      * Destroys this resizable. If the element was wrapped and
13350      * removeEl is not true then the element remains.
13351      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13352      */
13353     destroy : function(removeEl){
13354         this.proxy.remove();
13355         if(this.overlay){
13356             this.overlay.removeAllListeners();
13357             this.overlay.remove();
13358         }
13359         var ps = Roo.Resizable.positions;
13360         for(var k in ps){
13361             if(typeof ps[k] != "function" && this[ps[k]]){
13362                 var h = this[ps[k]];
13363                 h.el.removeAllListeners();
13364                 h.el.remove();
13365             }
13366         }
13367         if(removeEl){
13368             this.el.update("");
13369             this.el.remove();
13370         }
13371     }
13372 });
13373
13374 // private
13375 // hash to map config positions to true positions
13376 Roo.Resizable.positions = {
13377     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13378     hd: "hdrag"
13379 };
13380
13381 // private
13382 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13383     if(!this.tpl){
13384         // only initialize the template if resizable is used
13385         var tpl = Roo.DomHelper.createTemplate(
13386             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13387         );
13388         tpl.compile();
13389         Roo.Resizable.Handle.prototype.tpl = tpl;
13390     }
13391     this.position = pos;
13392     this.rz = rz;
13393     // show north drag fro topdra
13394     var handlepos = pos == 'hdrag' ? 'north' : pos;
13395     
13396     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13397     if (pos == 'hdrag') {
13398         this.el.setStyle('cursor', 'pointer');
13399     }
13400     this.el.unselectable();
13401     if(transparent){
13402         this.el.setOpacity(0);
13403     }
13404     this.el.on("mousedown", this.onMouseDown, this);
13405     if(!disableTrackOver){
13406         this.el.on("mouseover", this.onMouseOver, this);
13407         this.el.on("mouseout", this.onMouseOut, this);
13408     }
13409 };
13410
13411 // private
13412 Roo.Resizable.Handle.prototype = {
13413     afterResize : function(rz){
13414         Roo.log('after?');
13415         // do nothing
13416     },
13417     // private
13418     onMouseDown : function(e){
13419         this.rz.onMouseDown(this, e);
13420     },
13421     // private
13422     onMouseOver : function(e){
13423         this.rz.handleOver(this, e);
13424     },
13425     // private
13426     onMouseOut : function(e){
13427         this.rz.handleOut(this, e);
13428     }
13429 };/*
13430  * Based on:
13431  * Ext JS Library 1.1.1
13432  * Copyright(c) 2006-2007, Ext JS, LLC.
13433  *
13434  * Originally Released Under LGPL - original licence link has changed is not relivant.
13435  *
13436  * Fork - LGPL
13437  * <script type="text/javascript">
13438  */
13439
13440 /**
13441  * @class Roo.Editor
13442  * @extends Roo.Component
13443  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13444  * @constructor
13445  * Create a new Editor
13446  * @param {Roo.form.Field} field The Field object (or descendant)
13447  * @param {Object} config The config object
13448  */
13449 Roo.Editor = function(field, config){
13450     Roo.Editor.superclass.constructor.call(this, config);
13451     this.field = field;
13452     this.addEvents({
13453         /**
13454              * @event beforestartedit
13455              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13456              * false from the handler of this event.
13457              * @param {Editor} this
13458              * @param {Roo.Element} boundEl The underlying element bound to this editor
13459              * @param {Mixed} value The field value being set
13460              */
13461         "beforestartedit" : true,
13462         /**
13463              * @event startedit
13464              * Fires when this editor is displayed
13465              * @param {Roo.Element} boundEl The underlying element bound to this editor
13466              * @param {Mixed} value The starting field value
13467              */
13468         "startedit" : true,
13469         /**
13470              * @event beforecomplete
13471              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13472              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13473              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13474              * event will not fire since no edit actually occurred.
13475              * @param {Editor} this
13476              * @param {Mixed} value The current field value
13477              * @param {Mixed} startValue The original field value
13478              */
13479         "beforecomplete" : true,
13480         /**
13481              * @event complete
13482              * Fires after editing is complete and any changed value has been written to the underlying field.
13483              * @param {Editor} this
13484              * @param {Mixed} value The current field value
13485              * @param {Mixed} startValue The original field value
13486              */
13487         "complete" : true,
13488         /**
13489          * @event specialkey
13490          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13491          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13492          * @param {Roo.form.Field} this
13493          * @param {Roo.EventObject} e The event object
13494          */
13495         "specialkey" : true
13496     });
13497 };
13498
13499 Roo.extend(Roo.Editor, Roo.Component, {
13500     /**
13501      * @cfg {Boolean/String} autosize
13502      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13503      * or "height" to adopt the height only (defaults to false)
13504      */
13505     /**
13506      * @cfg {Boolean} revertInvalid
13507      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13508      * validation fails (defaults to true)
13509      */
13510     /**
13511      * @cfg {Boolean} ignoreNoChange
13512      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13513      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13514      * will never be ignored.
13515      */
13516     /**
13517      * @cfg {Boolean} hideEl
13518      * False to keep the bound element visible while the editor is displayed (defaults to true)
13519      */
13520     /**
13521      * @cfg {Mixed} value
13522      * The data value of the underlying field (defaults to "")
13523      */
13524     value : "",
13525     /**
13526      * @cfg {String} alignment
13527      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13528      */
13529     alignment: "c-c?",
13530     /**
13531      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13532      * for bottom-right shadow (defaults to "frame")
13533      */
13534     shadow : "frame",
13535     /**
13536      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13537      */
13538     constrain : false,
13539     /**
13540      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13541      */
13542     completeOnEnter : false,
13543     /**
13544      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13545      */
13546     cancelOnEsc : false,
13547     /**
13548      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13549      */
13550     updateEl : false,
13551
13552     // private
13553     onRender : function(ct, position){
13554         this.el = new Roo.Layer({
13555             shadow: this.shadow,
13556             cls: "x-editor",
13557             parentEl : ct,
13558             shim : this.shim,
13559             shadowOffset:4,
13560             id: this.id,
13561             constrain: this.constrain
13562         });
13563         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13564         if(this.field.msgTarget != 'title'){
13565             this.field.msgTarget = 'qtip';
13566         }
13567         this.field.render(this.el);
13568         if(Roo.isGecko){
13569             this.field.el.dom.setAttribute('autocomplete', 'off');
13570         }
13571         this.field.on("specialkey", this.onSpecialKey, this);
13572         if(this.swallowKeys){
13573             this.field.el.swallowEvent(['keydown','keypress']);
13574         }
13575         this.field.show();
13576         this.field.on("blur", this.onBlur, this);
13577         if(this.field.grow){
13578             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13579         }
13580     },
13581
13582     onSpecialKey : function(field, e)
13583     {
13584         //Roo.log('editor onSpecialKey');
13585         if(this.completeOnEnter && e.getKey() == e.ENTER){
13586             e.stopEvent();
13587             this.completeEdit();
13588             return;
13589         }
13590         // do not fire special key otherwise it might hide close the editor...
13591         if(e.getKey() == e.ENTER){    
13592             return;
13593         }
13594         if(this.cancelOnEsc && e.getKey() == e.ESC){
13595             this.cancelEdit();
13596             return;
13597         } 
13598         this.fireEvent('specialkey', field, e);
13599     
13600     },
13601
13602     /**
13603      * Starts the editing process and shows the editor.
13604      * @param {String/HTMLElement/Element} el The element to edit
13605      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13606       * to the innerHTML of el.
13607      */
13608     startEdit : function(el, value){
13609         if(this.editing){
13610             this.completeEdit();
13611         }
13612         this.boundEl = Roo.get(el);
13613         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13614         if(!this.rendered){
13615             this.render(this.parentEl || document.body);
13616         }
13617         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13618             return;
13619         }
13620         this.startValue = v;
13621         this.field.setValue(v);
13622         if(this.autoSize){
13623             var sz = this.boundEl.getSize();
13624             switch(this.autoSize){
13625                 case "width":
13626                 this.setSize(sz.width,  "");
13627                 break;
13628                 case "height":
13629                 this.setSize("",  sz.height);
13630                 break;
13631                 default:
13632                 this.setSize(sz.width,  sz.height);
13633             }
13634         }
13635         this.el.alignTo(this.boundEl, this.alignment);
13636         this.editing = true;
13637         if(Roo.QuickTips){
13638             Roo.QuickTips.disable();
13639         }
13640         this.show();
13641     },
13642
13643     /**
13644      * Sets the height and width of this editor.
13645      * @param {Number} width The new width
13646      * @param {Number} height The new height
13647      */
13648     setSize : function(w, h){
13649         this.field.setSize(w, h);
13650         if(this.el){
13651             this.el.sync();
13652         }
13653     },
13654
13655     /**
13656      * Realigns the editor to the bound field based on the current alignment config value.
13657      */
13658     realign : function(){
13659         this.el.alignTo(this.boundEl, this.alignment);
13660     },
13661
13662     /**
13663      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13664      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13665      */
13666     completeEdit : function(remainVisible){
13667         if(!this.editing){
13668             return;
13669         }
13670         var v = this.getValue();
13671         if(this.revertInvalid !== false && !this.field.isValid()){
13672             v = this.startValue;
13673             this.cancelEdit(true);
13674         }
13675         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13676             this.editing = false;
13677             this.hide();
13678             return;
13679         }
13680         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13681             this.editing = false;
13682             if(this.updateEl && this.boundEl){
13683                 this.boundEl.update(v);
13684             }
13685             if(remainVisible !== true){
13686                 this.hide();
13687             }
13688             this.fireEvent("complete", this, v, this.startValue);
13689         }
13690     },
13691
13692     // private
13693     onShow : function(){
13694         this.el.show();
13695         if(this.hideEl !== false){
13696             this.boundEl.hide();
13697         }
13698         this.field.show();
13699         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13700             this.fixIEFocus = true;
13701             this.deferredFocus.defer(50, this);
13702         }else{
13703             this.field.focus();
13704         }
13705         this.fireEvent("startedit", this.boundEl, this.startValue);
13706     },
13707
13708     deferredFocus : function(){
13709         if(this.editing){
13710             this.field.focus();
13711         }
13712     },
13713
13714     /**
13715      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13716      * reverted to the original starting value.
13717      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13718      * cancel (defaults to false)
13719      */
13720     cancelEdit : function(remainVisible){
13721         if(this.editing){
13722             this.setValue(this.startValue);
13723             if(remainVisible !== true){
13724                 this.hide();
13725             }
13726         }
13727     },
13728
13729     // private
13730     onBlur : function(){
13731         if(this.allowBlur !== true && this.editing){
13732             this.completeEdit();
13733         }
13734     },
13735
13736     // private
13737     onHide : function(){
13738         if(this.editing){
13739             this.completeEdit();
13740             return;
13741         }
13742         this.field.blur();
13743         if(this.field.collapse){
13744             this.field.collapse();
13745         }
13746         this.el.hide();
13747         if(this.hideEl !== false){
13748             this.boundEl.show();
13749         }
13750         if(Roo.QuickTips){
13751             Roo.QuickTips.enable();
13752         }
13753     },
13754
13755     /**
13756      * Sets the data value of the editor
13757      * @param {Mixed} value Any valid value supported by the underlying field
13758      */
13759     setValue : function(v){
13760         this.field.setValue(v);
13761     },
13762
13763     /**
13764      * Gets the data value of the editor
13765      * @return {Mixed} The data value
13766      */
13767     getValue : function(){
13768         return this.field.getValue();
13769     }
13770 });/*
13771  * Based on:
13772  * Ext JS Library 1.1.1
13773  * Copyright(c) 2006-2007, Ext JS, LLC.
13774  *
13775  * Originally Released Under LGPL - original licence link has changed is not relivant.
13776  *
13777  * Fork - LGPL
13778  * <script type="text/javascript">
13779  */
13780  
13781 /**
13782  * @class Roo.BasicDialog
13783  * @extends Roo.util.Observable
13784  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13785  * <pre><code>
13786 var dlg = new Roo.BasicDialog("my-dlg", {
13787     height: 200,
13788     width: 300,
13789     minHeight: 100,
13790     minWidth: 150,
13791     modal: true,
13792     proxyDrag: true,
13793     shadow: true
13794 });
13795 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13796 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13797 dlg.addButton('Cancel', dlg.hide, dlg);
13798 dlg.show();
13799 </code></pre>
13800   <b>A Dialog should always be a direct child of the body element.</b>
13801  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13802  * @cfg {String} title Default text to display in the title bar (defaults to null)
13803  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13804  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13805  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13806  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13807  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13808  * (defaults to null with no animation)
13809  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13810  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13811  * property for valid values (defaults to 'all')
13812  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13813  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13814  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13815  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13816  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13817  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13818  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13819  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13820  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13821  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13822  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13823  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13824  * draggable = true (defaults to false)
13825  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13826  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13827  * shadow (defaults to false)
13828  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13829  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13830  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13831  * @cfg {Array} buttons Array of buttons
13832  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13833  * @constructor
13834  * Create a new BasicDialog.
13835  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13836  * @param {Object} config Configuration options
13837  */
13838 Roo.BasicDialog = function(el, config){
13839     this.el = Roo.get(el);
13840     var dh = Roo.DomHelper;
13841     if(!this.el && config && config.autoCreate){
13842         if(typeof config.autoCreate == "object"){
13843             if(!config.autoCreate.id){
13844                 config.autoCreate.id = el;
13845             }
13846             this.el = dh.append(document.body,
13847                         config.autoCreate, true);
13848         }else{
13849             this.el = dh.append(document.body,
13850                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13851         }
13852     }
13853     el = this.el;
13854     el.setDisplayed(true);
13855     el.hide = this.hideAction;
13856     this.id = el.id;
13857     el.addClass("x-dlg");
13858
13859     Roo.apply(this, config);
13860
13861     this.proxy = el.createProxy("x-dlg-proxy");
13862     this.proxy.hide = this.hideAction;
13863     this.proxy.setOpacity(.5);
13864     this.proxy.hide();
13865
13866     if(config.width){
13867         el.setWidth(config.width);
13868     }
13869     if(config.height){
13870         el.setHeight(config.height);
13871     }
13872     this.size = el.getSize();
13873     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13874         this.xy = [config.x,config.y];
13875     }else{
13876         this.xy = el.getCenterXY(true);
13877     }
13878     /** The header element @type Roo.Element */
13879     this.header = el.child("> .x-dlg-hd");
13880     /** The body element @type Roo.Element */
13881     this.body = el.child("> .x-dlg-bd");
13882     /** The footer element @type Roo.Element */
13883     this.footer = el.child("> .x-dlg-ft");
13884
13885     if(!this.header){
13886         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13887     }
13888     if(!this.body){
13889         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13890     }
13891
13892     this.header.unselectable();
13893     if(this.title){
13894         this.header.update(this.title);
13895     }
13896     // this element allows the dialog to be focused for keyboard event
13897     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13898     this.focusEl.swallowEvent("click", true);
13899
13900     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13901
13902     // wrap the body and footer for special rendering
13903     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13904     if(this.footer){
13905         this.bwrap.dom.appendChild(this.footer.dom);
13906     }
13907
13908     this.bg = this.el.createChild({
13909         tag: "div", cls:"x-dlg-bg",
13910         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13911     });
13912     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13913
13914
13915     if(this.autoScroll !== false && !this.autoTabs){
13916         this.body.setStyle("overflow", "auto");
13917     }
13918
13919     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13920
13921     if(this.closable !== false){
13922         this.el.addClass("x-dlg-closable");
13923         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13924         this.close.on("click", this.closeClick, this);
13925         this.close.addClassOnOver("x-dlg-close-over");
13926     }
13927     if(this.collapsible !== false){
13928         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13929         this.collapseBtn.on("click", this.collapseClick, this);
13930         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13931         this.header.on("dblclick", this.collapseClick, this);
13932     }
13933     if(this.resizable !== false){
13934         this.el.addClass("x-dlg-resizable");
13935         this.resizer = new Roo.Resizable(el, {
13936             minWidth: this.minWidth || 80,
13937             minHeight:this.minHeight || 80,
13938             handles: this.resizeHandles || "all",
13939             pinned: true
13940         });
13941         this.resizer.on("beforeresize", this.beforeResize, this);
13942         this.resizer.on("resize", this.onResize, this);
13943     }
13944     if(this.draggable !== false){
13945         el.addClass("x-dlg-draggable");
13946         if (!this.proxyDrag) {
13947             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13948         }
13949         else {
13950             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13951         }
13952         dd.setHandleElId(this.header.id);
13953         dd.endDrag = this.endMove.createDelegate(this);
13954         dd.startDrag = this.startMove.createDelegate(this);
13955         dd.onDrag = this.onDrag.createDelegate(this);
13956         dd.scroll = false;
13957         this.dd = dd;
13958     }
13959     if(this.modal){
13960         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13961         this.mask.enableDisplayMode("block");
13962         this.mask.hide();
13963         this.el.addClass("x-dlg-modal");
13964     }
13965     if(this.shadow){
13966         this.shadow = new Roo.Shadow({
13967             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13968             offset : this.shadowOffset
13969         });
13970     }else{
13971         this.shadowOffset = 0;
13972     }
13973     if(Roo.useShims && this.shim !== false){
13974         this.shim = this.el.createShim();
13975         this.shim.hide = this.hideAction;
13976         this.shim.hide();
13977     }else{
13978         this.shim = false;
13979     }
13980     if(this.autoTabs){
13981         this.initTabs();
13982     }
13983     if (this.buttons) { 
13984         var bts= this.buttons;
13985         this.buttons = [];
13986         Roo.each(bts, function(b) {
13987             this.addButton(b);
13988         }, this);
13989     }
13990     
13991     
13992     this.addEvents({
13993         /**
13994          * @event keydown
13995          * Fires when a key is pressed
13996          * @param {Roo.BasicDialog} this
13997          * @param {Roo.EventObject} e
13998          */
13999         "keydown" : true,
14000         /**
14001          * @event move
14002          * Fires when this dialog is moved by the user.
14003          * @param {Roo.BasicDialog} this
14004          * @param {Number} x The new page X
14005          * @param {Number} y The new page Y
14006          */
14007         "move" : true,
14008         /**
14009          * @event resize
14010          * Fires when this dialog is resized by the user.
14011          * @param {Roo.BasicDialog} this
14012          * @param {Number} width The new width
14013          * @param {Number} height The new height
14014          */
14015         "resize" : true,
14016         /**
14017          * @event beforehide
14018          * Fires before this dialog is hidden.
14019          * @param {Roo.BasicDialog} this
14020          */
14021         "beforehide" : true,
14022         /**
14023          * @event hide
14024          * Fires when this dialog is hidden.
14025          * @param {Roo.BasicDialog} this
14026          */
14027         "hide" : true,
14028         /**
14029          * @event beforeshow
14030          * Fires before this dialog is shown.
14031          * @param {Roo.BasicDialog} this
14032          */
14033         "beforeshow" : true,
14034         /**
14035          * @event show
14036          * Fires when this dialog is shown.
14037          * @param {Roo.BasicDialog} this
14038          */
14039         "show" : true
14040     });
14041     el.on("keydown", this.onKeyDown, this);
14042     el.on("mousedown", this.toFront, this);
14043     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14044     this.el.hide();
14045     Roo.DialogManager.register(this);
14046     Roo.BasicDialog.superclass.constructor.call(this);
14047 };
14048
14049 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14050     shadowOffset: Roo.isIE ? 6 : 5,
14051     minHeight: 80,
14052     minWidth: 200,
14053     minButtonWidth: 75,
14054     defaultButton: null,
14055     buttonAlign: "right",
14056     tabTag: 'div',
14057     firstShow: true,
14058
14059     /**
14060      * Sets the dialog title text
14061      * @param {String} text The title text to display
14062      * @return {Roo.BasicDialog} this
14063      */
14064     setTitle : function(text){
14065         this.header.update(text);
14066         return this;
14067     },
14068
14069     // private
14070     closeClick : function(){
14071         this.hide();
14072     },
14073
14074     // private
14075     collapseClick : function(){
14076         this[this.collapsed ? "expand" : "collapse"]();
14077     },
14078
14079     /**
14080      * Collapses the dialog to its minimized state (only the title bar is visible).
14081      * Equivalent to the user clicking the collapse dialog button.
14082      */
14083     collapse : function(){
14084         if(!this.collapsed){
14085             this.collapsed = true;
14086             this.el.addClass("x-dlg-collapsed");
14087             this.restoreHeight = this.el.getHeight();
14088             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14089         }
14090     },
14091
14092     /**
14093      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14094      * clicking the expand dialog button.
14095      */
14096     expand : function(){
14097         if(this.collapsed){
14098             this.collapsed = false;
14099             this.el.removeClass("x-dlg-collapsed");
14100             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14101         }
14102     },
14103
14104     /**
14105      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14106      * @return {Roo.TabPanel} The tabs component
14107      */
14108     initTabs : function(){
14109         var tabs = this.getTabs();
14110         while(tabs.getTab(0)){
14111             tabs.removeTab(0);
14112         }
14113         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14114             var dom = el.dom;
14115             tabs.addTab(Roo.id(dom), dom.title);
14116             dom.title = "";
14117         });
14118         tabs.activate(0);
14119         return tabs;
14120     },
14121
14122     // private
14123     beforeResize : function(){
14124         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14125     },
14126
14127     // private
14128     onResize : function(){
14129         this.refreshSize();
14130         this.syncBodyHeight();
14131         this.adjustAssets();
14132         this.focus();
14133         this.fireEvent("resize", this, this.size.width, this.size.height);
14134     },
14135
14136     // private
14137     onKeyDown : function(e){
14138         if(this.isVisible()){
14139             this.fireEvent("keydown", this, e);
14140         }
14141     },
14142
14143     /**
14144      * Resizes the dialog.
14145      * @param {Number} width
14146      * @param {Number} height
14147      * @return {Roo.BasicDialog} this
14148      */
14149     resizeTo : function(width, height){
14150         this.el.setSize(width, height);
14151         this.size = {width: width, height: height};
14152         this.syncBodyHeight();
14153         if(this.fixedcenter){
14154             this.center();
14155         }
14156         if(this.isVisible()){
14157             this.constrainXY();
14158             this.adjustAssets();
14159         }
14160         this.fireEvent("resize", this, width, height);
14161         return this;
14162     },
14163
14164
14165     /**
14166      * Resizes the dialog to fit the specified content size.
14167      * @param {Number} width
14168      * @param {Number} height
14169      * @return {Roo.BasicDialog} this
14170      */
14171     setContentSize : function(w, h){
14172         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14173         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14174         //if(!this.el.isBorderBox()){
14175             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14176             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14177         //}
14178         if(this.tabs){
14179             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14180             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14181         }
14182         this.resizeTo(w, h);
14183         return this;
14184     },
14185
14186     /**
14187      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14188      * executed in response to a particular key being pressed while the dialog is active.
14189      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14190      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14191      * @param {Function} fn The function to call
14192      * @param {Object} scope (optional) The scope of the function
14193      * @return {Roo.BasicDialog} this
14194      */
14195     addKeyListener : function(key, fn, scope){
14196         var keyCode, shift, ctrl, alt;
14197         if(typeof key == "object" && !(key instanceof Array)){
14198             keyCode = key["key"];
14199             shift = key["shift"];
14200             ctrl = key["ctrl"];
14201             alt = key["alt"];
14202         }else{
14203             keyCode = key;
14204         }
14205         var handler = function(dlg, e){
14206             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14207                 var k = e.getKey();
14208                 if(keyCode instanceof Array){
14209                     for(var i = 0, len = keyCode.length; i < len; i++){
14210                         if(keyCode[i] == k){
14211                           fn.call(scope || window, dlg, k, e);
14212                           return;
14213                         }
14214                     }
14215                 }else{
14216                     if(k == keyCode){
14217                         fn.call(scope || window, dlg, k, e);
14218                     }
14219                 }
14220             }
14221         };
14222         this.on("keydown", handler);
14223         return this;
14224     },
14225
14226     /**
14227      * Returns the TabPanel component (creates it if it doesn't exist).
14228      * Note: If you wish to simply check for the existence of tabs without creating them,
14229      * check for a null 'tabs' property.
14230      * @return {Roo.TabPanel} The tabs component
14231      */
14232     getTabs : function(){
14233         if(!this.tabs){
14234             this.el.addClass("x-dlg-auto-tabs");
14235             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14236             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14237         }
14238         return this.tabs;
14239     },
14240
14241     /**
14242      * Adds a button to the footer section of the dialog.
14243      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14244      * object or a valid Roo.DomHelper element config
14245      * @param {Function} handler The function called when the button is clicked
14246      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14247      * @return {Roo.Button} The new button
14248      */
14249     addButton : function(config, handler, scope){
14250         var dh = Roo.DomHelper;
14251         if(!this.footer){
14252             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14253         }
14254         if(!this.btnContainer){
14255             var tb = this.footer.createChild({
14256
14257                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14258                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14259             }, null, true);
14260             this.btnContainer = tb.firstChild.firstChild.firstChild;
14261         }
14262         var bconfig = {
14263             handler: handler,
14264             scope: scope,
14265             minWidth: this.minButtonWidth,
14266             hideParent:true
14267         };
14268         if(typeof config == "string"){
14269             bconfig.text = config;
14270         }else{
14271             if(config.tag){
14272                 bconfig.dhconfig = config;
14273             }else{
14274                 Roo.apply(bconfig, config);
14275             }
14276         }
14277         var fc = false;
14278         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14279             bconfig.position = Math.max(0, bconfig.position);
14280             fc = this.btnContainer.childNodes[bconfig.position];
14281         }
14282          
14283         var btn = new Roo.Button(
14284             fc ? 
14285                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14286                 : this.btnContainer.appendChild(document.createElement("td")),
14287             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14288             bconfig
14289         );
14290         this.syncBodyHeight();
14291         if(!this.buttons){
14292             /**
14293              * Array of all the buttons that have been added to this dialog via addButton
14294              * @type Array
14295              */
14296             this.buttons = [];
14297         }
14298         this.buttons.push(btn);
14299         return btn;
14300     },
14301
14302     /**
14303      * Sets the default button to be focused when the dialog is displayed.
14304      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14305      * @return {Roo.BasicDialog} this
14306      */
14307     setDefaultButton : function(btn){
14308         this.defaultButton = btn;
14309         return this;
14310     },
14311
14312     // private
14313     getHeaderFooterHeight : function(safe){
14314         var height = 0;
14315         if(this.header){
14316            height += this.header.getHeight();
14317         }
14318         if(this.footer){
14319            var fm = this.footer.getMargins();
14320             height += (this.footer.getHeight()+fm.top+fm.bottom);
14321         }
14322         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14323         height += this.centerBg.getPadding("tb");
14324         return height;
14325     },
14326
14327     // private
14328     syncBodyHeight : function()
14329     {
14330         var bd = this.body, // the text
14331             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14332             bw = this.bwrap;
14333         var height = this.size.height - this.getHeaderFooterHeight(false);
14334         bd.setHeight(height-bd.getMargins("tb"));
14335         var hh = this.header.getHeight();
14336         var h = this.size.height-hh;
14337         cb.setHeight(h);
14338         
14339         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14340         bw.setHeight(h-cb.getPadding("tb"));
14341         
14342         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14343         bd.setWidth(bw.getWidth(true));
14344         if(this.tabs){
14345             this.tabs.syncHeight();
14346             if(Roo.isIE){
14347                 this.tabs.el.repaint();
14348             }
14349         }
14350     },
14351
14352     /**
14353      * Restores the previous state of the dialog if Roo.state is configured.
14354      * @return {Roo.BasicDialog} this
14355      */
14356     restoreState : function(){
14357         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14358         if(box && box.width){
14359             this.xy = [box.x, box.y];
14360             this.resizeTo(box.width, box.height);
14361         }
14362         return this;
14363     },
14364
14365     // private
14366     beforeShow : function(){
14367         this.expand();
14368         if(this.fixedcenter){
14369             this.xy = this.el.getCenterXY(true);
14370         }
14371         if(this.modal){
14372             Roo.get(document.body).addClass("x-body-masked");
14373             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14374             this.mask.show();
14375         }
14376         this.constrainXY();
14377     },
14378
14379     // private
14380     animShow : function(){
14381         var b = Roo.get(this.animateTarget).getBox();
14382         this.proxy.setSize(b.width, b.height);
14383         this.proxy.setLocation(b.x, b.y);
14384         this.proxy.show();
14385         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14386                     true, .35, this.showEl.createDelegate(this));
14387     },
14388
14389     /**
14390      * Shows the dialog.
14391      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14392      * @return {Roo.BasicDialog} this
14393      */
14394     show : function(animateTarget){
14395         if (this.fireEvent("beforeshow", this) === false){
14396             return;
14397         }
14398         if(this.syncHeightBeforeShow){
14399             this.syncBodyHeight();
14400         }else if(this.firstShow){
14401             this.firstShow = false;
14402             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14403         }
14404         this.animateTarget = animateTarget || this.animateTarget;
14405         if(!this.el.isVisible()){
14406             this.beforeShow();
14407             if(this.animateTarget && Roo.get(this.animateTarget)){
14408                 this.animShow();
14409             }else{
14410                 this.showEl();
14411             }
14412         }
14413         return this;
14414     },
14415
14416     // private
14417     showEl : function(){
14418         this.proxy.hide();
14419         this.el.setXY(this.xy);
14420         this.el.show();
14421         this.adjustAssets(true);
14422         this.toFront();
14423         this.focus();
14424         // IE peekaboo bug - fix found by Dave Fenwick
14425         if(Roo.isIE){
14426             this.el.repaint();
14427         }
14428         this.fireEvent("show", this);
14429     },
14430
14431     /**
14432      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14433      * dialog itself will receive focus.
14434      */
14435     focus : function(){
14436         if(this.defaultButton){
14437             this.defaultButton.focus();
14438         }else{
14439             this.focusEl.focus();
14440         }
14441     },
14442
14443     // private
14444     constrainXY : function(){
14445         if(this.constraintoviewport !== false){
14446             if(!this.viewSize){
14447                 if(this.container){
14448                     var s = this.container.getSize();
14449                     this.viewSize = [s.width, s.height];
14450                 }else{
14451                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14452                 }
14453             }
14454             var s = Roo.get(this.container||document).getScroll();
14455
14456             var x = this.xy[0], y = this.xy[1];
14457             var w = this.size.width, h = this.size.height;
14458             var vw = this.viewSize[0], vh = this.viewSize[1];
14459             // only move it if it needs it
14460             var moved = false;
14461             // first validate right/bottom
14462             if(x + w > vw+s.left){
14463                 x = vw - w;
14464                 moved = true;
14465             }
14466             if(y + h > vh+s.top){
14467                 y = vh - h;
14468                 moved = true;
14469             }
14470             // then make sure top/left isn't negative
14471             if(x < s.left){
14472                 x = s.left;
14473                 moved = true;
14474             }
14475             if(y < s.top){
14476                 y = s.top;
14477                 moved = true;
14478             }
14479             if(moved){
14480                 // cache xy
14481                 this.xy = [x, y];
14482                 if(this.isVisible()){
14483                     this.el.setLocation(x, y);
14484                     this.adjustAssets();
14485                 }
14486             }
14487         }
14488     },
14489
14490     // private
14491     onDrag : function(){
14492         if(!this.proxyDrag){
14493             this.xy = this.el.getXY();
14494             this.adjustAssets();
14495         }
14496     },
14497
14498     // private
14499     adjustAssets : function(doShow){
14500         var x = this.xy[0], y = this.xy[1];
14501         var w = this.size.width, h = this.size.height;
14502         if(doShow === true){
14503             if(this.shadow){
14504                 this.shadow.show(this.el);
14505             }
14506             if(this.shim){
14507                 this.shim.show();
14508             }
14509         }
14510         if(this.shadow && this.shadow.isVisible()){
14511             this.shadow.show(this.el);
14512         }
14513         if(this.shim && this.shim.isVisible()){
14514             this.shim.setBounds(x, y, w, h);
14515         }
14516     },
14517
14518     // private
14519     adjustViewport : function(w, h){
14520         if(!w || !h){
14521             w = Roo.lib.Dom.getViewWidth();
14522             h = Roo.lib.Dom.getViewHeight();
14523         }
14524         // cache the size
14525         this.viewSize = [w, h];
14526         if(this.modal && this.mask.isVisible()){
14527             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14528             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14529         }
14530         if(this.isVisible()){
14531             this.constrainXY();
14532         }
14533     },
14534
14535     /**
14536      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14537      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14538      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14539      */
14540     destroy : function(removeEl){
14541         if(this.isVisible()){
14542             this.animateTarget = null;
14543             this.hide();
14544         }
14545         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14546         if(this.tabs){
14547             this.tabs.destroy(removeEl);
14548         }
14549         Roo.destroy(
14550              this.shim,
14551              this.proxy,
14552              this.resizer,
14553              this.close,
14554              this.mask
14555         );
14556         if(this.dd){
14557             this.dd.unreg();
14558         }
14559         if(this.buttons){
14560            for(var i = 0, len = this.buttons.length; i < len; i++){
14561                this.buttons[i].destroy();
14562            }
14563         }
14564         this.el.removeAllListeners();
14565         if(removeEl === true){
14566             this.el.update("");
14567             this.el.remove();
14568         }
14569         Roo.DialogManager.unregister(this);
14570     },
14571
14572     // private
14573     startMove : function(){
14574         if(this.proxyDrag){
14575             this.proxy.show();
14576         }
14577         if(this.constraintoviewport !== false){
14578             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14579         }
14580     },
14581
14582     // private
14583     endMove : function(){
14584         if(!this.proxyDrag){
14585             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14586         }else{
14587             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14588             this.proxy.hide();
14589         }
14590         this.refreshSize();
14591         this.adjustAssets();
14592         this.focus();
14593         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14594     },
14595
14596     /**
14597      * Brings this dialog to the front of any other visible dialogs
14598      * @return {Roo.BasicDialog} this
14599      */
14600     toFront : function(){
14601         Roo.DialogManager.bringToFront(this);
14602         return this;
14603     },
14604
14605     /**
14606      * Sends this dialog to the back (under) of any other visible dialogs
14607      * @return {Roo.BasicDialog} this
14608      */
14609     toBack : function(){
14610         Roo.DialogManager.sendToBack(this);
14611         return this;
14612     },
14613
14614     /**
14615      * Centers this dialog in the viewport
14616      * @return {Roo.BasicDialog} this
14617      */
14618     center : function(){
14619         var xy = this.el.getCenterXY(true);
14620         this.moveTo(xy[0], xy[1]);
14621         return this;
14622     },
14623
14624     /**
14625      * Moves the dialog's top-left corner to the specified point
14626      * @param {Number} x
14627      * @param {Number} y
14628      * @return {Roo.BasicDialog} this
14629      */
14630     moveTo : function(x, y){
14631         this.xy = [x,y];
14632         if(this.isVisible()){
14633             this.el.setXY(this.xy);
14634             this.adjustAssets();
14635         }
14636         return this;
14637     },
14638
14639     /**
14640      * Aligns the dialog to the specified element
14641      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14642      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14643      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14644      * @return {Roo.BasicDialog} this
14645      */
14646     alignTo : function(element, position, offsets){
14647         this.xy = this.el.getAlignToXY(element, position, offsets);
14648         if(this.isVisible()){
14649             this.el.setXY(this.xy);
14650             this.adjustAssets();
14651         }
14652         return this;
14653     },
14654
14655     /**
14656      * Anchors an element to another element and realigns it when the window is resized.
14657      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14658      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14659      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14660      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14661      * is a number, it is used as the buffer delay (defaults to 50ms).
14662      * @return {Roo.BasicDialog} this
14663      */
14664     anchorTo : function(el, alignment, offsets, monitorScroll){
14665         var action = function(){
14666             this.alignTo(el, alignment, offsets);
14667         };
14668         Roo.EventManager.onWindowResize(action, this);
14669         var tm = typeof monitorScroll;
14670         if(tm != 'undefined'){
14671             Roo.EventManager.on(window, 'scroll', action, this,
14672                 {buffer: tm == 'number' ? monitorScroll : 50});
14673         }
14674         action.call(this);
14675         return this;
14676     },
14677
14678     /**
14679      * Returns true if the dialog is visible
14680      * @return {Boolean}
14681      */
14682     isVisible : function(){
14683         return this.el.isVisible();
14684     },
14685
14686     // private
14687     animHide : function(callback){
14688         var b = Roo.get(this.animateTarget).getBox();
14689         this.proxy.show();
14690         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14691         this.el.hide();
14692         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14693                     this.hideEl.createDelegate(this, [callback]));
14694     },
14695
14696     /**
14697      * Hides the dialog.
14698      * @param {Function} callback (optional) Function to call when the dialog is hidden
14699      * @return {Roo.BasicDialog} this
14700      */
14701     hide : function(callback){
14702         if (this.fireEvent("beforehide", this) === false){
14703             return;
14704         }
14705         if(this.shadow){
14706             this.shadow.hide();
14707         }
14708         if(this.shim) {
14709           this.shim.hide();
14710         }
14711         // sometimes animateTarget seems to get set.. causing problems...
14712         // this just double checks..
14713         if(this.animateTarget && Roo.get(this.animateTarget)) {
14714            this.animHide(callback);
14715         }else{
14716             this.el.hide();
14717             this.hideEl(callback);
14718         }
14719         return this;
14720     },
14721
14722     // private
14723     hideEl : function(callback){
14724         this.proxy.hide();
14725         if(this.modal){
14726             this.mask.hide();
14727             Roo.get(document.body).removeClass("x-body-masked");
14728         }
14729         this.fireEvent("hide", this);
14730         if(typeof callback == "function"){
14731             callback();
14732         }
14733     },
14734
14735     // private
14736     hideAction : function(){
14737         this.setLeft("-10000px");
14738         this.setTop("-10000px");
14739         this.setStyle("visibility", "hidden");
14740     },
14741
14742     // private
14743     refreshSize : function(){
14744         this.size = this.el.getSize();
14745         this.xy = this.el.getXY();
14746         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14747     },
14748
14749     // private
14750     // z-index is managed by the DialogManager and may be overwritten at any time
14751     setZIndex : function(index){
14752         if(this.modal){
14753             this.mask.setStyle("z-index", index);
14754         }
14755         if(this.shim){
14756             this.shim.setStyle("z-index", ++index);
14757         }
14758         if(this.shadow){
14759             this.shadow.setZIndex(++index);
14760         }
14761         this.el.setStyle("z-index", ++index);
14762         if(this.proxy){
14763             this.proxy.setStyle("z-index", ++index);
14764         }
14765         if(this.resizer){
14766             this.resizer.proxy.setStyle("z-index", ++index);
14767         }
14768
14769         this.lastZIndex = index;
14770     },
14771
14772     /**
14773      * Returns the element for this dialog
14774      * @return {Roo.Element} The underlying dialog Element
14775      */
14776     getEl : function(){
14777         return this.el;
14778     }
14779 });
14780
14781 /**
14782  * @class Roo.DialogManager
14783  * Provides global access to BasicDialogs that have been created and
14784  * support for z-indexing (layering) multiple open dialogs.
14785  */
14786 Roo.DialogManager = function(){
14787     var list = {};
14788     var accessList = [];
14789     var front = null;
14790
14791     // private
14792     var sortDialogs = function(d1, d2){
14793         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14794     };
14795
14796     // private
14797     var orderDialogs = function(){
14798         accessList.sort(sortDialogs);
14799         var seed = Roo.DialogManager.zseed;
14800         for(var i = 0, len = accessList.length; i < len; i++){
14801             var dlg = accessList[i];
14802             if(dlg){
14803                 dlg.setZIndex(seed + (i*10));
14804             }
14805         }
14806     };
14807
14808     return {
14809         /**
14810          * The starting z-index for BasicDialogs (defaults to 9000)
14811          * @type Number The z-index value
14812          */
14813         zseed : 9000,
14814
14815         // private
14816         register : function(dlg){
14817             list[dlg.id] = dlg;
14818             accessList.push(dlg);
14819         },
14820
14821         // private
14822         unregister : function(dlg){
14823             delete list[dlg.id];
14824             var i=0;
14825             var len=0;
14826             if(!accessList.indexOf){
14827                 for(  i = 0, len = accessList.length; i < len; i++){
14828                     if(accessList[i] == dlg){
14829                         accessList.splice(i, 1);
14830                         return;
14831                     }
14832                 }
14833             }else{
14834                  i = accessList.indexOf(dlg);
14835                 if(i != -1){
14836                     accessList.splice(i, 1);
14837                 }
14838             }
14839         },
14840
14841         /**
14842          * Gets a registered dialog by id
14843          * @param {String/Object} id The id of the dialog or a dialog
14844          * @return {Roo.BasicDialog} this
14845          */
14846         get : function(id){
14847             return typeof id == "object" ? id : list[id];
14848         },
14849
14850         /**
14851          * Brings the specified dialog to the front
14852          * @param {String/Object} dlg The id of the dialog or a dialog
14853          * @return {Roo.BasicDialog} this
14854          */
14855         bringToFront : function(dlg){
14856             dlg = this.get(dlg);
14857             if(dlg != front){
14858                 front = dlg;
14859                 dlg._lastAccess = new Date().getTime();
14860                 orderDialogs();
14861             }
14862             return dlg;
14863         },
14864
14865         /**
14866          * Sends the specified dialog to the back
14867          * @param {String/Object} dlg The id of the dialog or a dialog
14868          * @return {Roo.BasicDialog} this
14869          */
14870         sendToBack : function(dlg){
14871             dlg = this.get(dlg);
14872             dlg._lastAccess = -(new Date().getTime());
14873             orderDialogs();
14874             return dlg;
14875         },
14876
14877         /**
14878          * Hides all dialogs
14879          */
14880         hideAll : function(){
14881             for(var id in list){
14882                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14883                     list[id].hide();
14884                 }
14885             }
14886         }
14887     };
14888 }();
14889
14890 /**
14891  * @class Roo.LayoutDialog
14892  * @extends Roo.BasicDialog
14893  * Dialog which provides adjustments for working with a layout in a Dialog.
14894  * Add your necessary layout config options to the dialog's config.<br>
14895  * Example usage (including a nested layout):
14896  * <pre><code>
14897 if(!dialog){
14898     dialog = new Roo.LayoutDialog("download-dlg", {
14899         modal: true,
14900         width:600,
14901         height:450,
14902         shadow:true,
14903         minWidth:500,
14904         minHeight:350,
14905         autoTabs:true,
14906         proxyDrag:true,
14907         // layout config merges with the dialog config
14908         center:{
14909             tabPosition: "top",
14910             alwaysShowTabs: true
14911         }
14912     });
14913     dialog.addKeyListener(27, dialog.hide, dialog);
14914     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14915     dialog.addButton("Build It!", this.getDownload, this);
14916
14917     // we can even add nested layouts
14918     var innerLayout = new Roo.BorderLayout("dl-inner", {
14919         east: {
14920             initialSize: 200,
14921             autoScroll:true,
14922             split:true
14923         },
14924         center: {
14925             autoScroll:true
14926         }
14927     });
14928     innerLayout.beginUpdate();
14929     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14930     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14931     innerLayout.endUpdate(true);
14932
14933     var layout = dialog.getLayout();
14934     layout.beginUpdate();
14935     layout.add("center", new Roo.ContentPanel("standard-panel",
14936                         {title: "Download the Source", fitToFrame:true}));
14937     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14938                {title: "Build your own roo.js"}));
14939     layout.getRegion("center").showPanel(sp);
14940     layout.endUpdate();
14941 }
14942 </code></pre>
14943     * @constructor
14944     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14945     * @param {Object} config configuration options
14946   */
14947 Roo.LayoutDialog = function(el, cfg){
14948     
14949     var config=  cfg;
14950     if (typeof(cfg) == 'undefined') {
14951         config = Roo.apply({}, el);
14952         // not sure why we use documentElement here.. - it should always be body.
14953         // IE7 borks horribly if we use documentElement.
14954         // webkit also does not like documentElement - it creates a body element...
14955         el = Roo.get( document.body || document.documentElement ).createChild();
14956         //config.autoCreate = true;
14957     }
14958     
14959     
14960     config.autoTabs = false;
14961     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14962     this.body.setStyle({overflow:"hidden", position:"relative"});
14963     this.layout = new Roo.BorderLayout(this.body.dom, config);
14964     this.layout.monitorWindowResize = false;
14965     this.el.addClass("x-dlg-auto-layout");
14966     // fix case when center region overwrites center function
14967     this.center = Roo.BasicDialog.prototype.center;
14968     this.on("show", this.layout.layout, this.layout, true);
14969     if (config.items) {
14970         var xitems = config.items;
14971         delete config.items;
14972         Roo.each(xitems, this.addxtype, this);
14973     }
14974     
14975     
14976 };
14977 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14978     /**
14979      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14980      * @deprecated
14981      */
14982     endUpdate : function(){
14983         this.layout.endUpdate();
14984     },
14985
14986     /**
14987      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14988      *  @deprecated
14989      */
14990     beginUpdate : function(){
14991         this.layout.beginUpdate();
14992     },
14993
14994     /**
14995      * Get the BorderLayout for this dialog
14996      * @return {Roo.BorderLayout}
14997      */
14998     getLayout : function(){
14999         return this.layout;
15000     },
15001
15002     showEl : function(){
15003         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15004         if(Roo.isIE7){
15005             this.layout.layout();
15006         }
15007     },
15008
15009     // private
15010     // Use the syncHeightBeforeShow config option to control this automatically
15011     syncBodyHeight : function(){
15012         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15013         if(this.layout){this.layout.layout();}
15014     },
15015     
15016       /**
15017      * Add an xtype element (actually adds to the layout.)
15018      * @return {Object} xdata xtype object data.
15019      */
15020     
15021     addxtype : function(c) {
15022         return this.layout.addxtype(c);
15023     }
15024 });/*
15025  * Based on:
15026  * Ext JS Library 1.1.1
15027  * Copyright(c) 2006-2007, Ext JS, LLC.
15028  *
15029  * Originally Released Under LGPL - original licence link has changed is not relivant.
15030  *
15031  * Fork - LGPL
15032  * <script type="text/javascript">
15033  */
15034  
15035 /**
15036  * @class Roo.MessageBox
15037  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15038  * Example usage:
15039  *<pre><code>
15040 // Basic alert:
15041 Roo.Msg.alert('Status', 'Changes saved successfully.');
15042
15043 // Prompt for user data:
15044 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15045     if (btn == 'ok'){
15046         // process text value...
15047     }
15048 });
15049
15050 // Show a dialog using config options:
15051 Roo.Msg.show({
15052    title:'Save Changes?',
15053    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15054    buttons: Roo.Msg.YESNOCANCEL,
15055    fn: processResult,
15056    animEl: 'elId'
15057 });
15058 </code></pre>
15059  * @singleton
15060  */
15061 Roo.MessageBox = function(){
15062     var dlg, opt, mask, waitTimer;
15063     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15064     var buttons, activeTextEl, bwidth;
15065
15066     // private
15067     var handleButton = function(button){
15068         dlg.hide();
15069         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15070     };
15071
15072     // private
15073     var handleHide = function(){
15074         if(opt && opt.cls){
15075             dlg.el.removeClass(opt.cls);
15076         }
15077         if(waitTimer){
15078             Roo.TaskMgr.stop(waitTimer);
15079             waitTimer = null;
15080         }
15081     };
15082
15083     // private
15084     var updateButtons = function(b){
15085         var width = 0;
15086         if(!b){
15087             buttons["ok"].hide();
15088             buttons["cancel"].hide();
15089             buttons["yes"].hide();
15090             buttons["no"].hide();
15091             dlg.footer.dom.style.display = 'none';
15092             return width;
15093         }
15094         dlg.footer.dom.style.display = '';
15095         for(var k in buttons){
15096             if(typeof buttons[k] != "function"){
15097                 if(b[k]){
15098                     buttons[k].show();
15099                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15100                     width += buttons[k].el.getWidth()+15;
15101                 }else{
15102                     buttons[k].hide();
15103                 }
15104             }
15105         }
15106         return width;
15107     };
15108
15109     // private
15110     var handleEsc = function(d, k, e){
15111         if(opt && opt.closable !== false){
15112             dlg.hide();
15113         }
15114         if(e){
15115             e.stopEvent();
15116         }
15117     };
15118
15119     return {
15120         /**
15121          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15122          * @return {Roo.BasicDialog} The BasicDialog element
15123          */
15124         getDialog : function(){
15125            if(!dlg){
15126                 dlg = new Roo.BasicDialog("x-msg-box", {
15127                     autoCreate : true,
15128                     shadow: true,
15129                     draggable: true,
15130                     resizable:false,
15131                     constraintoviewport:false,
15132                     fixedcenter:true,
15133                     collapsible : false,
15134                     shim:true,
15135                     modal: true,
15136                     width:400, height:100,
15137                     buttonAlign:"center",
15138                     closeClick : function(){
15139                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15140                             handleButton("no");
15141                         }else{
15142                             handleButton("cancel");
15143                         }
15144                     }
15145                 });
15146                 dlg.on("hide", handleHide);
15147                 mask = dlg.mask;
15148                 dlg.addKeyListener(27, handleEsc);
15149                 buttons = {};
15150                 var bt = this.buttonText;
15151                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15152                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15153                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15154                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15155                 bodyEl = dlg.body.createChild({
15156
15157                     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>'
15158                 });
15159                 msgEl = bodyEl.dom.firstChild;
15160                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15161                 textboxEl.enableDisplayMode();
15162                 textboxEl.addKeyListener([10,13], function(){
15163                     if(dlg.isVisible() && opt && opt.buttons){
15164                         if(opt.buttons.ok){
15165                             handleButton("ok");
15166                         }else if(opt.buttons.yes){
15167                             handleButton("yes");
15168                         }
15169                     }
15170                 });
15171                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15172                 textareaEl.enableDisplayMode();
15173                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15174                 progressEl.enableDisplayMode();
15175                 var pf = progressEl.dom.firstChild;
15176                 if (pf) {
15177                     pp = Roo.get(pf.firstChild);
15178                     pp.setHeight(pf.offsetHeight);
15179                 }
15180                 
15181             }
15182             return dlg;
15183         },
15184
15185         /**
15186          * Updates the message box body text
15187          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15188          * the XHTML-compliant non-breaking space character '&amp;#160;')
15189          * @return {Roo.MessageBox} This message box
15190          */
15191         updateText : function(text){
15192             if(!dlg.isVisible() && !opt.width){
15193                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15194             }
15195             msgEl.innerHTML = text || '&#160;';
15196       
15197             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15198             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15199             var w = Math.max(
15200                     Math.min(opt.width || cw , this.maxWidth), 
15201                     Math.max(opt.minWidth || this.minWidth, bwidth)
15202             );
15203             if(opt.prompt){
15204                 activeTextEl.setWidth(w);
15205             }
15206             if(dlg.isVisible()){
15207                 dlg.fixedcenter = false;
15208             }
15209             // to big, make it scroll. = But as usual stupid IE does not support
15210             // !important..
15211             
15212             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15213                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15214                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15215             } else {
15216                 bodyEl.dom.style.height = '';
15217                 bodyEl.dom.style.overflowY = '';
15218             }
15219             if (cw > w) {
15220                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15221             } else {
15222                 bodyEl.dom.style.overflowX = '';
15223             }
15224             
15225             dlg.setContentSize(w, bodyEl.getHeight());
15226             if(dlg.isVisible()){
15227                 dlg.fixedcenter = true;
15228             }
15229             return this;
15230         },
15231
15232         /**
15233          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15234          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15235          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15236          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15237          * @return {Roo.MessageBox} This message box
15238          */
15239         updateProgress : function(value, text){
15240             if(text){
15241                 this.updateText(text);
15242             }
15243             if (pp) { // weird bug on my firefox - for some reason this is not defined
15244                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15245             }
15246             return this;
15247         },        
15248
15249         /**
15250          * Returns true if the message box is currently displayed
15251          * @return {Boolean} True if the message box is visible, else false
15252          */
15253         isVisible : function(){
15254             return dlg && dlg.isVisible();  
15255         },
15256
15257         /**
15258          * Hides the message box if it is displayed
15259          */
15260         hide : function(){
15261             if(this.isVisible()){
15262                 dlg.hide();
15263             }  
15264         },
15265
15266         /**
15267          * Displays a new message box, or reinitializes an existing message box, based on the config options
15268          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15269          * The following config object properties are supported:
15270          * <pre>
15271 Property    Type             Description
15272 ----------  ---------------  ------------------------------------------------------------------------------------
15273 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15274                                    closes (defaults to undefined)
15275 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15276                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15277 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15278                                    progress and wait dialogs will ignore this property and always hide the
15279                                    close button as they can only be closed programmatically.
15280 cls               String           A custom CSS class to apply to the message box element
15281 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15282                                    displayed (defaults to 75)
15283 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15284                                    function will be btn (the name of the button that was clicked, if applicable,
15285                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15286                                    Progress and wait dialogs will ignore this option since they do not respond to
15287                                    user actions and can only be closed programmatically, so any required function
15288                                    should be called by the same code after it closes the dialog.
15289 icon              String           A CSS class that provides a background image to be used as an icon for
15290                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15291 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15292 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15293 modal             Boolean          False to allow user interaction with the page while the message box is
15294                                    displayed (defaults to true)
15295 msg               String           A string that will replace the existing message box body text (defaults
15296                                    to the XHTML-compliant non-breaking space character '&#160;')
15297 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15298 progress          Boolean          True to display a progress bar (defaults to false)
15299 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15300 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15301 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15302 title             String           The title text
15303 value             String           The string value to set into the active textbox element if displayed
15304 wait              Boolean          True to display a progress bar (defaults to false)
15305 width             Number           The width of the dialog in pixels
15306 </pre>
15307          *
15308          * Example usage:
15309          * <pre><code>
15310 Roo.Msg.show({
15311    title: 'Address',
15312    msg: 'Please enter your address:',
15313    width: 300,
15314    buttons: Roo.MessageBox.OKCANCEL,
15315    multiline: true,
15316    fn: saveAddress,
15317    animEl: 'addAddressBtn'
15318 });
15319 </code></pre>
15320          * @param {Object} config Configuration options
15321          * @return {Roo.MessageBox} This message box
15322          */
15323         show : function(options)
15324         {
15325             
15326             // this causes nightmares if you show one dialog after another
15327             // especially on callbacks..
15328              
15329             if(this.isVisible()){
15330                 
15331                 this.hide();
15332                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15333                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15334                 Roo.log("New Dialog Message:" +  options.msg )
15335                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15336                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15337                 
15338             }
15339             var d = this.getDialog();
15340             opt = options;
15341             d.setTitle(opt.title || "&#160;");
15342             d.close.setDisplayed(opt.closable !== false);
15343             activeTextEl = textboxEl;
15344             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15345             if(opt.prompt){
15346                 if(opt.multiline){
15347                     textboxEl.hide();
15348                     textareaEl.show();
15349                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15350                         opt.multiline : this.defaultTextHeight);
15351                     activeTextEl = textareaEl;
15352                 }else{
15353                     textboxEl.show();
15354                     textareaEl.hide();
15355                 }
15356             }else{
15357                 textboxEl.hide();
15358                 textareaEl.hide();
15359             }
15360             progressEl.setDisplayed(opt.progress === true);
15361             this.updateProgress(0);
15362             activeTextEl.dom.value = opt.value || "";
15363             if(opt.prompt){
15364                 dlg.setDefaultButton(activeTextEl);
15365             }else{
15366                 var bs = opt.buttons;
15367                 var db = null;
15368                 if(bs && bs.ok){
15369                     db = buttons["ok"];
15370                 }else if(bs && bs.yes){
15371                     db = buttons["yes"];
15372                 }
15373                 dlg.setDefaultButton(db);
15374             }
15375             bwidth = updateButtons(opt.buttons);
15376             this.updateText(opt.msg);
15377             if(opt.cls){
15378                 d.el.addClass(opt.cls);
15379             }
15380             d.proxyDrag = opt.proxyDrag === true;
15381             d.modal = opt.modal !== false;
15382             d.mask = opt.modal !== false ? mask : false;
15383             if(!d.isVisible()){
15384                 // force it to the end of the z-index stack so it gets a cursor in FF
15385                 document.body.appendChild(dlg.el.dom);
15386                 d.animateTarget = null;
15387                 d.show(options.animEl);
15388             }
15389             return this;
15390         },
15391
15392         /**
15393          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15394          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15395          * and closing the message box when the process is complete.
15396          * @param {String} title The title bar text
15397          * @param {String} msg The message box body text
15398          * @return {Roo.MessageBox} This message box
15399          */
15400         progress : function(title, msg){
15401             this.show({
15402                 title : title,
15403                 msg : msg,
15404                 buttons: false,
15405                 progress:true,
15406                 closable:false,
15407                 minWidth: this.minProgressWidth,
15408                 modal : true
15409             });
15410             return this;
15411         },
15412
15413         /**
15414          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15415          * If a callback function is passed it will be called after the user clicks the button, and the
15416          * id of the button that was clicked will be passed as the only parameter to the callback
15417          * (could also be the top-right close button).
15418          * @param {String} title The title bar text
15419          * @param {String} msg The message box body text
15420          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15421          * @param {Object} scope (optional) The scope of the callback function
15422          * @return {Roo.MessageBox} This message box
15423          */
15424         alert : function(title, msg, fn, scope){
15425             this.show({
15426                 title : title,
15427                 msg : msg,
15428                 buttons: this.OK,
15429                 fn: fn,
15430                 scope : scope,
15431                 modal : true
15432             });
15433             return this;
15434         },
15435
15436         /**
15437          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15438          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15439          * You are responsible for closing the message box when the process is complete.
15440          * @param {String} msg The message box body text
15441          * @param {String} title (optional) The title bar text
15442          * @return {Roo.MessageBox} This message box
15443          */
15444         wait : function(msg, title){
15445             this.show({
15446                 title : title,
15447                 msg : msg,
15448                 buttons: false,
15449                 closable:false,
15450                 progress:true,
15451                 modal:true,
15452                 width:300,
15453                 wait:true
15454             });
15455             waitTimer = Roo.TaskMgr.start({
15456                 run: function(i){
15457                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15458                 },
15459                 interval: 1000
15460             });
15461             return this;
15462         },
15463
15464         /**
15465          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15466          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15467          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15468          * @param {String} title The title bar text
15469          * @param {String} msg The message box body text
15470          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15471          * @param {Object} scope (optional) The scope of the callback function
15472          * @return {Roo.MessageBox} This message box
15473          */
15474         confirm : function(title, msg, fn, scope){
15475             this.show({
15476                 title : title,
15477                 msg : msg,
15478                 buttons: this.YESNO,
15479                 fn: fn,
15480                 scope : scope,
15481                 modal : true
15482             });
15483             return this;
15484         },
15485
15486         /**
15487          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15488          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15489          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15490          * (could also be the top-right close button) and the text that was entered will be passed as the two
15491          * parameters to the callback.
15492          * @param {String} title The title bar text
15493          * @param {String} msg The message box body text
15494          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15495          * @param {Object} scope (optional) The scope of the callback function
15496          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15497          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15498          * @return {Roo.MessageBox} This message box
15499          */
15500         prompt : function(title, msg, fn, scope, multiline){
15501             this.show({
15502                 title : title,
15503                 msg : msg,
15504                 buttons: this.OKCANCEL,
15505                 fn: fn,
15506                 minWidth:250,
15507                 scope : scope,
15508                 prompt:true,
15509                 multiline: multiline,
15510                 modal : true
15511             });
15512             return this;
15513         },
15514
15515         /**
15516          * Button config that displays a single OK button
15517          * @type Object
15518          */
15519         OK : {ok:true},
15520         /**
15521          * Button config that displays Yes and No buttons
15522          * @type Object
15523          */
15524         YESNO : {yes:true, no:true},
15525         /**
15526          * Button config that displays OK and Cancel buttons
15527          * @type Object
15528          */
15529         OKCANCEL : {ok:true, cancel:true},
15530         /**
15531          * Button config that displays Yes, No and Cancel buttons
15532          * @type Object
15533          */
15534         YESNOCANCEL : {yes:true, no:true, cancel:true},
15535
15536         /**
15537          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15538          * @type Number
15539          */
15540         defaultTextHeight : 75,
15541         /**
15542          * The maximum width in pixels of the message box (defaults to 600)
15543          * @type Number
15544          */
15545         maxWidth : 600,
15546         /**
15547          * The minimum width in pixels of the message box (defaults to 100)
15548          * @type Number
15549          */
15550         minWidth : 100,
15551         /**
15552          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15553          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15554          * @type Number
15555          */
15556         minProgressWidth : 250,
15557         /**
15558          * An object containing the default button text strings that can be overriden for localized language support.
15559          * Supported properties are: ok, cancel, yes and no.
15560          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15561          * @type Object
15562          */
15563         buttonText : {
15564             ok : "OK",
15565             cancel : "Cancel",
15566             yes : "Yes",
15567             no : "No"
15568         }
15569     };
15570 }();
15571
15572 /**
15573  * Shorthand for {@link Roo.MessageBox}
15574  */
15575 Roo.Msg = Roo.MessageBox;/*
15576  * Based on:
15577  * Ext JS Library 1.1.1
15578  * Copyright(c) 2006-2007, Ext JS, LLC.
15579  *
15580  * Originally Released Under LGPL - original licence link has changed is not relivant.
15581  *
15582  * Fork - LGPL
15583  * <script type="text/javascript">
15584  */
15585 /**
15586  * @class Roo.QuickTips
15587  * Provides attractive and customizable tooltips for any element.
15588  * @singleton
15589  */
15590 Roo.QuickTips = function(){
15591     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15592     var ce, bd, xy, dd;
15593     var visible = false, disabled = true, inited = false;
15594     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15595     
15596     var onOver = function(e){
15597         if(disabled){
15598             return;
15599         }
15600         var t = e.getTarget();
15601         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15602             return;
15603         }
15604         if(ce && t == ce.el){
15605             clearTimeout(hideProc);
15606             return;
15607         }
15608         if(t && tagEls[t.id]){
15609             tagEls[t.id].el = t;
15610             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15611             return;
15612         }
15613         var ttp, et = Roo.fly(t);
15614         var ns = cfg.namespace;
15615         if(tm.interceptTitles && t.title){
15616             ttp = t.title;
15617             t.qtip = ttp;
15618             t.removeAttribute("title");
15619             e.preventDefault();
15620         }else{
15621             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15622         }
15623         if(ttp){
15624             showProc = show.defer(tm.showDelay, tm, [{
15625                 el: t, 
15626                 text: ttp, 
15627                 width: et.getAttributeNS(ns, cfg.width),
15628                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15629                 title: et.getAttributeNS(ns, cfg.title),
15630                     cls: et.getAttributeNS(ns, cfg.cls)
15631             }]);
15632         }
15633     };
15634     
15635     var onOut = function(e){
15636         clearTimeout(showProc);
15637         var t = e.getTarget();
15638         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15639             hideProc = setTimeout(hide, tm.hideDelay);
15640         }
15641     };
15642     
15643     var onMove = function(e){
15644         if(disabled){
15645             return;
15646         }
15647         xy = e.getXY();
15648         xy[1] += 18;
15649         if(tm.trackMouse && ce){
15650             el.setXY(xy);
15651         }
15652     };
15653     
15654     var onDown = function(e){
15655         clearTimeout(showProc);
15656         clearTimeout(hideProc);
15657         if(!e.within(el)){
15658             if(tm.hideOnClick){
15659                 hide();
15660                 tm.disable();
15661                 tm.enable.defer(100, tm);
15662             }
15663         }
15664     };
15665     
15666     var getPad = function(){
15667         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15668     };
15669
15670     var show = function(o){
15671         if(disabled){
15672             return;
15673         }
15674         clearTimeout(dismissProc);
15675         ce = o;
15676         if(removeCls){ // in case manually hidden
15677             el.removeClass(removeCls);
15678             removeCls = null;
15679         }
15680         if(ce.cls){
15681             el.addClass(ce.cls);
15682             removeCls = ce.cls;
15683         }
15684         if(ce.title){
15685             tipTitle.update(ce.title);
15686             tipTitle.show();
15687         }else{
15688             tipTitle.update('');
15689             tipTitle.hide();
15690         }
15691         el.dom.style.width  = tm.maxWidth+'px';
15692         //tipBody.dom.style.width = '';
15693         tipBodyText.update(o.text);
15694         var p = getPad(), w = ce.width;
15695         if(!w){
15696             var td = tipBodyText.dom;
15697             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15698             if(aw > tm.maxWidth){
15699                 w = tm.maxWidth;
15700             }else if(aw < tm.minWidth){
15701                 w = tm.minWidth;
15702             }else{
15703                 w = aw;
15704             }
15705         }
15706         //tipBody.setWidth(w);
15707         el.setWidth(parseInt(w, 10) + p);
15708         if(ce.autoHide === false){
15709             close.setDisplayed(true);
15710             if(dd){
15711                 dd.unlock();
15712             }
15713         }else{
15714             close.setDisplayed(false);
15715             if(dd){
15716                 dd.lock();
15717             }
15718         }
15719         if(xy){
15720             el.avoidY = xy[1]-18;
15721             el.setXY(xy);
15722         }
15723         if(tm.animate){
15724             el.setOpacity(.1);
15725             el.setStyle("visibility", "visible");
15726             el.fadeIn({callback: afterShow});
15727         }else{
15728             afterShow();
15729         }
15730     };
15731     
15732     var afterShow = function(){
15733         if(ce){
15734             el.show();
15735             esc.enable();
15736             if(tm.autoDismiss && ce.autoHide !== false){
15737                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15738             }
15739         }
15740     };
15741     
15742     var hide = function(noanim){
15743         clearTimeout(dismissProc);
15744         clearTimeout(hideProc);
15745         ce = null;
15746         if(el.isVisible()){
15747             esc.disable();
15748             if(noanim !== true && tm.animate){
15749                 el.fadeOut({callback: afterHide});
15750             }else{
15751                 afterHide();
15752             } 
15753         }
15754     };
15755     
15756     var afterHide = function(){
15757         el.hide();
15758         if(removeCls){
15759             el.removeClass(removeCls);
15760             removeCls = null;
15761         }
15762     };
15763     
15764     return {
15765         /**
15766         * @cfg {Number} minWidth
15767         * The minimum width of the quick tip (defaults to 40)
15768         */
15769        minWidth : 40,
15770         /**
15771         * @cfg {Number} maxWidth
15772         * The maximum width of the quick tip (defaults to 300)
15773         */
15774        maxWidth : 300,
15775         /**
15776         * @cfg {Boolean} interceptTitles
15777         * True to automatically use the element's DOM title value if available (defaults to false)
15778         */
15779        interceptTitles : false,
15780         /**
15781         * @cfg {Boolean} trackMouse
15782         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15783         */
15784        trackMouse : false,
15785         /**
15786         * @cfg {Boolean} hideOnClick
15787         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15788         */
15789        hideOnClick : true,
15790         /**
15791         * @cfg {Number} showDelay
15792         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15793         */
15794        showDelay : 500,
15795         /**
15796         * @cfg {Number} hideDelay
15797         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15798         */
15799        hideDelay : 200,
15800         /**
15801         * @cfg {Boolean} autoHide
15802         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15803         * Used in conjunction with hideDelay.
15804         */
15805        autoHide : true,
15806         /**
15807         * @cfg {Boolean}
15808         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15809         * (defaults to true).  Used in conjunction with autoDismissDelay.
15810         */
15811        autoDismiss : true,
15812         /**
15813         * @cfg {Number}
15814         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15815         */
15816        autoDismissDelay : 5000,
15817        /**
15818         * @cfg {Boolean} animate
15819         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15820         */
15821        animate : false,
15822
15823        /**
15824         * @cfg {String} title
15825         * Title text to display (defaults to '').  This can be any valid HTML markup.
15826         */
15827         title: '',
15828        /**
15829         * @cfg {String} text
15830         * Body text to display (defaults to '').  This can be any valid HTML markup.
15831         */
15832         text : '',
15833        /**
15834         * @cfg {String} cls
15835         * A CSS class to apply to the base quick tip element (defaults to '').
15836         */
15837         cls : '',
15838        /**
15839         * @cfg {Number} width
15840         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15841         * minWidth or maxWidth.
15842         */
15843         width : null,
15844
15845     /**
15846      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15847      * or display QuickTips in a page.
15848      */
15849        init : function(){
15850           tm = Roo.QuickTips;
15851           cfg = tm.tagConfig;
15852           if(!inited){
15853               if(!Roo.isReady){ // allow calling of init() before onReady
15854                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15855                   return;
15856               }
15857               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15858               el.fxDefaults = {stopFx: true};
15859               // maximum custom styling
15860               //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>');
15861               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>');              
15862               tipTitle = el.child('h3');
15863               tipTitle.enableDisplayMode("block");
15864               tipBody = el.child('div.x-tip-bd');
15865               tipBodyText = el.child('div.x-tip-bd-inner');
15866               //bdLeft = el.child('div.x-tip-bd-left');
15867               //bdRight = el.child('div.x-tip-bd-right');
15868               close = el.child('div.x-tip-close');
15869               close.enableDisplayMode("block");
15870               close.on("click", hide);
15871               var d = Roo.get(document);
15872               d.on("mousedown", onDown);
15873               d.on("mouseover", onOver);
15874               d.on("mouseout", onOut);
15875               d.on("mousemove", onMove);
15876               esc = d.addKeyListener(27, hide);
15877               esc.disable();
15878               if(Roo.dd.DD){
15879                   dd = el.initDD("default", null, {
15880                       onDrag : function(){
15881                           el.sync();  
15882                       }
15883                   });
15884                   dd.setHandleElId(tipTitle.id);
15885                   dd.lock();
15886               }
15887               inited = true;
15888           }
15889           this.enable(); 
15890        },
15891
15892     /**
15893      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15894      * are supported:
15895      * <pre>
15896 Property    Type                   Description
15897 ----------  ---------------------  ------------------------------------------------------------------------
15898 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15899      * </ul>
15900      * @param {Object} config The config object
15901      */
15902        register : function(config){
15903            var cs = config instanceof Array ? config : arguments;
15904            for(var i = 0, len = cs.length; i < len; i++) {
15905                var c = cs[i];
15906                var target = c.target;
15907                if(target){
15908                    if(target instanceof Array){
15909                        for(var j = 0, jlen = target.length; j < jlen; j++){
15910                            tagEls[target[j]] = c;
15911                        }
15912                    }else{
15913                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15914                    }
15915                }
15916            }
15917        },
15918
15919     /**
15920      * Removes this quick tip from its element and destroys it.
15921      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15922      */
15923        unregister : function(el){
15924            delete tagEls[Roo.id(el)];
15925        },
15926
15927     /**
15928      * Enable this quick tip.
15929      */
15930        enable : function(){
15931            if(inited && disabled){
15932                locks.pop();
15933                if(locks.length < 1){
15934                    disabled = false;
15935                }
15936            }
15937        },
15938
15939     /**
15940      * Disable this quick tip.
15941      */
15942        disable : function(){
15943           disabled = true;
15944           clearTimeout(showProc);
15945           clearTimeout(hideProc);
15946           clearTimeout(dismissProc);
15947           if(ce){
15948               hide(true);
15949           }
15950           locks.push(1);
15951        },
15952
15953     /**
15954      * Returns true if the quick tip is enabled, else false.
15955      */
15956        isEnabled : function(){
15957             return !disabled;
15958        },
15959
15960         // private
15961        tagConfig : {
15962            namespace : "ext",
15963            attribute : "qtip",
15964            width : "width",
15965            target : "target",
15966            title : "qtitle",
15967            hide : "hide",
15968            cls : "qclass"
15969        }
15970    };
15971 }();
15972
15973 // backwards compat
15974 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15975  * Based on:
15976  * Ext JS Library 1.1.1
15977  * Copyright(c) 2006-2007, Ext JS, LLC.
15978  *
15979  * Originally Released Under LGPL - original licence link has changed is not relivant.
15980  *
15981  * Fork - LGPL
15982  * <script type="text/javascript">
15983  */
15984  
15985
15986 /**
15987  * @class Roo.tree.TreePanel
15988  * @extends Roo.data.Tree
15989
15990  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15991  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15992  * @cfg {Boolean} enableDD true to enable drag and drop
15993  * @cfg {Boolean} enableDrag true to enable just drag
15994  * @cfg {Boolean} enableDrop true to enable just drop
15995  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15996  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15997  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15998  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15999  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16000  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16001  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16002  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16003  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16004  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16005  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16006  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16007  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16008  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16009  * @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>
16010  * @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>
16011  * 
16012  * @constructor
16013  * @param {String/HTMLElement/Element} el The container element
16014  * @param {Object} config
16015  */
16016 Roo.tree.TreePanel = function(el, config){
16017     var root = false;
16018     var loader = false;
16019     if (config.root) {
16020         root = config.root;
16021         delete config.root;
16022     }
16023     if (config.loader) {
16024         loader = config.loader;
16025         delete config.loader;
16026     }
16027     
16028     Roo.apply(this, config);
16029     Roo.tree.TreePanel.superclass.constructor.call(this);
16030     this.el = Roo.get(el);
16031     this.el.addClass('x-tree');
16032     //console.log(root);
16033     if (root) {
16034         this.setRootNode( Roo.factory(root, Roo.tree));
16035     }
16036     if (loader) {
16037         this.loader = Roo.factory(loader, Roo.tree);
16038     }
16039    /**
16040     * Read-only. The id of the container element becomes this TreePanel's id.
16041     */
16042     this.id = this.el.id;
16043     this.addEvents({
16044         /**
16045         * @event beforeload
16046         * Fires before a node is loaded, return false to cancel
16047         * @param {Node} node The node being loaded
16048         */
16049         "beforeload" : true,
16050         /**
16051         * @event load
16052         * Fires when a node is loaded
16053         * @param {Node} node The node that was loaded
16054         */
16055         "load" : true,
16056         /**
16057         * @event textchange
16058         * Fires when the text for a node is changed
16059         * @param {Node} node The node
16060         * @param {String} text The new text
16061         * @param {String} oldText The old text
16062         */
16063         "textchange" : true,
16064         /**
16065         * @event beforeexpand
16066         * Fires before a node is expanded, return false to cancel.
16067         * @param {Node} node The node
16068         * @param {Boolean} deep
16069         * @param {Boolean} anim
16070         */
16071         "beforeexpand" : true,
16072         /**
16073         * @event beforecollapse
16074         * Fires before a node is collapsed, return false to cancel.
16075         * @param {Node} node The node
16076         * @param {Boolean} deep
16077         * @param {Boolean} anim
16078         */
16079         "beforecollapse" : true,
16080         /**
16081         * @event expand
16082         * Fires when a node is expanded
16083         * @param {Node} node The node
16084         */
16085         "expand" : true,
16086         /**
16087         * @event disabledchange
16088         * Fires when the disabled status of a node changes
16089         * @param {Node} node The node
16090         * @param {Boolean} disabled
16091         */
16092         "disabledchange" : true,
16093         /**
16094         * @event collapse
16095         * Fires when a node is collapsed
16096         * @param {Node} node The node
16097         */
16098         "collapse" : true,
16099         /**
16100         * @event beforeclick
16101         * Fires before click processing on a node. Return false to cancel the default action.
16102         * @param {Node} node The node
16103         * @param {Roo.EventObject} e The event object
16104         */
16105         "beforeclick":true,
16106         /**
16107         * @event checkchange
16108         * Fires when a node with a checkbox's checked property changes
16109         * @param {Node} this This node
16110         * @param {Boolean} checked
16111         */
16112         "checkchange":true,
16113         /**
16114         * @event click
16115         * Fires when a node is clicked
16116         * @param {Node} node The node
16117         * @param {Roo.EventObject} e The event object
16118         */
16119         "click":true,
16120         /**
16121         * @event dblclick
16122         * Fires when a node is double clicked
16123         * @param {Node} node The node
16124         * @param {Roo.EventObject} e The event object
16125         */
16126         "dblclick":true,
16127         /**
16128         * @event contextmenu
16129         * Fires when a node is right clicked
16130         * @param {Node} node The node
16131         * @param {Roo.EventObject} e The event object
16132         */
16133         "contextmenu":true,
16134         /**
16135         * @event beforechildrenrendered
16136         * Fires right before the child nodes for a node are rendered
16137         * @param {Node} node The node
16138         */
16139         "beforechildrenrendered":true,
16140         /**
16141         * @event startdrag
16142         * Fires when a node starts being dragged
16143         * @param {Roo.tree.TreePanel} this
16144         * @param {Roo.tree.TreeNode} node
16145         * @param {event} e The raw browser event
16146         */ 
16147        "startdrag" : true,
16148        /**
16149         * @event enddrag
16150         * Fires when a drag operation is complete
16151         * @param {Roo.tree.TreePanel} this
16152         * @param {Roo.tree.TreeNode} node
16153         * @param {event} e The raw browser event
16154         */
16155        "enddrag" : true,
16156        /**
16157         * @event dragdrop
16158         * Fires when a dragged node is dropped on a valid DD target
16159         * @param {Roo.tree.TreePanel} this
16160         * @param {Roo.tree.TreeNode} node
16161         * @param {DD} dd The dd it was dropped on
16162         * @param {event} e The raw browser event
16163         */
16164        "dragdrop" : true,
16165        /**
16166         * @event beforenodedrop
16167         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16168         * passed to handlers has the following properties:<br />
16169         * <ul style="padding:5px;padding-left:16px;">
16170         * <li>tree - The TreePanel</li>
16171         * <li>target - The node being targeted for the drop</li>
16172         * <li>data - The drag data from the drag source</li>
16173         * <li>point - The point of the drop - append, above or below</li>
16174         * <li>source - The drag source</li>
16175         * <li>rawEvent - Raw mouse event</li>
16176         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16177         * to be inserted by setting them on this object.</li>
16178         * <li>cancel - Set this to true to cancel the drop.</li>
16179         * </ul>
16180         * @param {Object} dropEvent
16181         */
16182        "beforenodedrop" : true,
16183        /**
16184         * @event nodedrop
16185         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16186         * passed to handlers has the following properties:<br />
16187         * <ul style="padding:5px;padding-left:16px;">
16188         * <li>tree - The TreePanel</li>
16189         * <li>target - The node being targeted for the drop</li>
16190         * <li>data - The drag data from the drag source</li>
16191         * <li>point - The point of the drop - append, above or below</li>
16192         * <li>source - The drag source</li>
16193         * <li>rawEvent - Raw mouse event</li>
16194         * <li>dropNode - Dropped node(s).</li>
16195         * </ul>
16196         * @param {Object} dropEvent
16197         */
16198        "nodedrop" : true,
16199         /**
16200         * @event nodedragover
16201         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16202         * passed to handlers has the following properties:<br />
16203         * <ul style="padding:5px;padding-left:16px;">
16204         * <li>tree - The TreePanel</li>
16205         * <li>target - The node being targeted for the drop</li>
16206         * <li>data - The drag data from the drag source</li>
16207         * <li>point - The point of the drop - append, above or below</li>
16208         * <li>source - The drag source</li>
16209         * <li>rawEvent - Raw mouse event</li>
16210         * <li>dropNode - Drop node(s) provided by the source.</li>
16211         * <li>cancel - Set this to true to signal drop not allowed.</li>
16212         * </ul>
16213         * @param {Object} dragOverEvent
16214         */
16215        "nodedragover" : true
16216         
16217     });
16218     if(this.singleExpand){
16219        this.on("beforeexpand", this.restrictExpand, this);
16220     }
16221     if (this.editor) {
16222         this.editor.tree = this;
16223         this.editor = Roo.factory(this.editor, Roo.tree);
16224     }
16225     
16226     if (this.selModel) {
16227         this.selModel = Roo.factory(this.selModel, Roo.tree);
16228     }
16229    
16230 };
16231 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16232     rootVisible : true,
16233     animate: Roo.enableFx,
16234     lines : true,
16235     enableDD : false,
16236     hlDrop : Roo.enableFx,
16237   
16238     renderer: false,
16239     
16240     rendererTip: false,
16241     // private
16242     restrictExpand : function(node){
16243         var p = node.parentNode;
16244         if(p){
16245             if(p.expandedChild && p.expandedChild.parentNode == p){
16246                 p.expandedChild.collapse();
16247             }
16248             p.expandedChild = node;
16249         }
16250     },
16251
16252     // private override
16253     setRootNode : function(node){
16254         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16255         if(!this.rootVisible){
16256             node.ui = new Roo.tree.RootTreeNodeUI(node);
16257         }
16258         return node;
16259     },
16260
16261     /**
16262      * Returns the container element for this TreePanel
16263      */
16264     getEl : function(){
16265         return this.el;
16266     },
16267
16268     /**
16269      * Returns the default TreeLoader for this TreePanel
16270      */
16271     getLoader : function(){
16272         return this.loader;
16273     },
16274
16275     /**
16276      * Expand all nodes
16277      */
16278     expandAll : function(){
16279         this.root.expand(true);
16280     },
16281
16282     /**
16283      * Collapse all nodes
16284      */
16285     collapseAll : function(){
16286         this.root.collapse(true);
16287     },
16288
16289     /**
16290      * Returns the selection model used by this TreePanel
16291      */
16292     getSelectionModel : function(){
16293         if(!this.selModel){
16294             this.selModel = new Roo.tree.DefaultSelectionModel();
16295         }
16296         return this.selModel;
16297     },
16298
16299     /**
16300      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16301      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16302      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16303      * @return {Array}
16304      */
16305     getChecked : function(a, startNode){
16306         startNode = startNode || this.root;
16307         var r = [];
16308         var f = function(){
16309             if(this.attributes.checked){
16310                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16311             }
16312         }
16313         startNode.cascade(f);
16314         return r;
16315     },
16316
16317     /**
16318      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16319      * @param {String} path
16320      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16321      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16322      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16323      */
16324     expandPath : function(path, attr, callback){
16325         attr = attr || "id";
16326         var keys = path.split(this.pathSeparator);
16327         var curNode = this.root;
16328         if(curNode.attributes[attr] != keys[1]){ // invalid root
16329             if(callback){
16330                 callback(false, null);
16331             }
16332             return;
16333         }
16334         var index = 1;
16335         var f = function(){
16336             if(++index == keys.length){
16337                 if(callback){
16338                     callback(true, curNode);
16339                 }
16340                 return;
16341             }
16342             var c = curNode.findChild(attr, keys[index]);
16343             if(!c){
16344                 if(callback){
16345                     callback(false, curNode);
16346                 }
16347                 return;
16348             }
16349             curNode = c;
16350             c.expand(false, false, f);
16351         };
16352         curNode.expand(false, false, f);
16353     },
16354
16355     /**
16356      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16357      * @param {String} path
16358      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16359      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16360      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16361      */
16362     selectPath : function(path, attr, callback){
16363         attr = attr || "id";
16364         var keys = path.split(this.pathSeparator);
16365         var v = keys.pop();
16366         if(keys.length > 0){
16367             var f = function(success, node){
16368                 if(success && node){
16369                     var n = node.findChild(attr, v);
16370                     if(n){
16371                         n.select();
16372                         if(callback){
16373                             callback(true, n);
16374                         }
16375                     }else if(callback){
16376                         callback(false, n);
16377                     }
16378                 }else{
16379                     if(callback){
16380                         callback(false, n);
16381                     }
16382                 }
16383             };
16384             this.expandPath(keys.join(this.pathSeparator), attr, f);
16385         }else{
16386             this.root.select();
16387             if(callback){
16388                 callback(true, this.root);
16389             }
16390         }
16391     },
16392
16393     getTreeEl : function(){
16394         return this.el;
16395     },
16396
16397     /**
16398      * Trigger rendering of this TreePanel
16399      */
16400     render : function(){
16401         if (this.innerCt) {
16402             return this; // stop it rendering more than once!!
16403         }
16404         
16405         this.innerCt = this.el.createChild({tag:"ul",
16406                cls:"x-tree-root-ct " +
16407                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16408
16409         if(this.containerScroll){
16410             Roo.dd.ScrollManager.register(this.el);
16411         }
16412         if((this.enableDD || this.enableDrop) && !this.dropZone){
16413            /**
16414             * The dropZone used by this tree if drop is enabled
16415             * @type Roo.tree.TreeDropZone
16416             */
16417              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16418                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16419            });
16420         }
16421         if((this.enableDD || this.enableDrag) && !this.dragZone){
16422            /**
16423             * The dragZone used by this tree if drag is enabled
16424             * @type Roo.tree.TreeDragZone
16425             */
16426             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16427                ddGroup: this.ddGroup || "TreeDD",
16428                scroll: this.ddScroll
16429            });
16430         }
16431         this.getSelectionModel().init(this);
16432         if (!this.root) {
16433             Roo.log("ROOT not set in tree");
16434             return this;
16435         }
16436         this.root.render();
16437         if(!this.rootVisible){
16438             this.root.renderChildren();
16439         }
16440         return this;
16441     }
16442 });/*
16443  * Based on:
16444  * Ext JS Library 1.1.1
16445  * Copyright(c) 2006-2007, Ext JS, LLC.
16446  *
16447  * Originally Released Under LGPL - original licence link has changed is not relivant.
16448  *
16449  * Fork - LGPL
16450  * <script type="text/javascript">
16451  */
16452  
16453
16454 /**
16455  * @class Roo.tree.DefaultSelectionModel
16456  * @extends Roo.util.Observable
16457  * The default single selection for a TreePanel.
16458  * @param {Object} cfg Configuration
16459  */
16460 Roo.tree.DefaultSelectionModel = function(cfg){
16461    this.selNode = null;
16462    
16463    
16464    
16465    this.addEvents({
16466        /**
16467         * @event selectionchange
16468         * Fires when the selected node changes
16469         * @param {DefaultSelectionModel} this
16470         * @param {TreeNode} node the new selection
16471         */
16472        "selectionchange" : true,
16473
16474        /**
16475         * @event beforeselect
16476         * Fires before the selected node changes, return false to cancel the change
16477         * @param {DefaultSelectionModel} this
16478         * @param {TreeNode} node the new selection
16479         * @param {TreeNode} node the old selection
16480         */
16481        "beforeselect" : true
16482    });
16483    
16484     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16485 };
16486
16487 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16488     init : function(tree){
16489         this.tree = tree;
16490         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16491         tree.on("click", this.onNodeClick, this);
16492     },
16493     
16494     onNodeClick : function(node, e){
16495         if (e.ctrlKey && this.selNode == node)  {
16496             this.unselect(node);
16497             return;
16498         }
16499         this.select(node);
16500     },
16501     
16502     /**
16503      * Select a node.
16504      * @param {TreeNode} node The node to select
16505      * @return {TreeNode} The selected node
16506      */
16507     select : function(node){
16508         var last = this.selNode;
16509         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16510             if(last){
16511                 last.ui.onSelectedChange(false);
16512             }
16513             this.selNode = node;
16514             node.ui.onSelectedChange(true);
16515             this.fireEvent("selectionchange", this, node, last);
16516         }
16517         return node;
16518     },
16519     
16520     /**
16521      * Deselect a node.
16522      * @param {TreeNode} node The node to unselect
16523      */
16524     unselect : function(node){
16525         if(this.selNode == node){
16526             this.clearSelections();
16527         }    
16528     },
16529     
16530     /**
16531      * Clear all selections
16532      */
16533     clearSelections : function(){
16534         var n = this.selNode;
16535         if(n){
16536             n.ui.onSelectedChange(false);
16537             this.selNode = null;
16538             this.fireEvent("selectionchange", this, null);
16539         }
16540         return n;
16541     },
16542     
16543     /**
16544      * Get the selected node
16545      * @return {TreeNode} The selected node
16546      */
16547     getSelectedNode : function(){
16548         return this.selNode;    
16549     },
16550     
16551     /**
16552      * Returns true if the node is selected
16553      * @param {TreeNode} node The node to check
16554      * @return {Boolean}
16555      */
16556     isSelected : function(node){
16557         return this.selNode == node;  
16558     },
16559
16560     /**
16561      * Selects the node above the selected node in the tree, intelligently walking the nodes
16562      * @return TreeNode The new selection
16563      */
16564     selectPrevious : function(){
16565         var s = this.selNode || this.lastSelNode;
16566         if(!s){
16567             return null;
16568         }
16569         var ps = s.previousSibling;
16570         if(ps){
16571             if(!ps.isExpanded() || ps.childNodes.length < 1){
16572                 return this.select(ps);
16573             } else{
16574                 var lc = ps.lastChild;
16575                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16576                     lc = lc.lastChild;
16577                 }
16578                 return this.select(lc);
16579             }
16580         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16581             return this.select(s.parentNode);
16582         }
16583         return null;
16584     },
16585
16586     /**
16587      * Selects the node above the selected node in the tree, intelligently walking the nodes
16588      * @return TreeNode The new selection
16589      */
16590     selectNext : function(){
16591         var s = this.selNode || this.lastSelNode;
16592         if(!s){
16593             return null;
16594         }
16595         if(s.firstChild && s.isExpanded()){
16596              return this.select(s.firstChild);
16597          }else if(s.nextSibling){
16598              return this.select(s.nextSibling);
16599          }else if(s.parentNode){
16600             var newS = null;
16601             s.parentNode.bubble(function(){
16602                 if(this.nextSibling){
16603                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16604                     return false;
16605                 }
16606             });
16607             return newS;
16608          }
16609         return null;
16610     },
16611
16612     onKeyDown : function(e){
16613         var s = this.selNode || this.lastSelNode;
16614         // undesirable, but required
16615         var sm = this;
16616         if(!s){
16617             return;
16618         }
16619         var k = e.getKey();
16620         switch(k){
16621              case e.DOWN:
16622                  e.stopEvent();
16623                  this.selectNext();
16624              break;
16625              case e.UP:
16626                  e.stopEvent();
16627                  this.selectPrevious();
16628              break;
16629              case e.RIGHT:
16630                  e.preventDefault();
16631                  if(s.hasChildNodes()){
16632                      if(!s.isExpanded()){
16633                          s.expand();
16634                      }else if(s.firstChild){
16635                          this.select(s.firstChild, e);
16636                      }
16637                  }
16638              break;
16639              case e.LEFT:
16640                  e.preventDefault();
16641                  if(s.hasChildNodes() && s.isExpanded()){
16642                      s.collapse();
16643                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16644                      this.select(s.parentNode, e);
16645                  }
16646              break;
16647         };
16648     }
16649 });
16650
16651 /**
16652  * @class Roo.tree.MultiSelectionModel
16653  * @extends Roo.util.Observable
16654  * Multi selection for a TreePanel.
16655  * @param {Object} cfg Configuration
16656  */
16657 Roo.tree.MultiSelectionModel = function(){
16658    this.selNodes = [];
16659    this.selMap = {};
16660    this.addEvents({
16661        /**
16662         * @event selectionchange
16663         * Fires when the selected nodes change
16664         * @param {MultiSelectionModel} this
16665         * @param {Array} nodes Array of the selected nodes
16666         */
16667        "selectionchange" : true
16668    });
16669    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16670    
16671 };
16672
16673 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16674     init : function(tree){
16675         this.tree = tree;
16676         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16677         tree.on("click", this.onNodeClick, this);
16678     },
16679     
16680     onNodeClick : function(node, e){
16681         this.select(node, e, e.ctrlKey);
16682     },
16683     
16684     /**
16685      * Select a node.
16686      * @param {TreeNode} node The node to select
16687      * @param {EventObject} e (optional) An event associated with the selection
16688      * @param {Boolean} keepExisting True to retain existing selections
16689      * @return {TreeNode} The selected node
16690      */
16691     select : function(node, e, keepExisting){
16692         if(keepExisting !== true){
16693             this.clearSelections(true);
16694         }
16695         if(this.isSelected(node)){
16696             this.lastSelNode = node;
16697             return node;
16698         }
16699         this.selNodes.push(node);
16700         this.selMap[node.id] = node;
16701         this.lastSelNode = node;
16702         node.ui.onSelectedChange(true);
16703         this.fireEvent("selectionchange", this, this.selNodes);
16704         return node;
16705     },
16706     
16707     /**
16708      * Deselect a node.
16709      * @param {TreeNode} node The node to unselect
16710      */
16711     unselect : function(node){
16712         if(this.selMap[node.id]){
16713             node.ui.onSelectedChange(false);
16714             var sn = this.selNodes;
16715             var index = -1;
16716             if(sn.indexOf){
16717                 index = sn.indexOf(node);
16718             }else{
16719                 for(var i = 0, len = sn.length; i < len; i++){
16720                     if(sn[i] == node){
16721                         index = i;
16722                         break;
16723                     }
16724                 }
16725             }
16726             if(index != -1){
16727                 this.selNodes.splice(index, 1);
16728             }
16729             delete this.selMap[node.id];
16730             this.fireEvent("selectionchange", this, this.selNodes);
16731         }
16732     },
16733     
16734     /**
16735      * Clear all selections
16736      */
16737     clearSelections : function(suppressEvent){
16738         var sn = this.selNodes;
16739         if(sn.length > 0){
16740             for(var i = 0, len = sn.length; i < len; i++){
16741                 sn[i].ui.onSelectedChange(false);
16742             }
16743             this.selNodes = [];
16744             this.selMap = {};
16745             if(suppressEvent !== true){
16746                 this.fireEvent("selectionchange", this, this.selNodes);
16747             }
16748         }
16749     },
16750     
16751     /**
16752      * Returns true if the node is selected
16753      * @param {TreeNode} node The node to check
16754      * @return {Boolean}
16755      */
16756     isSelected : function(node){
16757         return this.selMap[node.id] ? true : false;  
16758     },
16759     
16760     /**
16761      * Returns an array of the selected nodes
16762      * @return {Array}
16763      */
16764     getSelectedNodes : function(){
16765         return this.selNodes;    
16766     },
16767
16768     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16769
16770     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16771
16772     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16773 });/*
16774  * Based on:
16775  * Ext JS Library 1.1.1
16776  * Copyright(c) 2006-2007, Ext JS, LLC.
16777  *
16778  * Originally Released Under LGPL - original licence link has changed is not relivant.
16779  *
16780  * Fork - LGPL
16781  * <script type="text/javascript">
16782  */
16783  
16784 /**
16785  * @class Roo.tree.TreeNode
16786  * @extends Roo.data.Node
16787  * @cfg {String} text The text for this node
16788  * @cfg {Boolean} expanded true to start the node expanded
16789  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16790  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16791  * @cfg {Boolean} disabled true to start the node disabled
16792  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16793  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16794  * @cfg {String} cls A css class to be added to the node
16795  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16796  * @cfg {String} href URL of the link used for the node (defaults to #)
16797  * @cfg {String} hrefTarget target frame for the link
16798  * @cfg {String} qtip An Ext QuickTip for the node
16799  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16800  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16801  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16802  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16803  * (defaults to undefined with no checkbox rendered)
16804  * @constructor
16805  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16806  */
16807 Roo.tree.TreeNode = function(attributes){
16808     attributes = attributes || {};
16809     if(typeof attributes == "string"){
16810         attributes = {text: attributes};
16811     }
16812     this.childrenRendered = false;
16813     this.rendered = false;
16814     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16815     this.expanded = attributes.expanded === true;
16816     this.isTarget = attributes.isTarget !== false;
16817     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16818     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16819
16820     /**
16821      * Read-only. The text for this node. To change it use setText().
16822      * @type String
16823      */
16824     this.text = attributes.text;
16825     /**
16826      * True if this node is disabled.
16827      * @type Boolean
16828      */
16829     this.disabled = attributes.disabled === true;
16830
16831     this.addEvents({
16832         /**
16833         * @event textchange
16834         * Fires when the text for this node is changed
16835         * @param {Node} this This node
16836         * @param {String} text The new text
16837         * @param {String} oldText The old text
16838         */
16839         "textchange" : true,
16840         /**
16841         * @event beforeexpand
16842         * Fires before this node is expanded, return false to cancel.
16843         * @param {Node} this This node
16844         * @param {Boolean} deep
16845         * @param {Boolean} anim
16846         */
16847         "beforeexpand" : true,
16848         /**
16849         * @event beforecollapse
16850         * Fires before this node is collapsed, return false to cancel.
16851         * @param {Node} this This node
16852         * @param {Boolean} deep
16853         * @param {Boolean} anim
16854         */
16855         "beforecollapse" : true,
16856         /**
16857         * @event expand
16858         * Fires when this node is expanded
16859         * @param {Node} this This node
16860         */
16861         "expand" : true,
16862         /**
16863         * @event disabledchange
16864         * Fires when the disabled status of this node changes
16865         * @param {Node} this This node
16866         * @param {Boolean} disabled
16867         */
16868         "disabledchange" : true,
16869         /**
16870         * @event collapse
16871         * Fires when this node is collapsed
16872         * @param {Node} this This node
16873         */
16874         "collapse" : true,
16875         /**
16876         * @event beforeclick
16877         * Fires before click processing. Return false to cancel the default action.
16878         * @param {Node} this This node
16879         * @param {Roo.EventObject} e The event object
16880         */
16881         "beforeclick":true,
16882         /**
16883         * @event checkchange
16884         * Fires when a node with a checkbox's checked property changes
16885         * @param {Node} this This node
16886         * @param {Boolean} checked
16887         */
16888         "checkchange":true,
16889         /**
16890         * @event click
16891         * Fires when this node is clicked
16892         * @param {Node} this This node
16893         * @param {Roo.EventObject} e The event object
16894         */
16895         "click":true,
16896         /**
16897         * @event dblclick
16898         * Fires when this node is double clicked
16899         * @param {Node} this This node
16900         * @param {Roo.EventObject} e The event object
16901         */
16902         "dblclick":true,
16903         /**
16904         * @event contextmenu
16905         * Fires when this node is right clicked
16906         * @param {Node} this This node
16907         * @param {Roo.EventObject} e The event object
16908         */
16909         "contextmenu":true,
16910         /**
16911         * @event beforechildrenrendered
16912         * Fires right before the child nodes for this node are rendered
16913         * @param {Node} this This node
16914         */
16915         "beforechildrenrendered":true
16916     });
16917
16918     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16919
16920     /**
16921      * Read-only. The UI for this node
16922      * @type TreeNodeUI
16923      */
16924     this.ui = new uiClass(this);
16925     
16926     // finally support items[]
16927     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16928         return;
16929     }
16930     
16931     
16932     Roo.each(this.attributes.items, function(c) {
16933         this.appendChild(Roo.factory(c,Roo.Tree));
16934     }, this);
16935     delete this.attributes.items;
16936     
16937     
16938     
16939 };
16940 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16941     preventHScroll: true,
16942     /**
16943      * Returns true if this node is expanded
16944      * @return {Boolean}
16945      */
16946     isExpanded : function(){
16947         return this.expanded;
16948     },
16949
16950     /**
16951      * Returns the UI object for this node
16952      * @return {TreeNodeUI}
16953      */
16954     getUI : function(){
16955         return this.ui;
16956     },
16957
16958     // private override
16959     setFirstChild : function(node){
16960         var of = this.firstChild;
16961         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16962         if(this.childrenRendered && of && node != of){
16963             of.renderIndent(true, true);
16964         }
16965         if(this.rendered){
16966             this.renderIndent(true, true);
16967         }
16968     },
16969
16970     // private override
16971     setLastChild : function(node){
16972         var ol = this.lastChild;
16973         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16974         if(this.childrenRendered && ol && node != ol){
16975             ol.renderIndent(true, true);
16976         }
16977         if(this.rendered){
16978             this.renderIndent(true, true);
16979         }
16980     },
16981
16982     // these methods are overridden to provide lazy rendering support
16983     // private override
16984     appendChild : function()
16985     {
16986         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16987         if(node && this.childrenRendered){
16988             node.render();
16989         }
16990         this.ui.updateExpandIcon();
16991         return node;
16992     },
16993
16994     // private override
16995     removeChild : function(node){
16996         this.ownerTree.getSelectionModel().unselect(node);
16997         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16998         // if it's been rendered remove dom node
16999         if(this.childrenRendered){
17000             node.ui.remove();
17001         }
17002         if(this.childNodes.length < 1){
17003             this.collapse(false, false);
17004         }else{
17005             this.ui.updateExpandIcon();
17006         }
17007         if(!this.firstChild) {
17008             this.childrenRendered = false;
17009         }
17010         return node;
17011     },
17012
17013     // private override
17014     insertBefore : function(node, refNode){
17015         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17016         if(newNode && refNode && this.childrenRendered){
17017             node.render();
17018         }
17019         this.ui.updateExpandIcon();
17020         return newNode;
17021     },
17022
17023     /**
17024      * Sets the text for this node
17025      * @param {String} text
17026      */
17027     setText : function(text){
17028         var oldText = this.text;
17029         this.text = text;
17030         this.attributes.text = text;
17031         if(this.rendered){ // event without subscribing
17032             this.ui.onTextChange(this, text, oldText);
17033         }
17034         this.fireEvent("textchange", this, text, oldText);
17035     },
17036
17037     /**
17038      * Triggers selection of this node
17039      */
17040     select : function(){
17041         this.getOwnerTree().getSelectionModel().select(this);
17042     },
17043
17044     /**
17045      * Triggers deselection of this node
17046      */
17047     unselect : function(){
17048         this.getOwnerTree().getSelectionModel().unselect(this);
17049     },
17050
17051     /**
17052      * Returns true if this node is selected
17053      * @return {Boolean}
17054      */
17055     isSelected : function(){
17056         return this.getOwnerTree().getSelectionModel().isSelected(this);
17057     },
17058
17059     /**
17060      * Expand this node.
17061      * @param {Boolean} deep (optional) True to expand all children as well
17062      * @param {Boolean} anim (optional) false to cancel the default animation
17063      * @param {Function} callback (optional) A callback to be called when
17064      * expanding this node completes (does not wait for deep expand to complete).
17065      * Called with 1 parameter, this node.
17066      */
17067     expand : function(deep, anim, callback){
17068         if(!this.expanded){
17069             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17070                 return;
17071             }
17072             if(!this.childrenRendered){
17073                 this.renderChildren();
17074             }
17075             this.expanded = true;
17076             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17077                 this.ui.animExpand(function(){
17078                     this.fireEvent("expand", this);
17079                     if(typeof callback == "function"){
17080                         callback(this);
17081                     }
17082                     if(deep === true){
17083                         this.expandChildNodes(true);
17084                     }
17085                 }.createDelegate(this));
17086                 return;
17087             }else{
17088                 this.ui.expand();
17089                 this.fireEvent("expand", this);
17090                 if(typeof callback == "function"){
17091                     callback(this);
17092                 }
17093             }
17094         }else{
17095            if(typeof callback == "function"){
17096                callback(this);
17097            }
17098         }
17099         if(deep === true){
17100             this.expandChildNodes(true);
17101         }
17102     },
17103
17104     isHiddenRoot : function(){
17105         return this.isRoot && !this.getOwnerTree().rootVisible;
17106     },
17107
17108     /**
17109      * Collapse this node.
17110      * @param {Boolean} deep (optional) True to collapse all children as well
17111      * @param {Boolean} anim (optional) false to cancel the default animation
17112      */
17113     collapse : function(deep, anim){
17114         if(this.expanded && !this.isHiddenRoot()){
17115             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17116                 return;
17117             }
17118             this.expanded = false;
17119             if((this.getOwnerTree().animate && anim !== false) || anim){
17120                 this.ui.animCollapse(function(){
17121                     this.fireEvent("collapse", this);
17122                     if(deep === true){
17123                         this.collapseChildNodes(true);
17124                     }
17125                 }.createDelegate(this));
17126                 return;
17127             }else{
17128                 this.ui.collapse();
17129                 this.fireEvent("collapse", this);
17130             }
17131         }
17132         if(deep === true){
17133             var cs = this.childNodes;
17134             for(var i = 0, len = cs.length; i < len; i++) {
17135                 cs[i].collapse(true, false);
17136             }
17137         }
17138     },
17139
17140     // private
17141     delayedExpand : function(delay){
17142         if(!this.expandProcId){
17143             this.expandProcId = this.expand.defer(delay, this);
17144         }
17145     },
17146
17147     // private
17148     cancelExpand : function(){
17149         if(this.expandProcId){
17150             clearTimeout(this.expandProcId);
17151         }
17152         this.expandProcId = false;
17153     },
17154
17155     /**
17156      * Toggles expanded/collapsed state of the node
17157      */
17158     toggle : function(){
17159         if(this.expanded){
17160             this.collapse();
17161         }else{
17162             this.expand();
17163         }
17164     },
17165
17166     /**
17167      * Ensures all parent nodes are expanded
17168      */
17169     ensureVisible : function(callback){
17170         var tree = this.getOwnerTree();
17171         tree.expandPath(this.parentNode.getPath(), false, function(){
17172             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17173             Roo.callback(callback);
17174         }.createDelegate(this));
17175     },
17176
17177     /**
17178      * Expand all child nodes
17179      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17180      */
17181     expandChildNodes : function(deep){
17182         var cs = this.childNodes;
17183         for(var i = 0, len = cs.length; i < len; i++) {
17184                 cs[i].expand(deep);
17185         }
17186     },
17187
17188     /**
17189      * Collapse all child nodes
17190      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17191      */
17192     collapseChildNodes : function(deep){
17193         var cs = this.childNodes;
17194         for(var i = 0, len = cs.length; i < len; i++) {
17195                 cs[i].collapse(deep);
17196         }
17197     },
17198
17199     /**
17200      * Disables this node
17201      */
17202     disable : function(){
17203         this.disabled = true;
17204         this.unselect();
17205         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17206             this.ui.onDisableChange(this, true);
17207         }
17208         this.fireEvent("disabledchange", this, true);
17209     },
17210
17211     /**
17212      * Enables this node
17213      */
17214     enable : function(){
17215         this.disabled = false;
17216         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17217             this.ui.onDisableChange(this, false);
17218         }
17219         this.fireEvent("disabledchange", this, false);
17220     },
17221
17222     // private
17223     renderChildren : function(suppressEvent){
17224         if(suppressEvent !== false){
17225             this.fireEvent("beforechildrenrendered", this);
17226         }
17227         var cs = this.childNodes;
17228         for(var i = 0, len = cs.length; i < len; i++){
17229             cs[i].render(true);
17230         }
17231         this.childrenRendered = true;
17232     },
17233
17234     // private
17235     sort : function(fn, scope){
17236         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17237         if(this.childrenRendered){
17238             var cs = this.childNodes;
17239             for(var i = 0, len = cs.length; i < len; i++){
17240                 cs[i].render(true);
17241             }
17242         }
17243     },
17244
17245     // private
17246     render : function(bulkRender){
17247         this.ui.render(bulkRender);
17248         if(!this.rendered){
17249             this.rendered = true;
17250             if(this.expanded){
17251                 this.expanded = false;
17252                 this.expand(false, false);
17253             }
17254         }
17255     },
17256
17257     // private
17258     renderIndent : function(deep, refresh){
17259         if(refresh){
17260             this.ui.childIndent = null;
17261         }
17262         this.ui.renderIndent();
17263         if(deep === true && this.childrenRendered){
17264             var cs = this.childNodes;
17265             for(var i = 0, len = cs.length; i < len; i++){
17266                 cs[i].renderIndent(true, refresh);
17267             }
17268         }
17269     }
17270 });/*
17271  * Based on:
17272  * Ext JS Library 1.1.1
17273  * Copyright(c) 2006-2007, Ext JS, LLC.
17274  *
17275  * Originally Released Under LGPL - original licence link has changed is not relivant.
17276  *
17277  * Fork - LGPL
17278  * <script type="text/javascript">
17279  */
17280  
17281 /**
17282  * @class Roo.tree.AsyncTreeNode
17283  * @extends Roo.tree.TreeNode
17284  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17285  * @constructor
17286  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17287  */
17288  Roo.tree.AsyncTreeNode = function(config){
17289     this.loaded = false;
17290     this.loading = false;
17291     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17292     /**
17293     * @event beforeload
17294     * Fires before this node is loaded, return false to cancel
17295     * @param {Node} this This node
17296     */
17297     this.addEvents({'beforeload':true, 'load': true});
17298     /**
17299     * @event load
17300     * Fires when this node is loaded
17301     * @param {Node} this This node
17302     */
17303     /**
17304      * The loader used by this node (defaults to using the tree's defined loader)
17305      * @type TreeLoader
17306      * @property loader
17307      */
17308 };
17309 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17310     expand : function(deep, anim, callback){
17311         if(this.loading){ // if an async load is already running, waiting til it's done
17312             var timer;
17313             var f = function(){
17314                 if(!this.loading){ // done loading
17315                     clearInterval(timer);
17316                     this.expand(deep, anim, callback);
17317                 }
17318             }.createDelegate(this);
17319             timer = setInterval(f, 200);
17320             return;
17321         }
17322         if(!this.loaded){
17323             if(this.fireEvent("beforeload", this) === false){
17324                 return;
17325             }
17326             this.loading = true;
17327             this.ui.beforeLoad(this);
17328             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17329             if(loader){
17330                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17331                 return;
17332             }
17333         }
17334         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17335     },
17336     
17337     /**
17338      * Returns true if this node is currently loading
17339      * @return {Boolean}
17340      */
17341     isLoading : function(){
17342         return this.loading;  
17343     },
17344     
17345     loadComplete : function(deep, anim, callback){
17346         this.loading = false;
17347         this.loaded = true;
17348         this.ui.afterLoad(this);
17349         this.fireEvent("load", this);
17350         this.expand(deep, anim, callback);
17351     },
17352     
17353     /**
17354      * Returns true if this node has been loaded
17355      * @return {Boolean}
17356      */
17357     isLoaded : function(){
17358         return this.loaded;
17359     },
17360     
17361     hasChildNodes : function(){
17362         if(!this.isLeaf() && !this.loaded){
17363             return true;
17364         }else{
17365             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17366         }
17367     },
17368
17369     /**
17370      * Trigger a reload for this node
17371      * @param {Function} callback
17372      */
17373     reload : function(callback){
17374         this.collapse(false, false);
17375         while(this.firstChild){
17376             this.removeChild(this.firstChild);
17377         }
17378         this.childrenRendered = false;
17379         this.loaded = false;
17380         if(this.isHiddenRoot()){
17381             this.expanded = false;
17382         }
17383         this.expand(false, false, callback);
17384     }
17385 });/*
17386  * Based on:
17387  * Ext JS Library 1.1.1
17388  * Copyright(c) 2006-2007, Ext JS, LLC.
17389  *
17390  * Originally Released Under LGPL - original licence link has changed is not relivant.
17391  *
17392  * Fork - LGPL
17393  * <script type="text/javascript">
17394  */
17395  
17396 /**
17397  * @class Roo.tree.TreeNodeUI
17398  * @constructor
17399  * @param {Object} node The node to render
17400  * The TreeNode UI implementation is separate from the
17401  * tree implementation. Unless you are customizing the tree UI,
17402  * you should never have to use this directly.
17403  */
17404 Roo.tree.TreeNodeUI = function(node){
17405     this.node = node;
17406     this.rendered = false;
17407     this.animating = false;
17408     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17409 };
17410
17411 Roo.tree.TreeNodeUI.prototype = {
17412     removeChild : function(node){
17413         if(this.rendered){
17414             this.ctNode.removeChild(node.ui.getEl());
17415         }
17416     },
17417
17418     beforeLoad : function(){
17419          this.addClass("x-tree-node-loading");
17420     },
17421
17422     afterLoad : function(){
17423          this.removeClass("x-tree-node-loading");
17424     },
17425
17426     onTextChange : function(node, text, oldText){
17427         if(this.rendered){
17428             this.textNode.innerHTML = text;
17429         }
17430     },
17431
17432     onDisableChange : function(node, state){
17433         this.disabled = state;
17434         if(state){
17435             this.addClass("x-tree-node-disabled");
17436         }else{
17437             this.removeClass("x-tree-node-disabled");
17438         }
17439     },
17440
17441     onSelectedChange : function(state){
17442         if(state){
17443             this.focus();
17444             this.addClass("x-tree-selected");
17445         }else{
17446             //this.blur();
17447             this.removeClass("x-tree-selected");
17448         }
17449     },
17450
17451     onMove : function(tree, node, oldParent, newParent, index, refNode){
17452         this.childIndent = null;
17453         if(this.rendered){
17454             var targetNode = newParent.ui.getContainer();
17455             if(!targetNode){//target not rendered
17456                 this.holder = document.createElement("div");
17457                 this.holder.appendChild(this.wrap);
17458                 return;
17459             }
17460             var insertBefore = refNode ? refNode.ui.getEl() : null;
17461             if(insertBefore){
17462                 targetNode.insertBefore(this.wrap, insertBefore);
17463             }else{
17464                 targetNode.appendChild(this.wrap);
17465             }
17466             this.node.renderIndent(true);
17467         }
17468     },
17469
17470     addClass : function(cls){
17471         if(this.elNode){
17472             Roo.fly(this.elNode).addClass(cls);
17473         }
17474     },
17475
17476     removeClass : function(cls){
17477         if(this.elNode){
17478             Roo.fly(this.elNode).removeClass(cls);
17479         }
17480     },
17481
17482     remove : function(){
17483         if(this.rendered){
17484             this.holder = document.createElement("div");
17485             this.holder.appendChild(this.wrap);
17486         }
17487     },
17488
17489     fireEvent : function(){
17490         return this.node.fireEvent.apply(this.node, arguments);
17491     },
17492
17493     initEvents : function(){
17494         this.node.on("move", this.onMove, this);
17495         var E = Roo.EventManager;
17496         var a = this.anchor;
17497
17498         var el = Roo.fly(a, '_treeui');
17499
17500         if(Roo.isOpera){ // opera render bug ignores the CSS
17501             el.setStyle("text-decoration", "none");
17502         }
17503
17504         el.on("click", this.onClick, this);
17505         el.on("dblclick", this.onDblClick, this);
17506
17507         if(this.checkbox){
17508             Roo.EventManager.on(this.checkbox,
17509                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17510         }
17511
17512         el.on("contextmenu", this.onContextMenu, this);
17513
17514         var icon = Roo.fly(this.iconNode);
17515         icon.on("click", this.onClick, this);
17516         icon.on("dblclick", this.onDblClick, this);
17517         icon.on("contextmenu", this.onContextMenu, this);
17518         E.on(this.ecNode, "click", this.ecClick, this, true);
17519
17520         if(this.node.disabled){
17521             this.addClass("x-tree-node-disabled");
17522         }
17523         if(this.node.hidden){
17524             this.addClass("x-tree-node-disabled");
17525         }
17526         var ot = this.node.getOwnerTree();
17527         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17528         if(dd && (!this.node.isRoot || ot.rootVisible)){
17529             Roo.dd.Registry.register(this.elNode, {
17530                 node: this.node,
17531                 handles: this.getDDHandles(),
17532                 isHandle: false
17533             });
17534         }
17535     },
17536
17537     getDDHandles : function(){
17538         return [this.iconNode, this.textNode];
17539     },
17540
17541     hide : function(){
17542         if(this.rendered){
17543             this.wrap.style.display = "none";
17544         }
17545     },
17546
17547     show : function(){
17548         if(this.rendered){
17549             this.wrap.style.display = "";
17550         }
17551     },
17552
17553     onContextMenu : function(e){
17554         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17555             e.preventDefault();
17556             this.focus();
17557             this.fireEvent("contextmenu", this.node, e);
17558         }
17559     },
17560
17561     onClick : function(e){
17562         if(this.dropping){
17563             e.stopEvent();
17564             return;
17565         }
17566         if(this.fireEvent("beforeclick", this.node, e) !== false){
17567             if(!this.disabled && this.node.attributes.href){
17568                 this.fireEvent("click", this.node, e);
17569                 return;
17570             }
17571             e.preventDefault();
17572             if(this.disabled){
17573                 return;
17574             }
17575
17576             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17577                 this.node.toggle();
17578             }
17579
17580             this.fireEvent("click", this.node, e);
17581         }else{
17582             e.stopEvent();
17583         }
17584     },
17585
17586     onDblClick : function(e){
17587         e.preventDefault();
17588         if(this.disabled){
17589             return;
17590         }
17591         if(this.checkbox){
17592             this.toggleCheck();
17593         }
17594         if(!this.animating && this.node.hasChildNodes()){
17595             this.node.toggle();
17596         }
17597         this.fireEvent("dblclick", this.node, e);
17598     },
17599
17600     onCheckChange : function(){
17601         var checked = this.checkbox.checked;
17602         this.node.attributes.checked = checked;
17603         this.fireEvent('checkchange', this.node, checked);
17604     },
17605
17606     ecClick : function(e){
17607         if(!this.animating && this.node.hasChildNodes()){
17608             this.node.toggle();
17609         }
17610     },
17611
17612     startDrop : function(){
17613         this.dropping = true;
17614     },
17615
17616     // delayed drop so the click event doesn't get fired on a drop
17617     endDrop : function(){
17618        setTimeout(function(){
17619            this.dropping = false;
17620        }.createDelegate(this), 50);
17621     },
17622
17623     expand : function(){
17624         this.updateExpandIcon();
17625         this.ctNode.style.display = "";
17626     },
17627
17628     focus : function(){
17629         if(!this.node.preventHScroll){
17630             try{this.anchor.focus();
17631             }catch(e){}
17632         }else if(!Roo.isIE){
17633             try{
17634                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17635                 var l = noscroll.scrollLeft;
17636                 this.anchor.focus();
17637                 noscroll.scrollLeft = l;
17638             }catch(e){}
17639         }
17640     },
17641
17642     toggleCheck : function(value){
17643         var cb = this.checkbox;
17644         if(cb){
17645             cb.checked = (value === undefined ? !cb.checked : value);
17646         }
17647     },
17648
17649     blur : function(){
17650         try{
17651             this.anchor.blur();
17652         }catch(e){}
17653     },
17654
17655     animExpand : function(callback){
17656         var ct = Roo.get(this.ctNode);
17657         ct.stopFx();
17658         if(!this.node.hasChildNodes()){
17659             this.updateExpandIcon();
17660             this.ctNode.style.display = "";
17661             Roo.callback(callback);
17662             return;
17663         }
17664         this.animating = true;
17665         this.updateExpandIcon();
17666
17667         ct.slideIn('t', {
17668            callback : function(){
17669                this.animating = false;
17670                Roo.callback(callback);
17671             },
17672             scope: this,
17673             duration: this.node.ownerTree.duration || .25
17674         });
17675     },
17676
17677     highlight : function(){
17678         var tree = this.node.getOwnerTree();
17679         Roo.fly(this.wrap).highlight(
17680             tree.hlColor || "C3DAF9",
17681             {endColor: tree.hlBaseColor}
17682         );
17683     },
17684
17685     collapse : function(){
17686         this.updateExpandIcon();
17687         this.ctNode.style.display = "none";
17688     },
17689
17690     animCollapse : function(callback){
17691         var ct = Roo.get(this.ctNode);
17692         ct.enableDisplayMode('block');
17693         ct.stopFx();
17694
17695         this.animating = true;
17696         this.updateExpandIcon();
17697
17698         ct.slideOut('t', {
17699             callback : function(){
17700                this.animating = false;
17701                Roo.callback(callback);
17702             },
17703             scope: this,
17704             duration: this.node.ownerTree.duration || .25
17705         });
17706     },
17707
17708     getContainer : function(){
17709         return this.ctNode;
17710     },
17711
17712     getEl : function(){
17713         return this.wrap;
17714     },
17715
17716     appendDDGhost : function(ghostNode){
17717         ghostNode.appendChild(this.elNode.cloneNode(true));
17718     },
17719
17720     getDDRepairXY : function(){
17721         return Roo.lib.Dom.getXY(this.iconNode);
17722     },
17723
17724     onRender : function(){
17725         this.render();
17726     },
17727
17728     render : function(bulkRender){
17729         var n = this.node, a = n.attributes;
17730         var targetNode = n.parentNode ?
17731               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17732
17733         if(!this.rendered){
17734             this.rendered = true;
17735
17736             this.renderElements(n, a, targetNode, bulkRender);
17737
17738             if(a.qtip){
17739                if(this.textNode.setAttributeNS){
17740                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17741                    if(a.qtipTitle){
17742                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17743                    }
17744                }else{
17745                    this.textNode.setAttribute("ext:qtip", a.qtip);
17746                    if(a.qtipTitle){
17747                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17748                    }
17749                }
17750             }else if(a.qtipCfg){
17751                 a.qtipCfg.target = Roo.id(this.textNode);
17752                 Roo.QuickTips.register(a.qtipCfg);
17753             }
17754             this.initEvents();
17755             if(!this.node.expanded){
17756                 this.updateExpandIcon();
17757             }
17758         }else{
17759             if(bulkRender === true) {
17760                 targetNode.appendChild(this.wrap);
17761             }
17762         }
17763     },
17764
17765     renderElements : function(n, a, targetNode, bulkRender)
17766     {
17767         // add some indent caching, this helps performance when rendering a large tree
17768         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17769         var t = n.getOwnerTree();
17770         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17771         if (typeof(n.attributes.html) != 'undefined') {
17772             txt = n.attributes.html;
17773         }
17774         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17775         var cb = typeof a.checked == 'boolean';
17776         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17777         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17778             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17779             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17780             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17781             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17782             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17783              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17784                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17785             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17786             "</li>"];
17787
17788         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17789             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17790                                 n.nextSibling.ui.getEl(), buf.join(""));
17791         }else{
17792             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17793         }
17794
17795         this.elNode = this.wrap.childNodes[0];
17796         this.ctNode = this.wrap.childNodes[1];
17797         var cs = this.elNode.childNodes;
17798         this.indentNode = cs[0];
17799         this.ecNode = cs[1];
17800         this.iconNode = cs[2];
17801         var index = 3;
17802         if(cb){
17803             this.checkbox = cs[3];
17804             index++;
17805         }
17806         this.anchor = cs[index];
17807         this.textNode = cs[index].firstChild;
17808     },
17809
17810     getAnchor : function(){
17811         return this.anchor;
17812     },
17813
17814     getTextEl : function(){
17815         return this.textNode;
17816     },
17817
17818     getIconEl : function(){
17819         return this.iconNode;
17820     },
17821
17822     isChecked : function(){
17823         return this.checkbox ? this.checkbox.checked : false;
17824     },
17825
17826     updateExpandIcon : function(){
17827         if(this.rendered){
17828             var n = this.node, c1, c2;
17829             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17830             var hasChild = n.hasChildNodes();
17831             if(hasChild){
17832                 if(n.expanded){
17833                     cls += "-minus";
17834                     c1 = "x-tree-node-collapsed";
17835                     c2 = "x-tree-node-expanded";
17836                 }else{
17837                     cls += "-plus";
17838                     c1 = "x-tree-node-expanded";
17839                     c2 = "x-tree-node-collapsed";
17840                 }
17841                 if(this.wasLeaf){
17842                     this.removeClass("x-tree-node-leaf");
17843                     this.wasLeaf = false;
17844                 }
17845                 if(this.c1 != c1 || this.c2 != c2){
17846                     Roo.fly(this.elNode).replaceClass(c1, c2);
17847                     this.c1 = c1; this.c2 = c2;
17848                 }
17849             }else{
17850                 // this changes non-leafs into leafs if they have no children.
17851                 // it's not very rational behaviour..
17852                 
17853                 if(!this.wasLeaf && this.node.leaf){
17854                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17855                     delete this.c1;
17856                     delete this.c2;
17857                     this.wasLeaf = true;
17858                 }
17859             }
17860             var ecc = "x-tree-ec-icon "+cls;
17861             if(this.ecc != ecc){
17862                 this.ecNode.className = ecc;
17863                 this.ecc = ecc;
17864             }
17865         }
17866     },
17867
17868     getChildIndent : function(){
17869         if(!this.childIndent){
17870             var buf = [];
17871             var p = this.node;
17872             while(p){
17873                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17874                     if(!p.isLast()) {
17875                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17876                     } else {
17877                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17878                     }
17879                 }
17880                 p = p.parentNode;
17881             }
17882             this.childIndent = buf.join("");
17883         }
17884         return this.childIndent;
17885     },
17886
17887     renderIndent : function(){
17888         if(this.rendered){
17889             var indent = "";
17890             var p = this.node.parentNode;
17891             if(p){
17892                 indent = p.ui.getChildIndent();
17893             }
17894             if(this.indentMarkup != indent){ // don't rerender if not required
17895                 this.indentNode.innerHTML = indent;
17896                 this.indentMarkup = indent;
17897             }
17898             this.updateExpandIcon();
17899         }
17900     }
17901 };
17902
17903 Roo.tree.RootTreeNodeUI = function(){
17904     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17905 };
17906 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17907     render : function(){
17908         if(!this.rendered){
17909             var targetNode = this.node.ownerTree.innerCt.dom;
17910             this.node.expanded = true;
17911             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17912             this.wrap = this.ctNode = targetNode.firstChild;
17913         }
17914     },
17915     collapse : function(){
17916     },
17917     expand : function(){
17918     }
17919 });/*
17920  * Based on:
17921  * Ext JS Library 1.1.1
17922  * Copyright(c) 2006-2007, Ext JS, LLC.
17923  *
17924  * Originally Released Under LGPL - original licence link has changed is not relivant.
17925  *
17926  * Fork - LGPL
17927  * <script type="text/javascript">
17928  */
17929 /**
17930  * @class Roo.tree.TreeLoader
17931  * @extends Roo.util.Observable
17932  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17933  * nodes from a specified URL. The response must be a javascript Array definition
17934  * who's elements are node definition objects. eg:
17935  * <pre><code>
17936 {  success : true,
17937    data :      [
17938    
17939     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17940     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17941     ]
17942 }
17943
17944
17945 </code></pre>
17946  * <br><br>
17947  * The old style respose with just an array is still supported, but not recommended.
17948  * <br><br>
17949  *
17950  * A server request is sent, and child nodes are loaded only when a node is expanded.
17951  * The loading node's id is passed to the server under the parameter name "node" to
17952  * enable the server to produce the correct child nodes.
17953  * <br><br>
17954  * To pass extra parameters, an event handler may be attached to the "beforeload"
17955  * event, and the parameters specified in the TreeLoader's baseParams property:
17956  * <pre><code>
17957     myTreeLoader.on("beforeload", function(treeLoader, node) {
17958         this.baseParams.category = node.attributes.category;
17959     }, this);
17960 </code></pre><
17961  * This would pass an HTTP parameter called "category" to the server containing
17962  * the value of the Node's "category" attribute.
17963  * @constructor
17964  * Creates a new Treeloader.
17965  * @param {Object} config A config object containing config properties.
17966  */
17967 Roo.tree.TreeLoader = function(config){
17968     this.baseParams = {};
17969     this.requestMethod = "POST";
17970     Roo.apply(this, config);
17971
17972     this.addEvents({
17973     
17974         /**
17975          * @event beforeload
17976          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17977          * @param {Object} This TreeLoader object.
17978          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17979          * @param {Object} callback The callback function specified in the {@link #load} call.
17980          */
17981         beforeload : true,
17982         /**
17983          * @event load
17984          * Fires when the node has been successfuly loaded.
17985          * @param {Object} This TreeLoader object.
17986          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17987          * @param {Object} response The response object containing the data from the server.
17988          */
17989         load : true,
17990         /**
17991          * @event loadexception
17992          * Fires if the network request failed.
17993          * @param {Object} This TreeLoader object.
17994          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17995          * @param {Object} response The response object containing the data from the server.
17996          */
17997         loadexception : true,
17998         /**
17999          * @event create
18000          * Fires before a node is created, enabling you to return custom Node types 
18001          * @param {Object} This TreeLoader object.
18002          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18003          */
18004         create : true
18005     });
18006
18007     Roo.tree.TreeLoader.superclass.constructor.call(this);
18008 };
18009
18010 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18011     /**
18012     * @cfg {String} dataUrl The URL from which to request a Json string which
18013     * specifies an array of node definition object representing the child nodes
18014     * to be loaded.
18015     */
18016     /**
18017     * @cfg {String} requestMethod either GET or POST
18018     * defaults to POST (due to BC)
18019     * to be loaded.
18020     */
18021     /**
18022     * @cfg {Object} baseParams (optional) An object containing properties which
18023     * specify HTTP parameters to be passed to each request for child nodes.
18024     */
18025     /**
18026     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18027     * created by this loader. If the attributes sent by the server have an attribute in this object,
18028     * they take priority.
18029     */
18030     /**
18031     * @cfg {Object} uiProviders (optional) An object containing properties which
18032     * 
18033     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18034     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18035     * <i>uiProvider</i> attribute of a returned child node is a string rather
18036     * than a reference to a TreeNodeUI implementation, this that string value
18037     * is used as a property name in the uiProviders object. You can define the provider named
18038     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18039     */
18040     uiProviders : {},
18041
18042     /**
18043     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18044     * child nodes before loading.
18045     */
18046     clearOnLoad : true,
18047
18048     /**
18049     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18050     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18051     * Grid query { data : [ .....] }
18052     */
18053     
18054     root : false,
18055      /**
18056     * @cfg {String} queryParam (optional) 
18057     * Name of the query as it will be passed on the querystring (defaults to 'node')
18058     * eg. the request will be ?node=[id]
18059     */
18060     
18061     
18062     queryParam: false,
18063     
18064     /**
18065      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18066      * This is called automatically when a node is expanded, but may be used to reload
18067      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18068      * @param {Roo.tree.TreeNode} node
18069      * @param {Function} callback
18070      */
18071     load : function(node, callback){
18072         if(this.clearOnLoad){
18073             while(node.firstChild){
18074                 node.removeChild(node.firstChild);
18075             }
18076         }
18077         if(node.attributes.children){ // preloaded json children
18078             var cs = node.attributes.children;
18079             for(var i = 0, len = cs.length; i < len; i++){
18080                 node.appendChild(this.createNode(cs[i]));
18081             }
18082             if(typeof callback == "function"){
18083                 callback();
18084             }
18085         }else if(this.dataUrl){
18086             this.requestData(node, callback);
18087         }
18088     },
18089
18090     getParams: function(node){
18091         var buf = [], bp = this.baseParams;
18092         for(var key in bp){
18093             if(typeof bp[key] != "function"){
18094                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18095             }
18096         }
18097         var n = this.queryParam === false ? 'node' : this.queryParam;
18098         buf.push(n + "=", encodeURIComponent(node.id));
18099         return buf.join("");
18100     },
18101
18102     requestData : function(node, callback){
18103         if(this.fireEvent("beforeload", this, node, callback) !== false){
18104             this.transId = Roo.Ajax.request({
18105                 method:this.requestMethod,
18106                 url: this.dataUrl||this.url,
18107                 success: this.handleResponse,
18108                 failure: this.handleFailure,
18109                 scope: this,
18110                 argument: {callback: callback, node: node},
18111                 params: this.getParams(node)
18112             });
18113         }else{
18114             // if the load is cancelled, make sure we notify
18115             // the node that we are done
18116             if(typeof callback == "function"){
18117                 callback();
18118             }
18119         }
18120     },
18121
18122     isLoading : function(){
18123         return this.transId ? true : false;
18124     },
18125
18126     abort : function(){
18127         if(this.isLoading()){
18128             Roo.Ajax.abort(this.transId);
18129         }
18130     },
18131
18132     // private
18133     createNode : function(attr)
18134     {
18135         // apply baseAttrs, nice idea Corey!
18136         if(this.baseAttrs){
18137             Roo.applyIf(attr, this.baseAttrs);
18138         }
18139         if(this.applyLoader !== false){
18140             attr.loader = this;
18141         }
18142         // uiProvider = depreciated..
18143         
18144         if(typeof(attr.uiProvider) == 'string'){
18145            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18146                 /**  eval:var:attr */ eval(attr.uiProvider);
18147         }
18148         if(typeof(this.uiProviders['default']) != 'undefined') {
18149             attr.uiProvider = this.uiProviders['default'];
18150         }
18151         
18152         this.fireEvent('create', this, attr);
18153         
18154         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18155         return(attr.leaf ?
18156                         new Roo.tree.TreeNode(attr) :
18157                         new Roo.tree.AsyncTreeNode(attr));
18158     },
18159
18160     processResponse : function(response, node, callback)
18161     {
18162         var json = response.responseText;
18163         try {
18164             
18165             var o = Roo.decode(json);
18166             
18167             if (this.root === false && typeof(o.success) != undefined) {
18168                 this.root = 'data'; // the default behaviour for list like data..
18169                 }
18170                 
18171             if (this.root !== false &&  !o.success) {
18172                 // it's a failure condition.
18173                 var a = response.argument;
18174                 this.fireEvent("loadexception", this, a.node, response);
18175                 Roo.log("Load failed - should have a handler really");
18176                 return;
18177             }
18178             
18179             
18180             
18181             if (this.root !== false) {
18182                  o = o[this.root];
18183             }
18184             
18185             for(var i = 0, len = o.length; i < len; i++){
18186                 var n = this.createNode(o[i]);
18187                 if(n){
18188                     node.appendChild(n);
18189                 }
18190             }
18191             if(typeof callback == "function"){
18192                 callback(this, node);
18193             }
18194         }catch(e){
18195             this.handleFailure(response);
18196         }
18197     },
18198
18199     handleResponse : function(response){
18200         this.transId = false;
18201         var a = response.argument;
18202         this.processResponse(response, a.node, a.callback);
18203         this.fireEvent("load", this, a.node, response);
18204     },
18205
18206     handleFailure : function(response)
18207     {
18208         // should handle failure better..
18209         this.transId = false;
18210         var a = response.argument;
18211         this.fireEvent("loadexception", this, a.node, response);
18212         if(typeof a.callback == "function"){
18213             a.callback(this, a.node);
18214         }
18215     }
18216 });/*
18217  * Based on:
18218  * Ext JS Library 1.1.1
18219  * Copyright(c) 2006-2007, Ext JS, LLC.
18220  *
18221  * Originally Released Under LGPL - original licence link has changed is not relivant.
18222  *
18223  * Fork - LGPL
18224  * <script type="text/javascript">
18225  */
18226
18227 /**
18228 * @class Roo.tree.TreeFilter
18229 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18230 * @param {TreePanel} tree
18231 * @param {Object} config (optional)
18232  */
18233 Roo.tree.TreeFilter = function(tree, config){
18234     this.tree = tree;
18235     this.filtered = {};
18236     Roo.apply(this, config);
18237 };
18238
18239 Roo.tree.TreeFilter.prototype = {
18240     clearBlank:false,
18241     reverse:false,
18242     autoClear:false,
18243     remove:false,
18244
18245      /**
18246      * Filter the data by a specific attribute.
18247      * @param {String/RegExp} value Either string that the attribute value
18248      * should start with or a RegExp to test against the attribute
18249      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18250      * @param {TreeNode} startNode (optional) The node to start the filter at.
18251      */
18252     filter : function(value, attr, startNode){
18253         attr = attr || "text";
18254         var f;
18255         if(typeof value == "string"){
18256             var vlen = value.length;
18257             // auto clear empty filter
18258             if(vlen == 0 && this.clearBlank){
18259                 this.clear();
18260                 return;
18261             }
18262             value = value.toLowerCase();
18263             f = function(n){
18264                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18265             };
18266         }else if(value.exec){ // regex?
18267             f = function(n){
18268                 return value.test(n.attributes[attr]);
18269             };
18270         }else{
18271             throw 'Illegal filter type, must be string or regex';
18272         }
18273         this.filterBy(f, null, startNode);
18274         },
18275
18276     /**
18277      * Filter by a function. The passed function will be called with each
18278      * node in the tree (or from the startNode). If the function returns true, the node is kept
18279      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18280      * @param {Function} fn The filter function
18281      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18282      */
18283     filterBy : function(fn, scope, startNode){
18284         startNode = startNode || this.tree.root;
18285         if(this.autoClear){
18286             this.clear();
18287         }
18288         var af = this.filtered, rv = this.reverse;
18289         var f = function(n){
18290             if(n == startNode){
18291                 return true;
18292             }
18293             if(af[n.id]){
18294                 return false;
18295             }
18296             var m = fn.call(scope || n, n);
18297             if(!m || rv){
18298                 af[n.id] = n;
18299                 n.ui.hide();
18300                 return false;
18301             }
18302             return true;
18303         };
18304         startNode.cascade(f);
18305         if(this.remove){
18306            for(var id in af){
18307                if(typeof id != "function"){
18308                    var n = af[id];
18309                    if(n && n.parentNode){
18310                        n.parentNode.removeChild(n);
18311                    }
18312                }
18313            }
18314         }
18315     },
18316
18317     /**
18318      * Clears the current filter. Note: with the "remove" option
18319      * set a filter cannot be cleared.
18320      */
18321     clear : function(){
18322         var t = this.tree;
18323         var af = this.filtered;
18324         for(var id in af){
18325             if(typeof id != "function"){
18326                 var n = af[id];
18327                 if(n){
18328                     n.ui.show();
18329                 }
18330             }
18331         }
18332         this.filtered = {};
18333     }
18334 };
18335 /*
18336  * Based on:
18337  * Ext JS Library 1.1.1
18338  * Copyright(c) 2006-2007, Ext JS, LLC.
18339  *
18340  * Originally Released Under LGPL - original licence link has changed is not relivant.
18341  *
18342  * Fork - LGPL
18343  * <script type="text/javascript">
18344  */
18345  
18346
18347 /**
18348  * @class Roo.tree.TreeSorter
18349  * Provides sorting of nodes in a TreePanel
18350  * 
18351  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18352  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18353  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18354  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18355  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18356  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18357  * @constructor
18358  * @param {TreePanel} tree
18359  * @param {Object} config
18360  */
18361 Roo.tree.TreeSorter = function(tree, config){
18362     Roo.apply(this, config);
18363     tree.on("beforechildrenrendered", this.doSort, this);
18364     tree.on("append", this.updateSort, this);
18365     tree.on("insert", this.updateSort, this);
18366     
18367     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18368     var p = this.property || "text";
18369     var sortType = this.sortType;
18370     var fs = this.folderSort;
18371     var cs = this.caseSensitive === true;
18372     var leafAttr = this.leafAttr || 'leaf';
18373
18374     this.sortFn = function(n1, n2){
18375         if(fs){
18376             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18377                 return 1;
18378             }
18379             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18380                 return -1;
18381             }
18382         }
18383         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18384         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18385         if(v1 < v2){
18386                         return dsc ? +1 : -1;
18387                 }else if(v1 > v2){
18388                         return dsc ? -1 : +1;
18389         }else{
18390                 return 0;
18391         }
18392     };
18393 };
18394
18395 Roo.tree.TreeSorter.prototype = {
18396     doSort : function(node){
18397         node.sort(this.sortFn);
18398     },
18399     
18400     compareNodes : function(n1, n2){
18401         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18402     },
18403     
18404     updateSort : function(tree, node){
18405         if(node.childrenRendered){
18406             this.doSort.defer(1, this, [node]);
18407         }
18408     }
18409 };/*
18410  * Based on:
18411  * Ext JS Library 1.1.1
18412  * Copyright(c) 2006-2007, Ext JS, LLC.
18413  *
18414  * Originally Released Under LGPL - original licence link has changed is not relivant.
18415  *
18416  * Fork - LGPL
18417  * <script type="text/javascript">
18418  */
18419
18420 if(Roo.dd.DropZone){
18421     
18422 Roo.tree.TreeDropZone = function(tree, config){
18423     this.allowParentInsert = false;
18424     this.allowContainerDrop = false;
18425     this.appendOnly = false;
18426     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18427     this.tree = tree;
18428     this.lastInsertClass = "x-tree-no-status";
18429     this.dragOverData = {};
18430 };
18431
18432 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18433     ddGroup : "TreeDD",
18434     scroll:  true,
18435     
18436     expandDelay : 1000,
18437     
18438     expandNode : function(node){
18439         if(node.hasChildNodes() && !node.isExpanded()){
18440             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18441         }
18442     },
18443     
18444     queueExpand : function(node){
18445         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18446     },
18447     
18448     cancelExpand : function(){
18449         if(this.expandProcId){
18450             clearTimeout(this.expandProcId);
18451             this.expandProcId = false;
18452         }
18453     },
18454     
18455     isValidDropPoint : function(n, pt, dd, e, data){
18456         if(!n || !data){ return false; }
18457         var targetNode = n.node;
18458         var dropNode = data.node;
18459         // default drop rules
18460         if(!(targetNode && targetNode.isTarget && pt)){
18461             return false;
18462         }
18463         if(pt == "append" && targetNode.allowChildren === false){
18464             return false;
18465         }
18466         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18467             return false;
18468         }
18469         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18470             return false;
18471         }
18472         // reuse the object
18473         var overEvent = this.dragOverData;
18474         overEvent.tree = this.tree;
18475         overEvent.target = targetNode;
18476         overEvent.data = data;
18477         overEvent.point = pt;
18478         overEvent.source = dd;
18479         overEvent.rawEvent = e;
18480         overEvent.dropNode = dropNode;
18481         overEvent.cancel = false;  
18482         var result = this.tree.fireEvent("nodedragover", overEvent);
18483         return overEvent.cancel === false && result !== false;
18484     },
18485     
18486     getDropPoint : function(e, n, dd)
18487     {
18488         var tn = n.node;
18489         if(tn.isRoot){
18490             return tn.allowChildren !== false ? "append" : false; // always append for root
18491         }
18492         var dragEl = n.ddel;
18493         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18494         var y = Roo.lib.Event.getPageY(e);
18495         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18496         
18497         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18498         var noAppend = tn.allowChildren === false;
18499         if(this.appendOnly || tn.parentNode.allowChildren === false){
18500             return noAppend ? false : "append";
18501         }
18502         var noBelow = false;
18503         if(!this.allowParentInsert){
18504             noBelow = tn.hasChildNodes() && tn.isExpanded();
18505         }
18506         var q = (b - t) / (noAppend ? 2 : 3);
18507         if(y >= t && y < (t + q)){
18508             return "above";
18509         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18510             return "below";
18511         }else{
18512             return "append";
18513         }
18514     },
18515     
18516     onNodeEnter : function(n, dd, e, data)
18517     {
18518         this.cancelExpand();
18519     },
18520     
18521     onNodeOver : function(n, dd, e, data)
18522     {
18523        
18524         var pt = this.getDropPoint(e, n, dd);
18525         var node = n.node;
18526         
18527         // auto node expand check
18528         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18529             this.queueExpand(node);
18530         }else if(pt != "append"){
18531             this.cancelExpand();
18532         }
18533         
18534         // set the insert point style on the target node
18535         var returnCls = this.dropNotAllowed;
18536         if(this.isValidDropPoint(n, pt, dd, e, data)){
18537            if(pt){
18538                var el = n.ddel;
18539                var cls;
18540                if(pt == "above"){
18541                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18542                    cls = "x-tree-drag-insert-above";
18543                }else if(pt == "below"){
18544                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18545                    cls = "x-tree-drag-insert-below";
18546                }else{
18547                    returnCls = "x-tree-drop-ok-append";
18548                    cls = "x-tree-drag-append";
18549                }
18550                if(this.lastInsertClass != cls){
18551                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18552                    this.lastInsertClass = cls;
18553                }
18554            }
18555        }
18556        return returnCls;
18557     },
18558     
18559     onNodeOut : function(n, dd, e, data){
18560         
18561         this.cancelExpand();
18562         this.removeDropIndicators(n);
18563     },
18564     
18565     onNodeDrop : function(n, dd, e, data){
18566         var point = this.getDropPoint(e, n, dd);
18567         var targetNode = n.node;
18568         targetNode.ui.startDrop();
18569         if(!this.isValidDropPoint(n, point, dd, e, data)){
18570             targetNode.ui.endDrop();
18571             return false;
18572         }
18573         // first try to find the drop node
18574         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18575         var dropEvent = {
18576             tree : this.tree,
18577             target: targetNode,
18578             data: data,
18579             point: point,
18580             source: dd,
18581             rawEvent: e,
18582             dropNode: dropNode,
18583             cancel: !dropNode   
18584         };
18585         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18586         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18587             targetNode.ui.endDrop();
18588             return false;
18589         }
18590         // allow target changing
18591         targetNode = dropEvent.target;
18592         if(point == "append" && !targetNode.isExpanded()){
18593             targetNode.expand(false, null, function(){
18594                 this.completeDrop(dropEvent);
18595             }.createDelegate(this));
18596         }else{
18597             this.completeDrop(dropEvent);
18598         }
18599         return true;
18600     },
18601     
18602     completeDrop : function(de){
18603         var ns = de.dropNode, p = de.point, t = de.target;
18604         if(!(ns instanceof Array)){
18605             ns = [ns];
18606         }
18607         var n;
18608         for(var i = 0, len = ns.length; i < len; i++){
18609             n = ns[i];
18610             if(p == "above"){
18611                 t.parentNode.insertBefore(n, t);
18612             }else if(p == "below"){
18613                 t.parentNode.insertBefore(n, t.nextSibling);
18614             }else{
18615                 t.appendChild(n);
18616             }
18617         }
18618         n.ui.focus();
18619         if(this.tree.hlDrop){
18620             n.ui.highlight();
18621         }
18622         t.ui.endDrop();
18623         this.tree.fireEvent("nodedrop", de);
18624     },
18625     
18626     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18627         if(this.tree.hlDrop){
18628             dropNode.ui.focus();
18629             dropNode.ui.highlight();
18630         }
18631         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18632     },
18633     
18634     getTree : function(){
18635         return this.tree;
18636     },
18637     
18638     removeDropIndicators : function(n){
18639         if(n && n.ddel){
18640             var el = n.ddel;
18641             Roo.fly(el).removeClass([
18642                     "x-tree-drag-insert-above",
18643                     "x-tree-drag-insert-below",
18644                     "x-tree-drag-append"]);
18645             this.lastInsertClass = "_noclass";
18646         }
18647     },
18648     
18649     beforeDragDrop : function(target, e, id){
18650         this.cancelExpand();
18651         return true;
18652     },
18653     
18654     afterRepair : function(data){
18655         if(data && Roo.enableFx){
18656             data.node.ui.highlight();
18657         }
18658         this.hideProxy();
18659     } 
18660     
18661 });
18662
18663 }
18664 /*
18665  * Based on:
18666  * Ext JS Library 1.1.1
18667  * Copyright(c) 2006-2007, Ext JS, LLC.
18668  *
18669  * Originally Released Under LGPL - original licence link has changed is not relivant.
18670  *
18671  * Fork - LGPL
18672  * <script type="text/javascript">
18673  */
18674  
18675
18676 if(Roo.dd.DragZone){
18677 Roo.tree.TreeDragZone = function(tree, config){
18678     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18679     this.tree = tree;
18680 };
18681
18682 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18683     ddGroup : "TreeDD",
18684    
18685     onBeforeDrag : function(data, e){
18686         var n = data.node;
18687         return n && n.draggable && !n.disabled;
18688     },
18689      
18690     
18691     onInitDrag : function(e){
18692         var data = this.dragData;
18693         this.tree.getSelectionModel().select(data.node);
18694         this.proxy.update("");
18695         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18696         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18697     },
18698     
18699     getRepairXY : function(e, data){
18700         return data.node.ui.getDDRepairXY();
18701     },
18702     
18703     onEndDrag : function(data, e){
18704         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18705         
18706         
18707     },
18708     
18709     onValidDrop : function(dd, e, id){
18710         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18711         this.hideProxy();
18712     },
18713     
18714     beforeInvalidDrop : function(e, id){
18715         // this scrolls the original position back into view
18716         var sm = this.tree.getSelectionModel();
18717         sm.clearSelections();
18718         sm.select(this.dragData.node);
18719     }
18720 });
18721 }/*
18722  * Based on:
18723  * Ext JS Library 1.1.1
18724  * Copyright(c) 2006-2007, Ext JS, LLC.
18725  *
18726  * Originally Released Under LGPL - original licence link has changed is not relivant.
18727  *
18728  * Fork - LGPL
18729  * <script type="text/javascript">
18730  */
18731 /**
18732  * @class Roo.tree.TreeEditor
18733  * @extends Roo.Editor
18734  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18735  * as the editor field.
18736  * @constructor
18737  * @param {Object} config (used to be the tree panel.)
18738  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18739  * 
18740  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18741  * @cfg {Roo.form.TextField|Object} field The field configuration
18742  *
18743  * 
18744  */
18745 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18746     var tree = config;
18747     var field;
18748     if (oldconfig) { // old style..
18749         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18750     } else {
18751         // new style..
18752         tree = config.tree;
18753         config.field = config.field  || {};
18754         config.field.xtype = 'TextField';
18755         field = Roo.factory(config.field, Roo.form);
18756     }
18757     config = config || {};
18758     
18759     
18760     this.addEvents({
18761         /**
18762          * @event beforenodeedit
18763          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18764          * false from the handler of this event.
18765          * @param {Editor} this
18766          * @param {Roo.tree.Node} node 
18767          */
18768         "beforenodeedit" : true
18769     });
18770     
18771     //Roo.log(config);
18772     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18773
18774     this.tree = tree;
18775
18776     tree.on('beforeclick', this.beforeNodeClick, this);
18777     tree.getTreeEl().on('mousedown', this.hide, this);
18778     this.on('complete', this.updateNode, this);
18779     this.on('beforestartedit', this.fitToTree, this);
18780     this.on('startedit', this.bindScroll, this, {delay:10});
18781     this.on('specialkey', this.onSpecialKey, this);
18782 };
18783
18784 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18785     /**
18786      * @cfg {String} alignment
18787      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18788      */
18789     alignment: "l-l",
18790     // inherit
18791     autoSize: false,
18792     /**
18793      * @cfg {Boolean} hideEl
18794      * True to hide the bound element while the editor is displayed (defaults to false)
18795      */
18796     hideEl : false,
18797     /**
18798      * @cfg {String} cls
18799      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18800      */
18801     cls: "x-small-editor x-tree-editor",
18802     /**
18803      * @cfg {Boolean} shim
18804      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18805      */
18806     shim:false,
18807     // inherit
18808     shadow:"frame",
18809     /**
18810      * @cfg {Number} maxWidth
18811      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18812      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18813      * scroll and client offsets into account prior to each edit.
18814      */
18815     maxWidth: 250,
18816
18817     editDelay : 350,
18818
18819     // private
18820     fitToTree : function(ed, el){
18821         var td = this.tree.getTreeEl().dom, nd = el.dom;
18822         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18823             td.scrollLeft = nd.offsetLeft;
18824         }
18825         var w = Math.min(
18826                 this.maxWidth,
18827                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18828         this.setSize(w, '');
18829         
18830         return this.fireEvent('beforenodeedit', this, this.editNode);
18831         
18832     },
18833
18834     // private
18835     triggerEdit : function(node){
18836         this.completeEdit();
18837         this.editNode = node;
18838         this.startEdit(node.ui.textNode, node.text);
18839     },
18840
18841     // private
18842     bindScroll : function(){
18843         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18844     },
18845
18846     // private
18847     beforeNodeClick : function(node, e){
18848         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18849         this.lastClick = new Date();
18850         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18851             e.stopEvent();
18852             this.triggerEdit(node);
18853             return false;
18854         }
18855         return true;
18856     },
18857
18858     // private
18859     updateNode : function(ed, value){
18860         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18861         this.editNode.setText(value);
18862     },
18863
18864     // private
18865     onHide : function(){
18866         Roo.tree.TreeEditor.superclass.onHide.call(this);
18867         if(this.editNode){
18868             this.editNode.ui.focus();
18869         }
18870     },
18871
18872     // private
18873     onSpecialKey : function(field, e){
18874         var k = e.getKey();
18875         if(k == e.ESC){
18876             e.stopEvent();
18877             this.cancelEdit();
18878         }else if(k == e.ENTER && !e.hasModifier()){
18879             e.stopEvent();
18880             this.completeEdit();
18881         }
18882     }
18883 });//<Script type="text/javascript">
18884 /*
18885  * Based on:
18886  * Ext JS Library 1.1.1
18887  * Copyright(c) 2006-2007, Ext JS, LLC.
18888  *
18889  * Originally Released Under LGPL - original licence link has changed is not relivant.
18890  *
18891  * Fork - LGPL
18892  * <script type="text/javascript">
18893  */
18894  
18895 /**
18896  * Not documented??? - probably should be...
18897  */
18898
18899 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18900     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18901     
18902     renderElements : function(n, a, targetNode, bulkRender){
18903         //consel.log("renderElements?");
18904         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18905
18906         var t = n.getOwnerTree();
18907         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18908         
18909         var cols = t.columns;
18910         var bw = t.borderWidth;
18911         var c = cols[0];
18912         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18913          var cb = typeof a.checked == "boolean";
18914         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18915         var colcls = 'x-t-' + tid + '-c0';
18916         var buf = [
18917             '<li class="x-tree-node">',
18918             
18919                 
18920                 '<div class="x-tree-node-el ', a.cls,'">',
18921                     // extran...
18922                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18923                 
18924                 
18925                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18926                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18927                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18928                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18929                            (a.iconCls ? ' '+a.iconCls : ''),
18930                            '" unselectable="on" />',
18931                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18932                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18933                              
18934                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18935                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18936                             '<span unselectable="on" qtip="' + tx + '">',
18937                              tx,
18938                              '</span></a>' ,
18939                     '</div>',
18940                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18941                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18942                  ];
18943         for(var i = 1, len = cols.length; i < len; i++){
18944             c = cols[i];
18945             colcls = 'x-t-' + tid + '-c' +i;
18946             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18947             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18948                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18949                       "</div>");
18950          }
18951          
18952          buf.push(
18953             '</a>',
18954             '<div class="x-clear"></div></div>',
18955             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18956             "</li>");
18957         
18958         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18959             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18960                                 n.nextSibling.ui.getEl(), buf.join(""));
18961         }else{
18962             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18963         }
18964         var el = this.wrap.firstChild;
18965         this.elRow = el;
18966         this.elNode = el.firstChild;
18967         this.ranchor = el.childNodes[1];
18968         this.ctNode = this.wrap.childNodes[1];
18969         var cs = el.firstChild.childNodes;
18970         this.indentNode = cs[0];
18971         this.ecNode = cs[1];
18972         this.iconNode = cs[2];
18973         var index = 3;
18974         if(cb){
18975             this.checkbox = cs[3];
18976             index++;
18977         }
18978         this.anchor = cs[index];
18979         
18980         this.textNode = cs[index].firstChild;
18981         
18982         //el.on("click", this.onClick, this);
18983         //el.on("dblclick", this.onDblClick, this);
18984         
18985         
18986        // console.log(this);
18987     },
18988     initEvents : function(){
18989         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18990         
18991             
18992         var a = this.ranchor;
18993
18994         var el = Roo.get(a);
18995
18996         if(Roo.isOpera){ // opera render bug ignores the CSS
18997             el.setStyle("text-decoration", "none");
18998         }
18999
19000         el.on("click", this.onClick, this);
19001         el.on("dblclick", this.onDblClick, this);
19002         el.on("contextmenu", this.onContextMenu, this);
19003         
19004     },
19005     
19006     /*onSelectedChange : function(state){
19007         if(state){
19008             this.focus();
19009             this.addClass("x-tree-selected");
19010         }else{
19011             //this.blur();
19012             this.removeClass("x-tree-selected");
19013         }
19014     },*/
19015     addClass : function(cls){
19016         if(this.elRow){
19017             Roo.fly(this.elRow).addClass(cls);
19018         }
19019         
19020     },
19021     
19022     
19023     removeClass : function(cls){
19024         if(this.elRow){
19025             Roo.fly(this.elRow).removeClass(cls);
19026         }
19027     }
19028
19029     
19030     
19031 });//<Script type="text/javascript">
19032
19033 /*
19034  * Based on:
19035  * Ext JS Library 1.1.1
19036  * Copyright(c) 2006-2007, Ext JS, LLC.
19037  *
19038  * Originally Released Under LGPL - original licence link has changed is not relivant.
19039  *
19040  * Fork - LGPL
19041  * <script type="text/javascript">
19042  */
19043  
19044
19045 /**
19046  * @class Roo.tree.ColumnTree
19047  * @extends Roo.data.TreePanel
19048  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19049  * @cfg {int} borderWidth  compined right/left border allowance
19050  * @constructor
19051  * @param {String/HTMLElement/Element} el The container element
19052  * @param {Object} config
19053  */
19054 Roo.tree.ColumnTree =  function(el, config)
19055 {
19056    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19057    this.addEvents({
19058         /**
19059         * @event resize
19060         * Fire this event on a container when it resizes
19061         * @param {int} w Width
19062         * @param {int} h Height
19063         */
19064        "resize" : true
19065     });
19066     this.on('resize', this.onResize, this);
19067 };
19068
19069 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19070     //lines:false,
19071     
19072     
19073     borderWidth: Roo.isBorderBox ? 0 : 2, 
19074     headEls : false,
19075     
19076     render : function(){
19077         // add the header.....
19078        
19079         Roo.tree.ColumnTree.superclass.render.apply(this);
19080         
19081         this.el.addClass('x-column-tree');
19082         
19083         this.headers = this.el.createChild(
19084             {cls:'x-tree-headers'},this.innerCt.dom);
19085    
19086         var cols = this.columns, c;
19087         var totalWidth = 0;
19088         this.headEls = [];
19089         var  len = cols.length;
19090         for(var i = 0; i < len; i++){
19091              c = cols[i];
19092              totalWidth += c.width;
19093             this.headEls.push(this.headers.createChild({
19094                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19095                  cn: {
19096                      cls:'x-tree-hd-text',
19097                      html: c.header
19098                  },
19099                  style:'width:'+(c.width-this.borderWidth)+'px;'
19100              }));
19101         }
19102         this.headers.createChild({cls:'x-clear'});
19103         // prevent floats from wrapping when clipped
19104         this.headers.setWidth(totalWidth);
19105         //this.innerCt.setWidth(totalWidth);
19106         this.innerCt.setStyle({ overflow: 'auto' });
19107         this.onResize(this.width, this.height);
19108              
19109         
19110     },
19111     onResize : function(w,h)
19112     {
19113         this.height = h;
19114         this.width = w;
19115         // resize cols..
19116         this.innerCt.setWidth(this.width);
19117         this.innerCt.setHeight(this.height-20);
19118         
19119         // headers...
19120         var cols = this.columns, c;
19121         var totalWidth = 0;
19122         var expEl = false;
19123         var len = cols.length;
19124         for(var i = 0; i < len; i++){
19125             c = cols[i];
19126             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19127                 // it's the expander..
19128                 expEl  = this.headEls[i];
19129                 continue;
19130             }
19131             totalWidth += c.width;
19132             
19133         }
19134         if (expEl) {
19135             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19136         }
19137         this.headers.setWidth(w-20);
19138
19139         
19140         
19141         
19142     }
19143 });
19144 /*
19145  * Based on:
19146  * Ext JS Library 1.1.1
19147  * Copyright(c) 2006-2007, Ext JS, LLC.
19148  *
19149  * Originally Released Under LGPL - original licence link has changed is not relivant.
19150  *
19151  * Fork - LGPL
19152  * <script type="text/javascript">
19153  */
19154  
19155 /**
19156  * @class Roo.menu.Menu
19157  * @extends Roo.util.Observable
19158  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19159  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19160  * @constructor
19161  * Creates a new Menu
19162  * @param {Object} config Configuration options
19163  */
19164 Roo.menu.Menu = function(config){
19165     Roo.apply(this, config);
19166     this.id = this.id || Roo.id();
19167     this.addEvents({
19168         /**
19169          * @event beforeshow
19170          * Fires before this menu is displayed
19171          * @param {Roo.menu.Menu} this
19172          */
19173         beforeshow : true,
19174         /**
19175          * @event beforehide
19176          * Fires before this menu is hidden
19177          * @param {Roo.menu.Menu} this
19178          */
19179         beforehide : true,
19180         /**
19181          * @event show
19182          * Fires after this menu is displayed
19183          * @param {Roo.menu.Menu} this
19184          */
19185         show : true,
19186         /**
19187          * @event hide
19188          * Fires after this menu is hidden
19189          * @param {Roo.menu.Menu} this
19190          */
19191         hide : true,
19192         /**
19193          * @event click
19194          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19195          * @param {Roo.menu.Menu} this
19196          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19197          * @param {Roo.EventObject} e
19198          */
19199         click : true,
19200         /**
19201          * @event mouseover
19202          * Fires when the mouse is hovering over this menu
19203          * @param {Roo.menu.Menu} this
19204          * @param {Roo.EventObject} e
19205          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19206          */
19207         mouseover : true,
19208         /**
19209          * @event mouseout
19210          * Fires when the mouse exits this menu
19211          * @param {Roo.menu.Menu} this
19212          * @param {Roo.EventObject} e
19213          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19214          */
19215         mouseout : true,
19216         /**
19217          * @event itemclick
19218          * Fires when a menu item contained in this menu is clicked
19219          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19220          * @param {Roo.EventObject} e
19221          */
19222         itemclick: true
19223     });
19224     if (this.registerMenu) {
19225         Roo.menu.MenuMgr.register(this);
19226     }
19227     
19228     var mis = this.items;
19229     this.items = new Roo.util.MixedCollection();
19230     if(mis){
19231         this.add.apply(this, mis);
19232     }
19233 };
19234
19235 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19236     /**
19237      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19238      */
19239     minWidth : 120,
19240     /**
19241      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19242      * for bottom-right shadow (defaults to "sides")
19243      */
19244     shadow : "sides",
19245     /**
19246      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19247      * this menu (defaults to "tl-tr?")
19248      */
19249     subMenuAlign : "tl-tr?",
19250     /**
19251      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19252      * relative to its element of origin (defaults to "tl-bl?")
19253      */
19254     defaultAlign : "tl-bl?",
19255     /**
19256      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19257      */
19258     allowOtherMenus : false,
19259     /**
19260      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19261      */
19262     registerMenu : true,
19263
19264     hidden:true,
19265
19266     // private
19267     render : function(){
19268         if(this.el){
19269             return;
19270         }
19271         var el = this.el = new Roo.Layer({
19272             cls: "x-menu",
19273             shadow:this.shadow,
19274             constrain: false,
19275             parentEl: this.parentEl || document.body,
19276             zindex:15000
19277         });
19278
19279         this.keyNav = new Roo.menu.MenuNav(this);
19280
19281         if(this.plain){
19282             el.addClass("x-menu-plain");
19283         }
19284         if(this.cls){
19285             el.addClass(this.cls);
19286         }
19287         // generic focus element
19288         this.focusEl = el.createChild({
19289             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19290         });
19291         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19292         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19293         
19294         ul.on("mouseover", this.onMouseOver, this);
19295         ul.on("mouseout", this.onMouseOut, this);
19296         this.items.each(function(item){
19297             if (item.hidden) {
19298                 return;
19299             }
19300             
19301             var li = document.createElement("li");
19302             li.className = "x-menu-list-item";
19303             ul.dom.appendChild(li);
19304             item.render(li, this);
19305         }, this);
19306         this.ul = ul;
19307         this.autoWidth();
19308     },
19309
19310     // private
19311     autoWidth : function(){
19312         var el = this.el, ul = this.ul;
19313         if(!el){
19314             return;
19315         }
19316         var w = this.width;
19317         if(w){
19318             el.setWidth(w);
19319         }else if(Roo.isIE){
19320             el.setWidth(this.minWidth);
19321             var t = el.dom.offsetWidth; // force recalc
19322             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19323         }
19324     },
19325
19326     // private
19327     delayAutoWidth : function(){
19328         if(this.rendered){
19329             if(!this.awTask){
19330                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19331             }
19332             this.awTask.delay(20);
19333         }
19334     },
19335
19336     // private
19337     findTargetItem : function(e){
19338         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19339         if(t && t.menuItemId){
19340             return this.items.get(t.menuItemId);
19341         }
19342     },
19343
19344     // private
19345     onClick : function(e){
19346         Roo.log("menu.onClick");
19347         var t = this.findTargetItem(e);
19348         if(!t){
19349             return;
19350         }
19351         Roo.log(e);
19352         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19353             if(t == this.activeItem && t.shouldDeactivate(e)){
19354                 this.activeItem.deactivate();
19355                 delete this.activeItem;
19356                 return;
19357             }
19358             if(t.canActivate){
19359                 this.setActiveItem(t, true);
19360             }
19361             return;
19362             
19363             
19364         }
19365         
19366         t.onClick(e);
19367         this.fireEvent("click", this, t, e);
19368     },
19369
19370     // private
19371     setActiveItem : function(item, autoExpand){
19372         if(item != this.activeItem){
19373             if(this.activeItem){
19374                 this.activeItem.deactivate();
19375             }
19376             this.activeItem = item;
19377             item.activate(autoExpand);
19378         }else if(autoExpand){
19379             item.expandMenu();
19380         }
19381     },
19382
19383     // private
19384     tryActivate : function(start, step){
19385         var items = this.items;
19386         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19387             var item = items.get(i);
19388             if(!item.disabled && item.canActivate){
19389                 this.setActiveItem(item, false);
19390                 return item;
19391             }
19392         }
19393         return false;
19394     },
19395
19396     // private
19397     onMouseOver : function(e){
19398         var t;
19399         if(t = this.findTargetItem(e)){
19400             if(t.canActivate && !t.disabled){
19401                 this.setActiveItem(t, true);
19402             }
19403         }
19404         this.fireEvent("mouseover", this, e, t);
19405     },
19406
19407     // private
19408     onMouseOut : function(e){
19409         var t;
19410         if(t = this.findTargetItem(e)){
19411             if(t == this.activeItem && t.shouldDeactivate(e)){
19412                 this.activeItem.deactivate();
19413                 delete this.activeItem;
19414             }
19415         }
19416         this.fireEvent("mouseout", this, e, t);
19417     },
19418
19419     /**
19420      * Read-only.  Returns true if the menu is currently displayed, else false.
19421      * @type Boolean
19422      */
19423     isVisible : function(){
19424         return this.el && !this.hidden;
19425     },
19426
19427     /**
19428      * Displays this menu relative to another element
19429      * @param {String/HTMLElement/Roo.Element} element The element to align to
19430      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19431      * the element (defaults to this.defaultAlign)
19432      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19433      */
19434     show : function(el, pos, parentMenu){
19435         this.parentMenu = parentMenu;
19436         if(!this.el){
19437             this.render();
19438         }
19439         this.fireEvent("beforeshow", this);
19440         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19441     },
19442
19443     /**
19444      * Displays this menu at a specific xy position
19445      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19446      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19447      */
19448     showAt : function(xy, parentMenu, /* private: */_e){
19449         this.parentMenu = parentMenu;
19450         if(!this.el){
19451             this.render();
19452         }
19453         if(_e !== false){
19454             this.fireEvent("beforeshow", this);
19455             xy = this.el.adjustForConstraints(xy);
19456         }
19457         this.el.setXY(xy);
19458         this.el.show();
19459         this.hidden = false;
19460         this.focus();
19461         this.fireEvent("show", this);
19462     },
19463
19464     focus : function(){
19465         if(!this.hidden){
19466             this.doFocus.defer(50, this);
19467         }
19468     },
19469
19470     doFocus : function(){
19471         if(!this.hidden){
19472             this.focusEl.focus();
19473         }
19474     },
19475
19476     /**
19477      * Hides this menu and optionally all parent menus
19478      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19479      */
19480     hide : function(deep){
19481         if(this.el && this.isVisible()){
19482             this.fireEvent("beforehide", this);
19483             if(this.activeItem){
19484                 this.activeItem.deactivate();
19485                 this.activeItem = null;
19486             }
19487             this.el.hide();
19488             this.hidden = true;
19489             this.fireEvent("hide", this);
19490         }
19491         if(deep === true && this.parentMenu){
19492             this.parentMenu.hide(true);
19493         }
19494     },
19495
19496     /**
19497      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19498      * Any of the following are valid:
19499      * <ul>
19500      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19501      * <li>An HTMLElement object which will be converted to a menu item</li>
19502      * <li>A menu item config object that will be created as a new menu item</li>
19503      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19504      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19505      * </ul>
19506      * Usage:
19507      * <pre><code>
19508 // Create the menu
19509 var menu = new Roo.menu.Menu();
19510
19511 // Create a menu item to add by reference
19512 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19513
19514 // Add a bunch of items at once using different methods.
19515 // Only the last item added will be returned.
19516 var item = menu.add(
19517     menuItem,                // add existing item by ref
19518     'Dynamic Item',          // new TextItem
19519     '-',                     // new separator
19520     { text: 'Config Item' }  // new item by config
19521 );
19522 </code></pre>
19523      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19524      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19525      */
19526     add : function(){
19527         var a = arguments, l = a.length, item;
19528         for(var i = 0; i < l; i++){
19529             var el = a[i];
19530             if ((typeof(el) == "object") && el.xtype && el.xns) {
19531                 el = Roo.factory(el, Roo.menu);
19532             }
19533             
19534             if(el.render){ // some kind of Item
19535                 item = this.addItem(el);
19536             }else if(typeof el == "string"){ // string
19537                 if(el == "separator" || el == "-"){
19538                     item = this.addSeparator();
19539                 }else{
19540                     item = this.addText(el);
19541                 }
19542             }else if(el.tagName || el.el){ // element
19543                 item = this.addElement(el);
19544             }else if(typeof el == "object"){ // must be menu item config?
19545                 item = this.addMenuItem(el);
19546             }
19547         }
19548         return item;
19549     },
19550
19551     /**
19552      * Returns this menu's underlying {@link Roo.Element} object
19553      * @return {Roo.Element} The element
19554      */
19555     getEl : function(){
19556         if(!this.el){
19557             this.render();
19558         }
19559         return this.el;
19560     },
19561
19562     /**
19563      * Adds a separator bar to the menu
19564      * @return {Roo.menu.Item} The menu item that was added
19565      */
19566     addSeparator : function(){
19567         return this.addItem(new Roo.menu.Separator());
19568     },
19569
19570     /**
19571      * Adds an {@link Roo.Element} object to the menu
19572      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19573      * @return {Roo.menu.Item} The menu item that was added
19574      */
19575     addElement : function(el){
19576         return this.addItem(new Roo.menu.BaseItem(el));
19577     },
19578
19579     /**
19580      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19581      * @param {Roo.menu.Item} item The menu item to add
19582      * @return {Roo.menu.Item} The menu item that was added
19583      */
19584     addItem : function(item){
19585         this.items.add(item);
19586         if(this.ul){
19587             var li = document.createElement("li");
19588             li.className = "x-menu-list-item";
19589             this.ul.dom.appendChild(li);
19590             item.render(li, this);
19591             this.delayAutoWidth();
19592         }
19593         return item;
19594     },
19595
19596     /**
19597      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19598      * @param {Object} config A MenuItem config object
19599      * @return {Roo.menu.Item} The menu item that was added
19600      */
19601     addMenuItem : function(config){
19602         if(!(config instanceof Roo.menu.Item)){
19603             if(typeof config.checked == "boolean"){ // must be check menu item config?
19604                 config = new Roo.menu.CheckItem(config);
19605             }else{
19606                 config = new Roo.menu.Item(config);
19607             }
19608         }
19609         return this.addItem(config);
19610     },
19611
19612     /**
19613      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19614      * @param {String} text The text to display in the menu item
19615      * @return {Roo.menu.Item} The menu item that was added
19616      */
19617     addText : function(text){
19618         return this.addItem(new Roo.menu.TextItem({ text : text }));
19619     },
19620
19621     /**
19622      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19623      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19624      * @param {Roo.menu.Item} item The menu item to add
19625      * @return {Roo.menu.Item} The menu item that was added
19626      */
19627     insert : function(index, item){
19628         this.items.insert(index, item);
19629         if(this.ul){
19630             var li = document.createElement("li");
19631             li.className = "x-menu-list-item";
19632             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19633             item.render(li, this);
19634             this.delayAutoWidth();
19635         }
19636         return item;
19637     },
19638
19639     /**
19640      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19641      * @param {Roo.menu.Item} item The menu item to remove
19642      */
19643     remove : function(item){
19644         this.items.removeKey(item.id);
19645         item.destroy();
19646     },
19647
19648     /**
19649      * Removes and destroys all items in the menu
19650      */
19651     removeAll : function(){
19652         var f;
19653         while(f = this.items.first()){
19654             this.remove(f);
19655         }
19656     }
19657 });
19658
19659 // MenuNav is a private utility class used internally by the Menu
19660 Roo.menu.MenuNav = function(menu){
19661     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19662     this.scope = this.menu = menu;
19663 };
19664
19665 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19666     doRelay : function(e, h){
19667         var k = e.getKey();
19668         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19669             this.menu.tryActivate(0, 1);
19670             return false;
19671         }
19672         return h.call(this.scope || this, e, this.menu);
19673     },
19674
19675     up : function(e, m){
19676         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19677             m.tryActivate(m.items.length-1, -1);
19678         }
19679     },
19680
19681     down : function(e, m){
19682         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19683             m.tryActivate(0, 1);
19684         }
19685     },
19686
19687     right : function(e, m){
19688         if(m.activeItem){
19689             m.activeItem.expandMenu(true);
19690         }
19691     },
19692
19693     left : function(e, m){
19694         m.hide();
19695         if(m.parentMenu && m.parentMenu.activeItem){
19696             m.parentMenu.activeItem.activate();
19697         }
19698     },
19699
19700     enter : function(e, m){
19701         if(m.activeItem){
19702             e.stopPropagation();
19703             m.activeItem.onClick(e);
19704             m.fireEvent("click", this, m.activeItem);
19705             return true;
19706         }
19707     }
19708 });/*
19709  * Based on:
19710  * Ext JS Library 1.1.1
19711  * Copyright(c) 2006-2007, Ext JS, LLC.
19712  *
19713  * Originally Released Under LGPL - original licence link has changed is not relivant.
19714  *
19715  * Fork - LGPL
19716  * <script type="text/javascript">
19717  */
19718  
19719 /**
19720  * @class Roo.menu.MenuMgr
19721  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19722  * @singleton
19723  */
19724 Roo.menu.MenuMgr = function(){
19725    var menus, active, groups = {}, attached = false, lastShow = new Date();
19726
19727    // private - called when first menu is created
19728    function init(){
19729        menus = {};
19730        active = new Roo.util.MixedCollection();
19731        Roo.get(document).addKeyListener(27, function(){
19732            if(active.length > 0){
19733                hideAll();
19734            }
19735        });
19736    }
19737
19738    // private
19739    function hideAll(){
19740        if(active && active.length > 0){
19741            var c = active.clone();
19742            c.each(function(m){
19743                m.hide();
19744            });
19745        }
19746    }
19747
19748    // private
19749    function onHide(m){
19750        active.remove(m);
19751        if(active.length < 1){
19752            Roo.get(document).un("mousedown", onMouseDown);
19753            attached = false;
19754        }
19755    }
19756
19757    // private
19758    function onShow(m){
19759        var last = active.last();
19760        lastShow = new Date();
19761        active.add(m);
19762        if(!attached){
19763            Roo.get(document).on("mousedown", onMouseDown);
19764            attached = true;
19765        }
19766        if(m.parentMenu){
19767           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19768           m.parentMenu.activeChild = m;
19769        }else if(last && last.isVisible()){
19770           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19771        }
19772    }
19773
19774    // private
19775    function onBeforeHide(m){
19776        if(m.activeChild){
19777            m.activeChild.hide();
19778        }
19779        if(m.autoHideTimer){
19780            clearTimeout(m.autoHideTimer);
19781            delete m.autoHideTimer;
19782        }
19783    }
19784
19785    // private
19786    function onBeforeShow(m){
19787        var pm = m.parentMenu;
19788        if(!pm && !m.allowOtherMenus){
19789            hideAll();
19790        }else if(pm && pm.activeChild && active != m){
19791            pm.activeChild.hide();
19792        }
19793    }
19794
19795    // private
19796    function onMouseDown(e){
19797        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19798            hideAll();
19799        }
19800    }
19801
19802    // private
19803    function onBeforeCheck(mi, state){
19804        if(state){
19805            var g = groups[mi.group];
19806            for(var i = 0, l = g.length; i < l; i++){
19807                if(g[i] != mi){
19808                    g[i].setChecked(false);
19809                }
19810            }
19811        }
19812    }
19813
19814    return {
19815
19816        /**
19817         * Hides all menus that are currently visible
19818         */
19819        hideAll : function(){
19820             hideAll();  
19821        },
19822
19823        // private
19824        register : function(menu){
19825            if(!menus){
19826                init();
19827            }
19828            menus[menu.id] = menu;
19829            menu.on("beforehide", onBeforeHide);
19830            menu.on("hide", onHide);
19831            menu.on("beforeshow", onBeforeShow);
19832            menu.on("show", onShow);
19833            var g = menu.group;
19834            if(g && menu.events["checkchange"]){
19835                if(!groups[g]){
19836                    groups[g] = [];
19837                }
19838                groups[g].push(menu);
19839                menu.on("checkchange", onCheck);
19840            }
19841        },
19842
19843         /**
19844          * Returns a {@link Roo.menu.Menu} object
19845          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19846          * be used to generate and return a new Menu instance.
19847          */
19848        get : function(menu){
19849            if(typeof menu == "string"){ // menu id
19850                return menus[menu];
19851            }else if(menu.events){  // menu instance
19852                return menu;
19853            }else if(typeof menu.length == 'number'){ // array of menu items?
19854                return new Roo.menu.Menu({items:menu});
19855            }else{ // otherwise, must be a config
19856                return new Roo.menu.Menu(menu);
19857            }
19858        },
19859
19860        // private
19861        unregister : function(menu){
19862            delete menus[menu.id];
19863            menu.un("beforehide", onBeforeHide);
19864            menu.un("hide", onHide);
19865            menu.un("beforeshow", onBeforeShow);
19866            menu.un("show", onShow);
19867            var g = menu.group;
19868            if(g && menu.events["checkchange"]){
19869                groups[g].remove(menu);
19870                menu.un("checkchange", onCheck);
19871            }
19872        },
19873
19874        // private
19875        registerCheckable : function(menuItem){
19876            var g = menuItem.group;
19877            if(g){
19878                if(!groups[g]){
19879                    groups[g] = [];
19880                }
19881                groups[g].push(menuItem);
19882                menuItem.on("beforecheckchange", onBeforeCheck);
19883            }
19884        },
19885
19886        // private
19887        unregisterCheckable : function(menuItem){
19888            var g = menuItem.group;
19889            if(g){
19890                groups[g].remove(menuItem);
19891                menuItem.un("beforecheckchange", onBeforeCheck);
19892            }
19893        }
19894    };
19895 }();/*
19896  * Based on:
19897  * Ext JS Library 1.1.1
19898  * Copyright(c) 2006-2007, Ext JS, LLC.
19899  *
19900  * Originally Released Under LGPL - original licence link has changed is not relivant.
19901  *
19902  * Fork - LGPL
19903  * <script type="text/javascript">
19904  */
19905  
19906
19907 /**
19908  * @class Roo.menu.BaseItem
19909  * @extends Roo.Component
19910  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19911  * management and base configuration options shared by all menu components.
19912  * @constructor
19913  * Creates a new BaseItem
19914  * @param {Object} config Configuration options
19915  */
19916 Roo.menu.BaseItem = function(config){
19917     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19918
19919     this.addEvents({
19920         /**
19921          * @event click
19922          * Fires when this item is clicked
19923          * @param {Roo.menu.BaseItem} this
19924          * @param {Roo.EventObject} e
19925          */
19926         click: true,
19927         /**
19928          * @event activate
19929          * Fires when this item is activated
19930          * @param {Roo.menu.BaseItem} this
19931          */
19932         activate : true,
19933         /**
19934          * @event deactivate
19935          * Fires when this item is deactivated
19936          * @param {Roo.menu.BaseItem} this
19937          */
19938         deactivate : true
19939     });
19940
19941     if(this.handler){
19942         this.on("click", this.handler, this.scope, true);
19943     }
19944 };
19945
19946 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19947     /**
19948      * @cfg {Function} handler
19949      * A function that will handle the click event of this menu item (defaults to undefined)
19950      */
19951     /**
19952      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19953      */
19954     canActivate : false,
19955     
19956      /**
19957      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19958      */
19959     hidden: false,
19960     
19961     /**
19962      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19963      */
19964     activeClass : "x-menu-item-active",
19965     /**
19966      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19967      */
19968     hideOnClick : true,
19969     /**
19970      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19971      */
19972     hideDelay : 100,
19973
19974     // private
19975     ctype: "Roo.menu.BaseItem",
19976
19977     // private
19978     actionMode : "container",
19979
19980     // private
19981     render : function(container, parentMenu){
19982         this.parentMenu = parentMenu;
19983         Roo.menu.BaseItem.superclass.render.call(this, container);
19984         this.container.menuItemId = this.id;
19985     },
19986
19987     // private
19988     onRender : function(container, position){
19989         this.el = Roo.get(this.el);
19990         container.dom.appendChild(this.el.dom);
19991     },
19992
19993     // private
19994     onClick : function(e){
19995         if(!this.disabled && this.fireEvent("click", this, e) !== false
19996                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19997             this.handleClick(e);
19998         }else{
19999             e.stopEvent();
20000         }
20001     },
20002
20003     // private
20004     activate : function(){
20005         if(this.disabled){
20006             return false;
20007         }
20008         var li = this.container;
20009         li.addClass(this.activeClass);
20010         this.region = li.getRegion().adjust(2, 2, -2, -2);
20011         this.fireEvent("activate", this);
20012         return true;
20013     },
20014
20015     // private
20016     deactivate : function(){
20017         this.container.removeClass(this.activeClass);
20018         this.fireEvent("deactivate", this);
20019     },
20020
20021     // private
20022     shouldDeactivate : function(e){
20023         return !this.region || !this.region.contains(e.getPoint());
20024     },
20025
20026     // private
20027     handleClick : function(e){
20028         if(this.hideOnClick){
20029             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20030         }
20031     },
20032
20033     // private
20034     expandMenu : function(autoActivate){
20035         // do nothing
20036     },
20037
20038     // private
20039     hideMenu : function(){
20040         // do nothing
20041     }
20042 });/*
20043  * Based on:
20044  * Ext JS Library 1.1.1
20045  * Copyright(c) 2006-2007, Ext JS, LLC.
20046  *
20047  * Originally Released Under LGPL - original licence link has changed is not relivant.
20048  *
20049  * Fork - LGPL
20050  * <script type="text/javascript">
20051  */
20052  
20053 /**
20054  * @class Roo.menu.Adapter
20055  * @extends Roo.menu.BaseItem
20056  * 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.
20057  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20058  * @constructor
20059  * Creates a new Adapter
20060  * @param {Object} config Configuration options
20061  */
20062 Roo.menu.Adapter = function(component, config){
20063     Roo.menu.Adapter.superclass.constructor.call(this, config);
20064     this.component = component;
20065 };
20066 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20067     // private
20068     canActivate : true,
20069
20070     // private
20071     onRender : function(container, position){
20072         this.component.render(container);
20073         this.el = this.component.getEl();
20074     },
20075
20076     // private
20077     activate : function(){
20078         if(this.disabled){
20079             return false;
20080         }
20081         this.component.focus();
20082         this.fireEvent("activate", this);
20083         return true;
20084     },
20085
20086     // private
20087     deactivate : function(){
20088         this.fireEvent("deactivate", this);
20089     },
20090
20091     // private
20092     disable : function(){
20093         this.component.disable();
20094         Roo.menu.Adapter.superclass.disable.call(this);
20095     },
20096
20097     // private
20098     enable : function(){
20099         this.component.enable();
20100         Roo.menu.Adapter.superclass.enable.call(this);
20101     }
20102 });/*
20103  * Based on:
20104  * Ext JS Library 1.1.1
20105  * Copyright(c) 2006-2007, Ext JS, LLC.
20106  *
20107  * Originally Released Under LGPL - original licence link has changed is not relivant.
20108  *
20109  * Fork - LGPL
20110  * <script type="text/javascript">
20111  */
20112
20113 /**
20114  * @class Roo.menu.TextItem
20115  * @extends Roo.menu.BaseItem
20116  * Adds a static text string to a menu, usually used as either a heading or group separator.
20117  * Note: old style constructor with text is still supported.
20118  * 
20119  * @constructor
20120  * Creates a new TextItem
20121  * @param {Object} cfg Configuration
20122  */
20123 Roo.menu.TextItem = function(cfg){
20124     if (typeof(cfg) == 'string') {
20125         this.text = cfg;
20126     } else {
20127         Roo.apply(this,cfg);
20128     }
20129     
20130     Roo.menu.TextItem.superclass.constructor.call(this);
20131 };
20132
20133 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20134     /**
20135      * @cfg {Boolean} text Text to show on item.
20136      */
20137     text : '',
20138     
20139     /**
20140      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20141      */
20142     hideOnClick : false,
20143     /**
20144      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20145      */
20146     itemCls : "x-menu-text",
20147
20148     // private
20149     onRender : function(){
20150         var s = document.createElement("span");
20151         s.className = this.itemCls;
20152         s.innerHTML = this.text;
20153         this.el = s;
20154         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20155     }
20156 });/*
20157  * Based on:
20158  * Ext JS Library 1.1.1
20159  * Copyright(c) 2006-2007, Ext JS, LLC.
20160  *
20161  * Originally Released Under LGPL - original licence link has changed is not relivant.
20162  *
20163  * Fork - LGPL
20164  * <script type="text/javascript">
20165  */
20166
20167 /**
20168  * @class Roo.menu.Separator
20169  * @extends Roo.menu.BaseItem
20170  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20171  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20172  * @constructor
20173  * @param {Object} config Configuration options
20174  */
20175 Roo.menu.Separator = function(config){
20176     Roo.menu.Separator.superclass.constructor.call(this, config);
20177 };
20178
20179 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20180     /**
20181      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20182      */
20183     itemCls : "x-menu-sep",
20184     /**
20185      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20186      */
20187     hideOnClick : false,
20188
20189     // private
20190     onRender : function(li){
20191         var s = document.createElement("span");
20192         s.className = this.itemCls;
20193         s.innerHTML = "&#160;";
20194         this.el = s;
20195         li.addClass("x-menu-sep-li");
20196         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20197     }
20198 });/*
20199  * Based on:
20200  * Ext JS Library 1.1.1
20201  * Copyright(c) 2006-2007, Ext JS, LLC.
20202  *
20203  * Originally Released Under LGPL - original licence link has changed is not relivant.
20204  *
20205  * Fork - LGPL
20206  * <script type="text/javascript">
20207  */
20208 /**
20209  * @class Roo.menu.Item
20210  * @extends Roo.menu.BaseItem
20211  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20212  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20213  * activation and click handling.
20214  * @constructor
20215  * Creates a new Item
20216  * @param {Object} config Configuration options
20217  */
20218 Roo.menu.Item = function(config){
20219     Roo.menu.Item.superclass.constructor.call(this, config);
20220     if(this.menu){
20221         this.menu = Roo.menu.MenuMgr.get(this.menu);
20222     }
20223 };
20224 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20225     
20226     /**
20227      * @cfg {String} text
20228      * The text to show on the menu item.
20229      */
20230     text: '',
20231      /**
20232      * @cfg {String} HTML to render in menu
20233      * The text to show on the menu item (HTML version).
20234      */
20235     html: '',
20236     /**
20237      * @cfg {String} icon
20238      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20239      */
20240     icon: undefined,
20241     /**
20242      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20243      */
20244     itemCls : "x-menu-item",
20245     /**
20246      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20247      */
20248     canActivate : true,
20249     /**
20250      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20251      */
20252     showDelay: 200,
20253     // doc'd in BaseItem
20254     hideDelay: 200,
20255
20256     // private
20257     ctype: "Roo.menu.Item",
20258     
20259     // private
20260     onRender : function(container, position){
20261         var el = document.createElement("a");
20262         el.hideFocus = true;
20263         el.unselectable = "on";
20264         el.href = this.href || "#";
20265         if(this.hrefTarget){
20266             el.target = this.hrefTarget;
20267         }
20268         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20269         
20270         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20271         
20272         el.innerHTML = String.format(
20273                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20274                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20275         this.el = el;
20276         Roo.menu.Item.superclass.onRender.call(this, container, position);
20277     },
20278
20279     /**
20280      * Sets the text to display in this menu item
20281      * @param {String} text The text to display
20282      * @param {Boolean} isHTML true to indicate text is pure html.
20283      */
20284     setText : function(text, isHTML){
20285         if (isHTML) {
20286             this.html = text;
20287         } else {
20288             this.text = text;
20289             this.html = '';
20290         }
20291         if(this.rendered){
20292             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20293      
20294             this.el.update(String.format(
20295                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20296                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20297             this.parentMenu.autoWidth();
20298         }
20299     },
20300
20301     // private
20302     handleClick : function(e){
20303         if(!this.href){ // if no link defined, stop the event automatically
20304             e.stopEvent();
20305         }
20306         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20307     },
20308
20309     // private
20310     activate : function(autoExpand){
20311         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20312             this.focus();
20313             if(autoExpand){
20314                 this.expandMenu();
20315             }
20316         }
20317         return true;
20318     },
20319
20320     // private
20321     shouldDeactivate : function(e){
20322         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20323             if(this.menu && this.menu.isVisible()){
20324                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20325             }
20326             return true;
20327         }
20328         return false;
20329     },
20330
20331     // private
20332     deactivate : function(){
20333         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20334         this.hideMenu();
20335     },
20336
20337     // private
20338     expandMenu : function(autoActivate){
20339         if(!this.disabled && this.menu){
20340             clearTimeout(this.hideTimer);
20341             delete this.hideTimer;
20342             if(!this.menu.isVisible() && !this.showTimer){
20343                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20344             }else if (this.menu.isVisible() && autoActivate){
20345                 this.menu.tryActivate(0, 1);
20346             }
20347         }
20348     },
20349
20350     // private
20351     deferExpand : function(autoActivate){
20352         delete this.showTimer;
20353         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20354         if(autoActivate){
20355             this.menu.tryActivate(0, 1);
20356         }
20357     },
20358
20359     // private
20360     hideMenu : function(){
20361         clearTimeout(this.showTimer);
20362         delete this.showTimer;
20363         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20364             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20365         }
20366     },
20367
20368     // private
20369     deferHide : function(){
20370         delete this.hideTimer;
20371         this.menu.hide();
20372     }
20373 });/*
20374  * Based on:
20375  * Ext JS Library 1.1.1
20376  * Copyright(c) 2006-2007, Ext JS, LLC.
20377  *
20378  * Originally Released Under LGPL - original licence link has changed is not relivant.
20379  *
20380  * Fork - LGPL
20381  * <script type="text/javascript">
20382  */
20383  
20384 /**
20385  * @class Roo.menu.CheckItem
20386  * @extends Roo.menu.Item
20387  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20388  * @constructor
20389  * Creates a new CheckItem
20390  * @param {Object} config Configuration options
20391  */
20392 Roo.menu.CheckItem = function(config){
20393     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20394     this.addEvents({
20395         /**
20396          * @event beforecheckchange
20397          * Fires before the checked value is set, providing an opportunity to cancel if needed
20398          * @param {Roo.menu.CheckItem} this
20399          * @param {Boolean} checked The new checked value that will be set
20400          */
20401         "beforecheckchange" : true,
20402         /**
20403          * @event checkchange
20404          * Fires after the checked value has been set
20405          * @param {Roo.menu.CheckItem} this
20406          * @param {Boolean} checked The checked value that was set
20407          */
20408         "checkchange" : true
20409     });
20410     if(this.checkHandler){
20411         this.on('checkchange', this.checkHandler, this.scope);
20412     }
20413 };
20414 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20415     /**
20416      * @cfg {String} group
20417      * All check items with the same group name will automatically be grouped into a single-select
20418      * radio button group (defaults to '')
20419      */
20420     /**
20421      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20422      */
20423     itemCls : "x-menu-item x-menu-check-item",
20424     /**
20425      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20426      */
20427     groupClass : "x-menu-group-item",
20428
20429     /**
20430      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20431      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20432      * initialized with checked = true will be rendered as checked.
20433      */
20434     checked: false,
20435
20436     // private
20437     ctype: "Roo.menu.CheckItem",
20438
20439     // private
20440     onRender : function(c){
20441         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20442         if(this.group){
20443             this.el.addClass(this.groupClass);
20444         }
20445         Roo.menu.MenuMgr.registerCheckable(this);
20446         if(this.checked){
20447             this.checked = false;
20448             this.setChecked(true, true);
20449         }
20450     },
20451
20452     // private
20453     destroy : function(){
20454         if(this.rendered){
20455             Roo.menu.MenuMgr.unregisterCheckable(this);
20456         }
20457         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20458     },
20459
20460     /**
20461      * Set the checked state of this item
20462      * @param {Boolean} checked The new checked value
20463      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20464      */
20465     setChecked : function(state, suppressEvent){
20466         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20467             if(this.container){
20468                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20469             }
20470             this.checked = state;
20471             if(suppressEvent !== true){
20472                 this.fireEvent("checkchange", this, state);
20473             }
20474         }
20475     },
20476
20477     // private
20478     handleClick : function(e){
20479        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20480            this.setChecked(!this.checked);
20481        }
20482        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20483     }
20484 });/*
20485  * Based on:
20486  * Ext JS Library 1.1.1
20487  * Copyright(c) 2006-2007, Ext JS, LLC.
20488  *
20489  * Originally Released Under LGPL - original licence link has changed is not relivant.
20490  *
20491  * Fork - LGPL
20492  * <script type="text/javascript">
20493  */
20494  
20495 /**
20496  * @class Roo.menu.DateItem
20497  * @extends Roo.menu.Adapter
20498  * A menu item that wraps the {@link Roo.DatPicker} component.
20499  * @constructor
20500  * Creates a new DateItem
20501  * @param {Object} config Configuration options
20502  */
20503 Roo.menu.DateItem = function(config){
20504     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20505     /** The Roo.DatePicker object @type Roo.DatePicker */
20506     this.picker = this.component;
20507     this.addEvents({select: true});
20508     
20509     this.picker.on("render", function(picker){
20510         picker.getEl().swallowEvent("click");
20511         picker.container.addClass("x-menu-date-item");
20512     });
20513
20514     this.picker.on("select", this.onSelect, this);
20515 };
20516
20517 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20518     // private
20519     onSelect : function(picker, date){
20520         this.fireEvent("select", this, date, picker);
20521         Roo.menu.DateItem.superclass.handleClick.call(this);
20522     }
20523 });/*
20524  * Based on:
20525  * Ext JS Library 1.1.1
20526  * Copyright(c) 2006-2007, Ext JS, LLC.
20527  *
20528  * Originally Released Under LGPL - original licence link has changed is not relivant.
20529  *
20530  * Fork - LGPL
20531  * <script type="text/javascript">
20532  */
20533  
20534 /**
20535  * @class Roo.menu.ColorItem
20536  * @extends Roo.menu.Adapter
20537  * A menu item that wraps the {@link Roo.ColorPalette} component.
20538  * @constructor
20539  * Creates a new ColorItem
20540  * @param {Object} config Configuration options
20541  */
20542 Roo.menu.ColorItem = function(config){
20543     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20544     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20545     this.palette = this.component;
20546     this.relayEvents(this.palette, ["select"]);
20547     if(this.selectHandler){
20548         this.on('select', this.selectHandler, this.scope);
20549     }
20550 };
20551 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20552  * Based on:
20553  * Ext JS Library 1.1.1
20554  * Copyright(c) 2006-2007, Ext JS, LLC.
20555  *
20556  * Originally Released Under LGPL - original licence link has changed is not relivant.
20557  *
20558  * Fork - LGPL
20559  * <script type="text/javascript">
20560  */
20561  
20562
20563 /**
20564  * @class Roo.menu.DateMenu
20565  * @extends Roo.menu.Menu
20566  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20567  * @constructor
20568  * Creates a new DateMenu
20569  * @param {Object} config Configuration options
20570  */
20571 Roo.menu.DateMenu = function(config){
20572     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20573     this.plain = true;
20574     var di = new Roo.menu.DateItem(config);
20575     this.add(di);
20576     /**
20577      * The {@link Roo.DatePicker} instance for this DateMenu
20578      * @type DatePicker
20579      */
20580     this.picker = di.picker;
20581     /**
20582      * @event select
20583      * @param {DatePicker} picker
20584      * @param {Date} date
20585      */
20586     this.relayEvents(di, ["select"]);
20587     this.on('beforeshow', function(){
20588         if(this.picker){
20589             this.picker.hideMonthPicker(false);
20590         }
20591     }, this);
20592 };
20593 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20594     cls:'x-date-menu'
20595 });/*
20596  * Based on:
20597  * Ext JS Library 1.1.1
20598  * Copyright(c) 2006-2007, Ext JS, LLC.
20599  *
20600  * Originally Released Under LGPL - original licence link has changed is not relivant.
20601  *
20602  * Fork - LGPL
20603  * <script type="text/javascript">
20604  */
20605  
20606
20607 /**
20608  * @class Roo.menu.ColorMenu
20609  * @extends Roo.menu.Menu
20610  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20611  * @constructor
20612  * Creates a new ColorMenu
20613  * @param {Object} config Configuration options
20614  */
20615 Roo.menu.ColorMenu = function(config){
20616     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20617     this.plain = true;
20618     var ci = new Roo.menu.ColorItem(config);
20619     this.add(ci);
20620     /**
20621      * The {@link Roo.ColorPalette} instance for this ColorMenu
20622      * @type ColorPalette
20623      */
20624     this.palette = ci.palette;
20625     /**
20626      * @event select
20627      * @param {ColorPalette} palette
20628      * @param {String} color
20629      */
20630     this.relayEvents(ci, ["select"]);
20631 };
20632 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20633  * Based on:
20634  * Ext JS Library 1.1.1
20635  * Copyright(c) 2006-2007, Ext JS, LLC.
20636  *
20637  * Originally Released Under LGPL - original licence link has changed is not relivant.
20638  *
20639  * Fork - LGPL
20640  * <script type="text/javascript">
20641  */
20642  
20643 /**
20644  * @class Roo.form.Field
20645  * @extends Roo.BoxComponent
20646  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20647  * @constructor
20648  * Creates a new Field
20649  * @param {Object} config Configuration options
20650  */
20651 Roo.form.Field = function(config){
20652     Roo.form.Field.superclass.constructor.call(this, config);
20653 };
20654
20655 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20656     /**
20657      * @cfg {String} fieldLabel Label to use when rendering a form.
20658      */
20659        /**
20660      * @cfg {String} qtip Mouse over tip
20661      */
20662      
20663     /**
20664      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20665      */
20666     invalidClass : "x-form-invalid",
20667     /**
20668      * @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")
20669      */
20670     invalidText : "The value in this field is invalid",
20671     /**
20672      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20673      */
20674     focusClass : "x-form-focus",
20675     /**
20676      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20677       automatic validation (defaults to "keyup").
20678      */
20679     validationEvent : "keyup",
20680     /**
20681      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20682      */
20683     validateOnBlur : true,
20684     /**
20685      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20686      */
20687     validationDelay : 250,
20688     /**
20689      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20690      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20691      */
20692     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
20693     /**
20694      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20695      */
20696     fieldClass : "x-form-field",
20697     /**
20698      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20699      *<pre>
20700 Value         Description
20701 -----------   ----------------------------------------------------------------------
20702 qtip          Display a quick tip when the user hovers over the field
20703 title         Display a default browser title attribute popup
20704 under         Add a block div beneath the field containing the error text
20705 side          Add an error icon to the right of the field with a popup on hover
20706 [element id]  Add the error text directly to the innerHTML of the specified element
20707 </pre>
20708      */
20709     msgTarget : 'qtip',
20710     /**
20711      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20712      */
20713     msgFx : 'normal',
20714
20715     /**
20716      * @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.
20717      */
20718     readOnly : false,
20719
20720     /**
20721      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20722      */
20723     disabled : false,
20724
20725     /**
20726      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20727      */
20728     inputType : undefined,
20729     
20730     /**
20731      * @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).
20732          */
20733         tabIndex : undefined,
20734         
20735     // private
20736     isFormField : true,
20737
20738     // private
20739     hasFocus : false,
20740     /**
20741      * @property {Roo.Element} fieldEl
20742      * Element Containing the rendered Field (with label etc.)
20743      */
20744     /**
20745      * @cfg {Mixed} value A value to initialize this field with.
20746      */
20747     value : undefined,
20748
20749     /**
20750      * @cfg {String} name The field's HTML name attribute.
20751      */
20752     /**
20753      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20754      */
20755
20756         // private ??
20757         initComponent : function(){
20758         Roo.form.Field.superclass.initComponent.call(this);
20759         this.addEvents({
20760             /**
20761              * @event focus
20762              * Fires when this field receives input focus.
20763              * @param {Roo.form.Field} this
20764              */
20765             focus : true,
20766             /**
20767              * @event blur
20768              * Fires when this field loses input focus.
20769              * @param {Roo.form.Field} this
20770              */
20771             blur : true,
20772             /**
20773              * @event specialkey
20774              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20775              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20776              * @param {Roo.form.Field} this
20777              * @param {Roo.EventObject} e The event object
20778              */
20779             specialkey : true,
20780             /**
20781              * @event change
20782              * Fires just before the field blurs if the field value has changed.
20783              * @param {Roo.form.Field} this
20784              * @param {Mixed} newValue The new value
20785              * @param {Mixed} oldValue The original value
20786              */
20787             change : true,
20788             /**
20789              * @event invalid
20790              * Fires after the field has been marked as invalid.
20791              * @param {Roo.form.Field} this
20792              * @param {String} msg The validation message
20793              */
20794             invalid : true,
20795             /**
20796              * @event valid
20797              * Fires after the field has been validated with no errors.
20798              * @param {Roo.form.Field} this
20799              */
20800             valid : true,
20801              /**
20802              * @event keyup
20803              * Fires after the key up
20804              * @param {Roo.form.Field} this
20805              * @param {Roo.EventObject}  e The event Object
20806              */
20807             keyup : true
20808         });
20809     },
20810
20811     /**
20812      * Returns the name attribute of the field if available
20813      * @return {String} name The field name
20814      */
20815     getName: function(){
20816          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20817     },
20818
20819     // private
20820     onRender : function(ct, position){
20821         Roo.form.Field.superclass.onRender.call(this, ct, position);
20822         if(!this.el){
20823             var cfg = this.getAutoCreate();
20824             if(!cfg.name){
20825                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20826             }
20827             if (!cfg.name.length) {
20828                 delete cfg.name;
20829             }
20830             if(this.inputType){
20831                 cfg.type = this.inputType;
20832             }
20833             this.el = ct.createChild(cfg, position);
20834         }
20835         var type = this.el.dom.type;
20836         if(type){
20837             if(type == 'password'){
20838                 type = 'text';
20839             }
20840             this.el.addClass('x-form-'+type);
20841         }
20842         if(this.readOnly){
20843             this.el.dom.readOnly = true;
20844         }
20845         if(this.tabIndex !== undefined){
20846             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20847         }
20848
20849         this.el.addClass([this.fieldClass, this.cls]);
20850         this.initValue();
20851     },
20852
20853     /**
20854      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20855      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20856      * @return {Roo.form.Field} this
20857      */
20858     applyTo : function(target){
20859         this.allowDomMove = false;
20860         this.el = Roo.get(target);
20861         this.render(this.el.dom.parentNode);
20862         return this;
20863     },
20864
20865     // private
20866     initValue : function(){
20867         if(this.value !== undefined){
20868             this.setValue(this.value);
20869         }else if(this.el.dom.value.length > 0){
20870             this.setValue(this.el.dom.value);
20871         }
20872     },
20873
20874     /**
20875      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20876      */
20877     isDirty : function() {
20878         if(this.disabled) {
20879             return false;
20880         }
20881         return String(this.getValue()) !== String(this.originalValue);
20882     },
20883
20884     // private
20885     afterRender : function(){
20886         Roo.form.Field.superclass.afterRender.call(this);
20887         this.initEvents();
20888     },
20889
20890     // private
20891     fireKey : function(e){
20892         //Roo.log('field ' + e.getKey());
20893         if(e.isNavKeyPress()){
20894             this.fireEvent("specialkey", this, e);
20895         }
20896     },
20897
20898     /**
20899      * Resets the current field value to the originally loaded value and clears any validation messages
20900      */
20901     reset : function(){
20902         this.setValue(this.resetValue);
20903         this.clearInvalid();
20904     },
20905
20906     // private
20907     initEvents : function(){
20908         // safari killled keypress - so keydown is now used..
20909         this.el.on("keydown" , this.fireKey,  this);
20910         this.el.on("focus", this.onFocus,  this);
20911         this.el.on("blur", this.onBlur,  this);
20912         this.el.relayEvent('keyup', this);
20913
20914         // reference to original value for reset
20915         this.originalValue = this.getValue();
20916         this.resetValue =  this.getValue();
20917     },
20918
20919     // private
20920     onFocus : function(){
20921         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20922             this.el.addClass(this.focusClass);
20923         }
20924         if(!this.hasFocus){
20925             this.hasFocus = true;
20926             this.startValue = this.getValue();
20927             this.fireEvent("focus", this);
20928         }
20929     },
20930
20931     beforeBlur : Roo.emptyFn,
20932
20933     // private
20934     onBlur : function(){
20935         this.beforeBlur();
20936         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20937             this.el.removeClass(this.focusClass);
20938         }
20939         this.hasFocus = false;
20940         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20941             this.validate();
20942         }
20943         var v = this.getValue();
20944         if(String(v) !== String(this.startValue)){
20945             this.fireEvent('change', this, v, this.startValue);
20946         }
20947         this.fireEvent("blur", this);
20948     },
20949
20950     /**
20951      * Returns whether or not the field value is currently valid
20952      * @param {Boolean} preventMark True to disable marking the field invalid
20953      * @return {Boolean} True if the value is valid, else false
20954      */
20955     isValid : function(preventMark){
20956         if(this.disabled){
20957             return true;
20958         }
20959         var restore = this.preventMark;
20960         this.preventMark = preventMark === true;
20961         var v = this.validateValue(this.processValue(this.getRawValue()));
20962         this.preventMark = restore;
20963         return v;
20964     },
20965
20966     /**
20967      * Validates the field value
20968      * @return {Boolean} True if the value is valid, else false
20969      */
20970     validate : function(){
20971         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20972             this.clearInvalid();
20973             return true;
20974         }
20975         return false;
20976     },
20977
20978     processValue : function(value){
20979         return value;
20980     },
20981
20982     // private
20983     // Subclasses should provide the validation implementation by overriding this
20984     validateValue : function(value){
20985         return true;
20986     },
20987
20988     /**
20989      * Mark this field as invalid
20990      * @param {String} msg The validation message
20991      */
20992     markInvalid : function(msg){
20993         if(!this.rendered || this.preventMark){ // not rendered
20994             return;
20995         }
20996         
20997         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20998         
20999         obj.el.addClass(this.invalidClass);
21000         msg = msg || this.invalidText;
21001         switch(this.msgTarget){
21002             case 'qtip':
21003                 obj.el.dom.qtip = msg;
21004                 obj.el.dom.qclass = 'x-form-invalid-tip';
21005                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21006                     Roo.QuickTips.enable();
21007                 }
21008                 break;
21009             case 'title':
21010                 this.el.dom.title = msg;
21011                 break;
21012             case 'under':
21013                 if(!this.errorEl){
21014                     var elp = this.el.findParent('.x-form-element', 5, true);
21015                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21016                     this.errorEl.setWidth(elp.getWidth(true)-20);
21017                 }
21018                 this.errorEl.update(msg);
21019                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21020                 break;
21021             case 'side':
21022                 if(!this.errorIcon){
21023                     var elp = this.el.findParent('.x-form-element', 5, true);
21024                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21025                 }
21026                 this.alignErrorIcon();
21027                 this.errorIcon.dom.qtip = msg;
21028                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21029                 this.errorIcon.show();
21030                 this.on('resize', this.alignErrorIcon, this);
21031                 break;
21032             default:
21033                 var t = Roo.getDom(this.msgTarget);
21034                 t.innerHTML = msg;
21035                 t.style.display = this.msgDisplay;
21036                 break;
21037         }
21038         this.fireEvent('invalid', this, msg);
21039     },
21040
21041     // private
21042     alignErrorIcon : function(){
21043         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21044     },
21045
21046     /**
21047      * Clear any invalid styles/messages for this field
21048      */
21049     clearInvalid : function(){
21050         if(!this.rendered || this.preventMark){ // not rendered
21051             return;
21052         }
21053         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
21054         
21055         obj.el.removeClass(this.invalidClass);
21056         switch(this.msgTarget){
21057             case 'qtip':
21058                 obj.el.dom.qtip = '';
21059                 break;
21060             case 'title':
21061                 this.el.dom.title = '';
21062                 break;
21063             case 'under':
21064                 if(this.errorEl){
21065                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21066                 }
21067                 break;
21068             case 'side':
21069                 if(this.errorIcon){
21070                     this.errorIcon.dom.qtip = '';
21071                     this.errorIcon.hide();
21072                     this.un('resize', this.alignErrorIcon, this);
21073                 }
21074                 break;
21075             default:
21076                 var t = Roo.getDom(this.msgTarget);
21077                 t.innerHTML = '';
21078                 t.style.display = 'none';
21079                 break;
21080         }
21081         this.fireEvent('valid', this);
21082     },
21083
21084     /**
21085      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21086      * @return {Mixed} value The field value
21087      */
21088     getRawValue : function(){
21089         var v = this.el.getValue();
21090         
21091         return v;
21092     },
21093
21094     /**
21095      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21096      * @return {Mixed} value The field value
21097      */
21098     getValue : function(){
21099         var v = this.el.getValue();
21100          
21101         return v;
21102     },
21103
21104     /**
21105      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21106      * @param {Mixed} value The value to set
21107      */
21108     setRawValue : function(v){
21109         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21110     },
21111
21112     /**
21113      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21114      * @param {Mixed} value The value to set
21115      */
21116     setValue : function(v){
21117         this.value = v;
21118         if(this.rendered){
21119             this.el.dom.value = (v === null || v === undefined ? '' : v);
21120              this.validate();
21121         }
21122     },
21123
21124     adjustSize : function(w, h){
21125         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21126         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21127         return s;
21128     },
21129
21130     adjustWidth : function(tag, w){
21131         tag = tag.toLowerCase();
21132         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21133             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21134                 if(tag == 'input'){
21135                     return w + 2;
21136                 }
21137                 if(tag == 'textarea'){
21138                     return w-2;
21139                 }
21140             }else if(Roo.isOpera){
21141                 if(tag == 'input'){
21142                     return w + 2;
21143                 }
21144                 if(tag == 'textarea'){
21145                     return w-2;
21146                 }
21147             }
21148         }
21149         return w;
21150     }
21151 });
21152
21153
21154 // anything other than normal should be considered experimental
21155 Roo.form.Field.msgFx = {
21156     normal : {
21157         show: function(msgEl, f){
21158             msgEl.setDisplayed('block');
21159         },
21160
21161         hide : function(msgEl, f){
21162             msgEl.setDisplayed(false).update('');
21163         }
21164     },
21165
21166     slide : {
21167         show: function(msgEl, f){
21168             msgEl.slideIn('t', {stopFx:true});
21169         },
21170
21171         hide : function(msgEl, f){
21172             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21173         }
21174     },
21175
21176     slideRight : {
21177         show: function(msgEl, f){
21178             msgEl.fixDisplay();
21179             msgEl.alignTo(f.el, 'tl-tr');
21180             msgEl.slideIn('l', {stopFx:true});
21181         },
21182
21183         hide : function(msgEl, f){
21184             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21185         }
21186     }
21187 };/*
21188  * Based on:
21189  * Ext JS Library 1.1.1
21190  * Copyright(c) 2006-2007, Ext JS, LLC.
21191  *
21192  * Originally Released Under LGPL - original licence link has changed is not relivant.
21193  *
21194  * Fork - LGPL
21195  * <script type="text/javascript">
21196  */
21197  
21198
21199 /**
21200  * @class Roo.form.TextField
21201  * @extends Roo.form.Field
21202  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21203  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21204  * @constructor
21205  * Creates a new TextField
21206  * @param {Object} config Configuration options
21207  */
21208 Roo.form.TextField = function(config){
21209     Roo.form.TextField.superclass.constructor.call(this, config);
21210     this.addEvents({
21211         /**
21212          * @event autosize
21213          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21214          * according to the default logic, but this event provides a hook for the developer to apply additional
21215          * logic at runtime to resize the field if needed.
21216              * @param {Roo.form.Field} this This text field
21217              * @param {Number} width The new field width
21218              */
21219         autosize : true
21220     });
21221 };
21222
21223 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21224     /**
21225      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21226      */
21227     grow : false,
21228     /**
21229      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21230      */
21231     growMin : 30,
21232     /**
21233      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21234      */
21235     growMax : 800,
21236     /**
21237      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21238      */
21239     vtype : null,
21240     /**
21241      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21242      */
21243     maskRe : null,
21244     /**
21245      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21246      */
21247     disableKeyFilter : false,
21248     /**
21249      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21250      */
21251     allowBlank : true,
21252     /**
21253      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21254      */
21255     minLength : 0,
21256     /**
21257      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21258      */
21259     maxLength : Number.MAX_VALUE,
21260     /**
21261      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21262      */
21263     minLengthText : "The minimum length for this field is {0}",
21264     /**
21265      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21266      */
21267     maxLengthText : "The maximum length for this field is {0}",
21268     /**
21269      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21270      */
21271     selectOnFocus : false,
21272     /**
21273      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21274      */
21275     blankText : "This field is required",
21276     /**
21277      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21278      * If available, this function will be called only after the basic validators all return true, and will be passed the
21279      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21280      */
21281     validator : null,
21282     /**
21283      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21284      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21285      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21286      */
21287     regex : null,
21288     /**
21289      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21290      */
21291     regexText : "",
21292     /**
21293      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21294      */
21295     emptyText : null,
21296    
21297
21298     // private
21299     initEvents : function()
21300     {
21301         if (this.emptyText) {
21302             this.el.attr('placeholder', this.emptyText);
21303         }
21304         
21305         Roo.form.TextField.superclass.initEvents.call(this);
21306         if(this.validationEvent == 'keyup'){
21307             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21308             this.el.on('keyup', this.filterValidation, this);
21309         }
21310         else if(this.validationEvent !== false){
21311             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21312         }
21313         
21314         if(this.selectOnFocus){
21315             this.on("focus", this.preFocus, this);
21316             
21317         }
21318         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21319             this.el.on("keypress", this.filterKeys, this);
21320         }
21321         if(this.grow){
21322             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21323             this.el.on("click", this.autoSize,  this);
21324         }
21325         if(this.el.is('input[type=password]') && Roo.isSafari){
21326             this.el.on('keydown', this.SafariOnKeyDown, this);
21327         }
21328     },
21329
21330     processValue : function(value){
21331         if(this.stripCharsRe){
21332             var newValue = value.replace(this.stripCharsRe, '');
21333             if(newValue !== value){
21334                 this.setRawValue(newValue);
21335                 return newValue;
21336             }
21337         }
21338         return value;
21339     },
21340
21341     filterValidation : function(e){
21342         if(!e.isNavKeyPress()){
21343             this.validationTask.delay(this.validationDelay);
21344         }
21345     },
21346
21347     // private
21348     onKeyUp : function(e){
21349         if(!e.isNavKeyPress()){
21350             this.autoSize();
21351         }
21352     },
21353
21354     /**
21355      * Resets the current field value to the originally-loaded value and clears any validation messages.
21356      *  
21357      */
21358     reset : function(){
21359         Roo.form.TextField.superclass.reset.call(this);
21360        
21361     },
21362
21363     
21364     // private
21365     preFocus : function(){
21366         
21367         if(this.selectOnFocus){
21368             this.el.dom.select();
21369         }
21370     },
21371
21372     
21373     // private
21374     filterKeys : function(e){
21375         var k = e.getKey();
21376         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21377             return;
21378         }
21379         var c = e.getCharCode(), cc = String.fromCharCode(c);
21380         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21381             return;
21382         }
21383         if(!this.maskRe.test(cc)){
21384             e.stopEvent();
21385         }
21386     },
21387
21388     setValue : function(v){
21389         
21390         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21391         
21392         this.autoSize();
21393     },
21394
21395     /**
21396      * Validates a value according to the field's validation rules and marks the field as invalid
21397      * if the validation fails
21398      * @param {Mixed} value The value to validate
21399      * @return {Boolean} True if the value is valid, else false
21400      */
21401     validateValue : function(value){
21402         if(value.length < 1)  { // if it's blank
21403              if(this.allowBlank){
21404                 this.clearInvalid();
21405                 return true;
21406              }else{
21407                 this.markInvalid(this.blankText);
21408                 return false;
21409              }
21410         }
21411         if(value.length < this.minLength){
21412             this.markInvalid(String.format(this.minLengthText, this.minLength));
21413             return false;
21414         }
21415         if(value.length > this.maxLength){
21416             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21417             return false;
21418         }
21419         if(this.vtype){
21420             var vt = Roo.form.VTypes;
21421             if(!vt[this.vtype](value, this)){
21422                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21423                 return false;
21424             }
21425         }
21426         if(typeof this.validator == "function"){
21427             var msg = this.validator(value);
21428             if(msg !== true){
21429                 this.markInvalid(msg);
21430                 return false;
21431             }
21432         }
21433         if(this.regex && !this.regex.test(value)){
21434             this.markInvalid(this.regexText);
21435             return false;
21436         }
21437         return true;
21438     },
21439
21440     /**
21441      * Selects text in this field
21442      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21443      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21444      */
21445     selectText : function(start, end){
21446         var v = this.getRawValue();
21447         if(v.length > 0){
21448             start = start === undefined ? 0 : start;
21449             end = end === undefined ? v.length : end;
21450             var d = this.el.dom;
21451             if(d.setSelectionRange){
21452                 d.setSelectionRange(start, end);
21453             }else if(d.createTextRange){
21454                 var range = d.createTextRange();
21455                 range.moveStart("character", start);
21456                 range.moveEnd("character", v.length-end);
21457                 range.select();
21458             }
21459         }
21460     },
21461
21462     /**
21463      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21464      * This only takes effect if grow = true, and fires the autosize event.
21465      */
21466     autoSize : function(){
21467         if(!this.grow || !this.rendered){
21468             return;
21469         }
21470         if(!this.metrics){
21471             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21472         }
21473         var el = this.el;
21474         var v = el.dom.value;
21475         var d = document.createElement('div');
21476         d.appendChild(document.createTextNode(v));
21477         v = d.innerHTML;
21478         d = null;
21479         v += "&#160;";
21480         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21481         this.el.setWidth(w);
21482         this.fireEvent("autosize", this, w);
21483     },
21484     
21485     // private
21486     SafariOnKeyDown : function(event)
21487     {
21488         // this is a workaround for a password hang bug on chrome/ webkit.
21489         
21490         var isSelectAll = false;
21491         
21492         if(this.el.dom.selectionEnd > 0){
21493             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21494         }
21495         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21496             event.preventDefault();
21497             this.setValue('');
21498             return;
21499         }
21500         
21501         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
21502             
21503             event.preventDefault();
21504             // this is very hacky as keydown always get's upper case.
21505             
21506             var cc = String.fromCharCode(event.getCharCode());
21507             
21508             
21509             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21510             
21511         }
21512         
21513         
21514     }
21515 });/*
21516  * Based on:
21517  * Ext JS Library 1.1.1
21518  * Copyright(c) 2006-2007, Ext JS, LLC.
21519  *
21520  * Originally Released Under LGPL - original licence link has changed is not relivant.
21521  *
21522  * Fork - LGPL
21523  * <script type="text/javascript">
21524  */
21525  
21526 /**
21527  * @class Roo.form.Hidden
21528  * @extends Roo.form.TextField
21529  * Simple Hidden element used on forms 
21530  * 
21531  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21532  * 
21533  * @constructor
21534  * Creates a new Hidden form element.
21535  * @param {Object} config Configuration options
21536  */
21537
21538
21539
21540 // easy hidden field...
21541 Roo.form.Hidden = function(config){
21542     Roo.form.Hidden.superclass.constructor.call(this, config);
21543 };
21544   
21545 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21546     fieldLabel:      '',
21547     inputType:      'hidden',
21548     width:          50,
21549     allowBlank:     true,
21550     labelSeparator: '',
21551     hidden:         true,
21552     itemCls :       'x-form-item-display-none'
21553
21554
21555 });
21556
21557
21558 /*
21559  * Based on:
21560  * Ext JS Library 1.1.1
21561  * Copyright(c) 2006-2007, Ext JS, LLC.
21562  *
21563  * Originally Released Under LGPL - original licence link has changed is not relivant.
21564  *
21565  * Fork - LGPL
21566  * <script type="text/javascript">
21567  */
21568  
21569 /**
21570  * @class Roo.form.TriggerField
21571  * @extends Roo.form.TextField
21572  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21573  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21574  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21575  * for which you can provide a custom implementation.  For example:
21576  * <pre><code>
21577 var trigger = new Roo.form.TriggerField();
21578 trigger.onTriggerClick = myTriggerFn;
21579 trigger.applyTo('my-field');
21580 </code></pre>
21581  *
21582  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21583  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21584  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21585  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21586  * @constructor
21587  * Create a new TriggerField.
21588  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21589  * to the base TextField)
21590  */
21591 Roo.form.TriggerField = function(config){
21592     this.mimicing = false;
21593     Roo.form.TriggerField.superclass.constructor.call(this, config);
21594 };
21595
21596 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21597     /**
21598      * @cfg {String} triggerClass A CSS class to apply to the trigger
21599      */
21600     /**
21601      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21602      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21603      */
21604     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
21605     /**
21606      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21607      */
21608     hideTrigger:false,
21609
21610     /** @cfg {Boolean} grow @hide */
21611     /** @cfg {Number} growMin @hide */
21612     /** @cfg {Number} growMax @hide */
21613
21614     /**
21615      * @hide 
21616      * @method
21617      */
21618     autoSize: Roo.emptyFn,
21619     // private
21620     monitorTab : true,
21621     // private
21622     deferHeight : true,
21623
21624     
21625     actionMode : 'wrap',
21626     // private
21627     onResize : function(w, h){
21628         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21629         if(typeof w == 'number'){
21630             var x = w - this.trigger.getWidth();
21631             this.el.setWidth(this.adjustWidth('input', x));
21632             this.trigger.setStyle('left', x+'px');
21633         }
21634     },
21635
21636     // private
21637     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21638
21639     // private
21640     getResizeEl : function(){
21641         return this.wrap;
21642     },
21643
21644     // private
21645     getPositionEl : function(){
21646         return this.wrap;
21647     },
21648
21649     // private
21650     alignErrorIcon : function(){
21651         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21652     },
21653
21654     // private
21655     onRender : function(ct, position){
21656         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21657         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21658         this.trigger = this.wrap.createChild(this.triggerConfig ||
21659                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21660         if(this.hideTrigger){
21661             this.trigger.setDisplayed(false);
21662         }
21663         this.initTrigger();
21664         if(!this.width){
21665             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21666         }
21667     },
21668
21669     // private
21670     initTrigger : function(){
21671         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21672         this.trigger.addClassOnOver('x-form-trigger-over');
21673         this.trigger.addClassOnClick('x-form-trigger-click');
21674     },
21675
21676     // private
21677     onDestroy : function(){
21678         if(this.trigger){
21679             this.trigger.removeAllListeners();
21680             this.trigger.remove();
21681         }
21682         if(this.wrap){
21683             this.wrap.remove();
21684         }
21685         Roo.form.TriggerField.superclass.onDestroy.call(this);
21686     },
21687
21688     // private
21689     onFocus : function(){
21690         Roo.form.TriggerField.superclass.onFocus.call(this);
21691         if(!this.mimicing){
21692             this.wrap.addClass('x-trigger-wrap-focus');
21693             this.mimicing = true;
21694             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21695             if(this.monitorTab){
21696                 this.el.on("keydown", this.checkTab, this);
21697             }
21698         }
21699     },
21700
21701     // private
21702     checkTab : function(e){
21703         if(e.getKey() == e.TAB){
21704             this.triggerBlur();
21705         }
21706     },
21707
21708     // private
21709     onBlur : function(){
21710         // do nothing
21711     },
21712
21713     // private
21714     mimicBlur : function(e, t){
21715         if(!this.wrap.contains(t) && this.validateBlur()){
21716             this.triggerBlur();
21717         }
21718     },
21719
21720     // private
21721     triggerBlur : function(){
21722         this.mimicing = false;
21723         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21724         if(this.monitorTab){
21725             this.el.un("keydown", this.checkTab, this);
21726         }
21727         this.wrap.removeClass('x-trigger-wrap-focus');
21728         Roo.form.TriggerField.superclass.onBlur.call(this);
21729     },
21730
21731     // private
21732     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21733     validateBlur : function(e, t){
21734         return true;
21735     },
21736
21737     // private
21738     onDisable : function(){
21739         Roo.form.TriggerField.superclass.onDisable.call(this);
21740         if(this.wrap){
21741             this.wrap.addClass('x-item-disabled');
21742         }
21743     },
21744
21745     // private
21746     onEnable : function(){
21747         Roo.form.TriggerField.superclass.onEnable.call(this);
21748         if(this.wrap){
21749             this.wrap.removeClass('x-item-disabled');
21750         }
21751     },
21752
21753     // private
21754     onShow : function(){
21755         var ae = this.getActionEl();
21756         
21757         if(ae){
21758             ae.dom.style.display = '';
21759             ae.dom.style.visibility = 'visible';
21760         }
21761     },
21762
21763     // private
21764     
21765     onHide : function(){
21766         var ae = this.getActionEl();
21767         ae.dom.style.display = 'none';
21768     },
21769
21770     /**
21771      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21772      * by an implementing function.
21773      * @method
21774      * @param {EventObject} e
21775      */
21776     onTriggerClick : Roo.emptyFn
21777 });
21778
21779 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21780 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21781 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21782 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21783     initComponent : function(){
21784         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21785
21786         this.triggerConfig = {
21787             tag:'span', cls:'x-form-twin-triggers', cn:[
21788             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21789             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21790         ]};
21791     },
21792
21793     getTrigger : function(index){
21794         return this.triggers[index];
21795     },
21796
21797     initTrigger : function(){
21798         var ts = this.trigger.select('.x-form-trigger', true);
21799         this.wrap.setStyle('overflow', 'hidden');
21800         var triggerField = this;
21801         ts.each(function(t, all, index){
21802             t.hide = function(){
21803                 var w = triggerField.wrap.getWidth();
21804                 this.dom.style.display = 'none';
21805                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21806             };
21807             t.show = function(){
21808                 var w = triggerField.wrap.getWidth();
21809                 this.dom.style.display = '';
21810                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21811             };
21812             var triggerIndex = 'Trigger'+(index+1);
21813
21814             if(this['hide'+triggerIndex]){
21815                 t.dom.style.display = 'none';
21816             }
21817             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21818             t.addClassOnOver('x-form-trigger-over');
21819             t.addClassOnClick('x-form-trigger-click');
21820         }, this);
21821         this.triggers = ts.elements;
21822     },
21823
21824     onTrigger1Click : Roo.emptyFn,
21825     onTrigger2Click : Roo.emptyFn
21826 });/*
21827  * Based on:
21828  * Ext JS Library 1.1.1
21829  * Copyright(c) 2006-2007, Ext JS, LLC.
21830  *
21831  * Originally Released Under LGPL - original licence link has changed is not relivant.
21832  *
21833  * Fork - LGPL
21834  * <script type="text/javascript">
21835  */
21836  
21837 /**
21838  * @class Roo.form.TextArea
21839  * @extends Roo.form.TextField
21840  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21841  * support for auto-sizing.
21842  * @constructor
21843  * Creates a new TextArea
21844  * @param {Object} config Configuration options
21845  */
21846 Roo.form.TextArea = function(config){
21847     Roo.form.TextArea.superclass.constructor.call(this, config);
21848     // these are provided exchanges for backwards compat
21849     // minHeight/maxHeight were replaced by growMin/growMax to be
21850     // compatible with TextField growing config values
21851     if(this.minHeight !== undefined){
21852         this.growMin = this.minHeight;
21853     }
21854     if(this.maxHeight !== undefined){
21855         this.growMax = this.maxHeight;
21856     }
21857 };
21858
21859 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21860     /**
21861      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21862      */
21863     growMin : 60,
21864     /**
21865      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21866      */
21867     growMax: 1000,
21868     /**
21869      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21870      * in the field (equivalent to setting overflow: hidden, defaults to false)
21871      */
21872     preventScrollbars: false,
21873     /**
21874      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21875      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21876      */
21877
21878     // private
21879     onRender : function(ct, position){
21880         if(!this.el){
21881             this.defaultAutoCreate = {
21882                 tag: "textarea",
21883                 style:"width:300px;height:60px;",
21884                 autocomplete: "new-password"
21885             };
21886         }
21887         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21888         if(this.grow){
21889             this.textSizeEl = Roo.DomHelper.append(document.body, {
21890                 tag: "pre", cls: "x-form-grow-sizer"
21891             });
21892             if(this.preventScrollbars){
21893                 this.el.setStyle("overflow", "hidden");
21894             }
21895             this.el.setHeight(this.growMin);
21896         }
21897     },
21898
21899     onDestroy : function(){
21900         if(this.textSizeEl){
21901             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21902         }
21903         Roo.form.TextArea.superclass.onDestroy.call(this);
21904     },
21905
21906     // private
21907     onKeyUp : function(e){
21908         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21909             this.autoSize();
21910         }
21911     },
21912
21913     /**
21914      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21915      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21916      */
21917     autoSize : function(){
21918         if(!this.grow || !this.textSizeEl){
21919             return;
21920         }
21921         var el = this.el;
21922         var v = el.dom.value;
21923         var ts = this.textSizeEl;
21924
21925         ts.innerHTML = '';
21926         ts.appendChild(document.createTextNode(v));
21927         v = ts.innerHTML;
21928
21929         Roo.fly(ts).setWidth(this.el.getWidth());
21930         if(v.length < 1){
21931             v = "&#160;&#160;";
21932         }else{
21933             if(Roo.isIE){
21934                 v = v.replace(/\n/g, '<p>&#160;</p>');
21935             }
21936             v += "&#160;\n&#160;";
21937         }
21938         ts.innerHTML = v;
21939         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21940         if(h != this.lastHeight){
21941             this.lastHeight = h;
21942             this.el.setHeight(h);
21943             this.fireEvent("autosize", this, h);
21944         }
21945     }
21946 });/*
21947  * Based on:
21948  * Ext JS Library 1.1.1
21949  * Copyright(c) 2006-2007, Ext JS, LLC.
21950  *
21951  * Originally Released Under LGPL - original licence link has changed is not relivant.
21952  *
21953  * Fork - LGPL
21954  * <script type="text/javascript">
21955  */
21956  
21957
21958 /**
21959  * @class Roo.form.NumberField
21960  * @extends Roo.form.TextField
21961  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21962  * @constructor
21963  * Creates a new NumberField
21964  * @param {Object} config Configuration options
21965  */
21966 Roo.form.NumberField = function(config){
21967     Roo.form.NumberField.superclass.constructor.call(this, config);
21968 };
21969
21970 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21971     /**
21972      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21973      */
21974     fieldClass: "x-form-field x-form-num-field",
21975     /**
21976      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21977      */
21978     allowDecimals : true,
21979     /**
21980      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21981      */
21982     decimalSeparator : ".",
21983     /**
21984      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21985      */
21986     decimalPrecision : 2,
21987     /**
21988      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21989      */
21990     allowNegative : true,
21991     /**
21992      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21993      */
21994     minValue : Number.NEGATIVE_INFINITY,
21995     /**
21996      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21997      */
21998     maxValue : Number.MAX_VALUE,
21999     /**
22000      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22001      */
22002     minText : "The minimum value for this field is {0}",
22003     /**
22004      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22005      */
22006     maxText : "The maximum value for this field is {0}",
22007     /**
22008      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22009      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22010      */
22011     nanText : "{0} is not a valid number",
22012
22013     // private
22014     initEvents : function(){
22015         Roo.form.NumberField.superclass.initEvents.call(this);
22016         var allowed = "0123456789";
22017         if(this.allowDecimals){
22018             allowed += this.decimalSeparator;
22019         }
22020         if(this.allowNegative){
22021             allowed += "-";
22022         }
22023         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22024         var keyPress = function(e){
22025             var k = e.getKey();
22026             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22027                 return;
22028             }
22029             var c = e.getCharCode();
22030             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22031                 e.stopEvent();
22032             }
22033         };
22034         this.el.on("keypress", keyPress, this);
22035     },
22036
22037     // private
22038     validateValue : function(value){
22039         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22040             return false;
22041         }
22042         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22043              return true;
22044         }
22045         var num = this.parseValue(value);
22046         if(isNaN(num)){
22047             this.markInvalid(String.format(this.nanText, value));
22048             return false;
22049         }
22050         if(num < this.minValue){
22051             this.markInvalid(String.format(this.minText, this.minValue));
22052             return false;
22053         }
22054         if(num > this.maxValue){
22055             this.markInvalid(String.format(this.maxText, this.maxValue));
22056             return false;
22057         }
22058         return true;
22059     },
22060
22061     getValue : function(){
22062         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22063     },
22064
22065     // private
22066     parseValue : function(value){
22067         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22068         return isNaN(value) ? '' : value;
22069     },
22070
22071     // private
22072     fixPrecision : function(value){
22073         var nan = isNaN(value);
22074         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22075             return nan ? '' : value;
22076         }
22077         return parseFloat(value).toFixed(this.decimalPrecision);
22078     },
22079
22080     setValue : function(v){
22081         v = this.fixPrecision(v);
22082         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22083     },
22084
22085     // private
22086     decimalPrecisionFcn : function(v){
22087         return Math.floor(v);
22088     },
22089
22090     beforeBlur : function(){
22091         var v = this.parseValue(this.getRawValue());
22092         if(v){
22093             this.setValue(v);
22094         }
22095     }
22096 });/*
22097  * Based on:
22098  * Ext JS Library 1.1.1
22099  * Copyright(c) 2006-2007, Ext JS, LLC.
22100  *
22101  * Originally Released Under LGPL - original licence link has changed is not relivant.
22102  *
22103  * Fork - LGPL
22104  * <script type="text/javascript">
22105  */
22106  
22107 /**
22108  * @class Roo.form.DateField
22109  * @extends Roo.form.TriggerField
22110  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22111 * @constructor
22112 * Create a new DateField
22113 * @param {Object} config
22114  */
22115 Roo.form.DateField = function(config){
22116     Roo.form.DateField.superclass.constructor.call(this, config);
22117     
22118       this.addEvents({
22119          
22120         /**
22121          * @event select
22122          * Fires when a date is selected
22123              * @param {Roo.form.DateField} combo This combo box
22124              * @param {Date} date The date selected
22125              */
22126         'select' : true
22127          
22128     });
22129     
22130     
22131     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22132     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22133     this.ddMatch = null;
22134     if(this.disabledDates){
22135         var dd = this.disabledDates;
22136         var re = "(?:";
22137         for(var i = 0; i < dd.length; i++){
22138             re += dd[i];
22139             if(i != dd.length-1) re += "|";
22140         }
22141         this.ddMatch = new RegExp(re + ")");
22142     }
22143 };
22144
22145 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22146     /**
22147      * @cfg {String} format
22148      * The default date format string which can be overriden for localization support.  The format must be
22149      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22150      */
22151     format : "m/d/y",
22152     /**
22153      * @cfg {String} altFormats
22154      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22155      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22156      */
22157     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22158     /**
22159      * @cfg {Array} disabledDays
22160      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22161      */
22162     disabledDays : null,
22163     /**
22164      * @cfg {String} disabledDaysText
22165      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22166      */
22167     disabledDaysText : "Disabled",
22168     /**
22169      * @cfg {Array} disabledDates
22170      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22171      * expression so they are very powerful. Some examples:
22172      * <ul>
22173      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22174      * <li>["03/08", "09/16"] would disable those days for every year</li>
22175      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22176      * <li>["03/../2006"] would disable every day in March 2006</li>
22177      * <li>["^03"] would disable every day in every March</li>
22178      * </ul>
22179      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22180      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22181      */
22182     disabledDates : null,
22183     /**
22184      * @cfg {String} disabledDatesText
22185      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22186      */
22187     disabledDatesText : "Disabled",
22188     /**
22189      * @cfg {Date/String} minValue
22190      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22191      * valid format (defaults to null).
22192      */
22193     minValue : null,
22194     /**
22195      * @cfg {Date/String} maxValue
22196      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22197      * valid format (defaults to null).
22198      */
22199     maxValue : null,
22200     /**
22201      * @cfg {String} minText
22202      * The error text to display when the date in the cell is before minValue (defaults to
22203      * 'The date in this field must be after {minValue}').
22204      */
22205     minText : "The date in this field must be equal to or after {0}",
22206     /**
22207      * @cfg {String} maxText
22208      * The error text to display when the date in the cell is after maxValue (defaults to
22209      * 'The date in this field must be before {maxValue}').
22210      */
22211     maxText : "The date in this field must be equal to or before {0}",
22212     /**
22213      * @cfg {String} invalidText
22214      * The error text to display when the date in the field is invalid (defaults to
22215      * '{value} is not a valid date - it must be in the format {format}').
22216      */
22217     invalidText : "{0} is not a valid date - it must be in the format {1}",
22218     /**
22219      * @cfg {String} triggerClass
22220      * An additional CSS class used to style the trigger button.  The trigger will always get the
22221      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22222      * which displays a calendar icon).
22223      */
22224     triggerClass : 'x-form-date-trigger',
22225     
22226
22227     /**
22228      * @cfg {Boolean} useIso
22229      * if enabled, then the date field will use a hidden field to store the 
22230      * real value as iso formated date. default (false)
22231      */ 
22232     useIso : false,
22233     /**
22234      * @cfg {String/Object} autoCreate
22235      * A DomHelper element spec, or true for a default element spec (defaults to
22236      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22237      */ 
22238     // private
22239     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22240     
22241     // private
22242     hiddenField: false,
22243     
22244     onRender : function(ct, position)
22245     {
22246         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22247         if (this.useIso) {
22248             //this.el.dom.removeAttribute('name'); 
22249             Roo.log("Changing name?");
22250             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22251             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22252                     'before', true);
22253             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22254             // prevent input submission
22255             this.hiddenName = this.name;
22256         }
22257             
22258             
22259     },
22260     
22261     // private
22262     validateValue : function(value)
22263     {
22264         value = this.formatDate(value);
22265         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22266             Roo.log('super failed');
22267             return false;
22268         }
22269         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22270              return true;
22271         }
22272         var svalue = value;
22273         value = this.parseDate(value);
22274         if(!value){
22275             Roo.log('parse date failed' + svalue);
22276             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22277             return false;
22278         }
22279         var time = value.getTime();
22280         if(this.minValue && time < this.minValue.getTime()){
22281             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22282             return false;
22283         }
22284         if(this.maxValue && time > this.maxValue.getTime()){
22285             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22286             return false;
22287         }
22288         if(this.disabledDays){
22289             var day = value.getDay();
22290             for(var i = 0; i < this.disabledDays.length; i++) {
22291                 if(day === this.disabledDays[i]){
22292                     this.markInvalid(this.disabledDaysText);
22293                     return false;
22294                 }
22295             }
22296         }
22297         var fvalue = this.formatDate(value);
22298         if(this.ddMatch && this.ddMatch.test(fvalue)){
22299             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22300             return false;
22301         }
22302         return true;
22303     },
22304
22305     // private
22306     // Provides logic to override the default TriggerField.validateBlur which just returns true
22307     validateBlur : function(){
22308         return !this.menu || !this.menu.isVisible();
22309     },
22310     
22311     getName: function()
22312     {
22313         // returns hidden if it's set..
22314         if (!this.rendered) {return ''};
22315         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22316         
22317     },
22318
22319     /**
22320      * Returns the current date value of the date field.
22321      * @return {Date} The date value
22322      */
22323     getValue : function(){
22324         
22325         return  this.hiddenField ?
22326                 this.hiddenField.value :
22327                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22328     },
22329
22330     /**
22331      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22332      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22333      * (the default format used is "m/d/y").
22334      * <br />Usage:
22335      * <pre><code>
22336 //All of these calls set the same date value (May 4, 2006)
22337
22338 //Pass a date object:
22339 var dt = new Date('5/4/06');
22340 dateField.setValue(dt);
22341
22342 //Pass a date string (default format):
22343 dateField.setValue('5/4/06');
22344
22345 //Pass a date string (custom format):
22346 dateField.format = 'Y-m-d';
22347 dateField.setValue('2006-5-4');
22348 </code></pre>
22349      * @param {String/Date} date The date or valid date string
22350      */
22351     setValue : function(date){
22352         if (this.hiddenField) {
22353             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22354         }
22355         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22356         // make sure the value field is always stored as a date..
22357         this.value = this.parseDate(date);
22358         
22359         
22360     },
22361
22362     // private
22363     parseDate : function(value){
22364         if(!value || value instanceof Date){
22365             return value;
22366         }
22367         var v = Date.parseDate(value, this.format);
22368          if (!v && this.useIso) {
22369             v = Date.parseDate(value, 'Y-m-d');
22370         }
22371         if(!v && this.altFormats){
22372             if(!this.altFormatsArray){
22373                 this.altFormatsArray = this.altFormats.split("|");
22374             }
22375             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22376                 v = Date.parseDate(value, this.altFormatsArray[i]);
22377             }
22378         }
22379         return v;
22380     },
22381
22382     // private
22383     formatDate : function(date, fmt){
22384         return (!date || !(date instanceof Date)) ?
22385                date : date.dateFormat(fmt || this.format);
22386     },
22387
22388     // private
22389     menuListeners : {
22390         select: function(m, d){
22391             
22392             this.setValue(d);
22393             this.fireEvent('select', this, d);
22394         },
22395         show : function(){ // retain focus styling
22396             this.onFocus();
22397         },
22398         hide : function(){
22399             this.focus.defer(10, this);
22400             var ml = this.menuListeners;
22401             this.menu.un("select", ml.select,  this);
22402             this.menu.un("show", ml.show,  this);
22403             this.menu.un("hide", ml.hide,  this);
22404         }
22405     },
22406
22407     // private
22408     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22409     onTriggerClick : function(){
22410         if(this.disabled){
22411             return;
22412         }
22413         if(this.menu == null){
22414             this.menu = new Roo.menu.DateMenu();
22415         }
22416         Roo.apply(this.menu.picker,  {
22417             showClear: this.allowBlank,
22418             minDate : this.minValue,
22419             maxDate : this.maxValue,
22420             disabledDatesRE : this.ddMatch,
22421             disabledDatesText : this.disabledDatesText,
22422             disabledDays : this.disabledDays,
22423             disabledDaysText : this.disabledDaysText,
22424             format : this.useIso ? 'Y-m-d' : this.format,
22425             minText : String.format(this.minText, this.formatDate(this.minValue)),
22426             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22427         });
22428         this.menu.on(Roo.apply({}, this.menuListeners, {
22429             scope:this
22430         }));
22431         this.menu.picker.setValue(this.getValue() || new Date());
22432         this.menu.show(this.el, "tl-bl?");
22433     },
22434
22435     beforeBlur : function(){
22436         var v = this.parseDate(this.getRawValue());
22437         if(v){
22438             this.setValue(v);
22439         }
22440     },
22441
22442     /*@
22443      * overide
22444      * 
22445      */
22446     isDirty : function() {
22447         if(this.disabled) {
22448             return false;
22449         }
22450         
22451         if(typeof(this.startValue) === 'undefined'){
22452             return false;
22453         }
22454         
22455         return String(this.getValue()) !== String(this.startValue);
22456         
22457     }
22458 });/*
22459  * Based on:
22460  * Ext JS Library 1.1.1
22461  * Copyright(c) 2006-2007, Ext JS, LLC.
22462  *
22463  * Originally Released Under LGPL - original licence link has changed is not relivant.
22464  *
22465  * Fork - LGPL
22466  * <script type="text/javascript">
22467  */
22468  
22469 /**
22470  * @class Roo.form.MonthField
22471  * @extends Roo.form.TriggerField
22472  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22473 * @constructor
22474 * Create a new MonthField
22475 * @param {Object} config
22476  */
22477 Roo.form.MonthField = function(config){
22478     
22479     Roo.form.MonthField.superclass.constructor.call(this, config);
22480     
22481       this.addEvents({
22482          
22483         /**
22484          * @event select
22485          * Fires when a date is selected
22486              * @param {Roo.form.MonthFieeld} combo This combo box
22487              * @param {Date} date The date selected
22488              */
22489         'select' : true
22490          
22491     });
22492     
22493     
22494     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22495     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22496     this.ddMatch = null;
22497     if(this.disabledDates){
22498         var dd = this.disabledDates;
22499         var re = "(?:";
22500         for(var i = 0; i < dd.length; i++){
22501             re += dd[i];
22502             if(i != dd.length-1) re += "|";
22503         }
22504         this.ddMatch = new RegExp(re + ")");
22505     }
22506 };
22507
22508 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22509     /**
22510      * @cfg {String} format
22511      * The default date format string which can be overriden for localization support.  The format must be
22512      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22513      */
22514     format : "M Y",
22515     /**
22516      * @cfg {String} altFormats
22517      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22518      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22519      */
22520     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22521     /**
22522      * @cfg {Array} disabledDays
22523      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22524      */
22525     disabledDays : [0,1,2,3,4,5,6],
22526     /**
22527      * @cfg {String} disabledDaysText
22528      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22529      */
22530     disabledDaysText : "Disabled",
22531     /**
22532      * @cfg {Array} disabledDates
22533      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22534      * expression so they are very powerful. Some examples:
22535      * <ul>
22536      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22537      * <li>["03/08", "09/16"] would disable those days for every year</li>
22538      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22539      * <li>["03/../2006"] would disable every day in March 2006</li>
22540      * <li>["^03"] would disable every day in every March</li>
22541      * </ul>
22542      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22543      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22544      */
22545     disabledDates : null,
22546     /**
22547      * @cfg {String} disabledDatesText
22548      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22549      */
22550     disabledDatesText : "Disabled",
22551     /**
22552      * @cfg {Date/String} minValue
22553      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22554      * valid format (defaults to null).
22555      */
22556     minValue : null,
22557     /**
22558      * @cfg {Date/String} maxValue
22559      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22560      * valid format (defaults to null).
22561      */
22562     maxValue : null,
22563     /**
22564      * @cfg {String} minText
22565      * The error text to display when the date in the cell is before minValue (defaults to
22566      * 'The date in this field must be after {minValue}').
22567      */
22568     minText : "The date in this field must be equal to or after {0}",
22569     /**
22570      * @cfg {String} maxTextf
22571      * The error text to display when the date in the cell is after maxValue (defaults to
22572      * 'The date in this field must be before {maxValue}').
22573      */
22574     maxText : "The date in this field must be equal to or before {0}",
22575     /**
22576      * @cfg {String} invalidText
22577      * The error text to display when the date in the field is invalid (defaults to
22578      * '{value} is not a valid date - it must be in the format {format}').
22579      */
22580     invalidText : "{0} is not a valid date - it must be in the format {1}",
22581     /**
22582      * @cfg {String} triggerClass
22583      * An additional CSS class used to style the trigger button.  The trigger will always get the
22584      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22585      * which displays a calendar icon).
22586      */
22587     triggerClass : 'x-form-date-trigger',
22588     
22589
22590     /**
22591      * @cfg {Boolean} useIso
22592      * if enabled, then the date field will use a hidden field to store the 
22593      * real value as iso formated date. default (true)
22594      */ 
22595     useIso : true,
22596     /**
22597      * @cfg {String/Object} autoCreate
22598      * A DomHelper element spec, or true for a default element spec (defaults to
22599      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22600      */ 
22601     // private
22602     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
22603     
22604     // private
22605     hiddenField: false,
22606     
22607     hideMonthPicker : false,
22608     
22609     onRender : function(ct, position)
22610     {
22611         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22612         if (this.useIso) {
22613             this.el.dom.removeAttribute('name'); 
22614             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22615                     'before', true);
22616             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22617             // prevent input submission
22618             this.hiddenName = this.name;
22619         }
22620             
22621             
22622     },
22623     
22624     // private
22625     validateValue : function(value)
22626     {
22627         value = this.formatDate(value);
22628         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22629             return false;
22630         }
22631         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22632              return true;
22633         }
22634         var svalue = value;
22635         value = this.parseDate(value);
22636         if(!value){
22637             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22638             return false;
22639         }
22640         var time = value.getTime();
22641         if(this.minValue && time < this.minValue.getTime()){
22642             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22643             return false;
22644         }
22645         if(this.maxValue && time > this.maxValue.getTime()){
22646             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22647             return false;
22648         }
22649         /*if(this.disabledDays){
22650             var day = value.getDay();
22651             for(var i = 0; i < this.disabledDays.length; i++) {
22652                 if(day === this.disabledDays[i]){
22653                     this.markInvalid(this.disabledDaysText);
22654                     return false;
22655                 }
22656             }
22657         }
22658         */
22659         var fvalue = this.formatDate(value);
22660         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22661             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22662             return false;
22663         }
22664         */
22665         return true;
22666     },
22667
22668     // private
22669     // Provides logic to override the default TriggerField.validateBlur which just returns true
22670     validateBlur : function(){
22671         return !this.menu || !this.menu.isVisible();
22672     },
22673
22674     /**
22675      * Returns the current date value of the date field.
22676      * @return {Date} The date value
22677      */
22678     getValue : function(){
22679         
22680         
22681         
22682         return  this.hiddenField ?
22683                 this.hiddenField.value :
22684                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22685     },
22686
22687     /**
22688      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22689      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22690      * (the default format used is "m/d/y").
22691      * <br />Usage:
22692      * <pre><code>
22693 //All of these calls set the same date value (May 4, 2006)
22694
22695 //Pass a date object:
22696 var dt = new Date('5/4/06');
22697 monthField.setValue(dt);
22698
22699 //Pass a date string (default format):
22700 monthField.setValue('5/4/06');
22701
22702 //Pass a date string (custom format):
22703 monthField.format = 'Y-m-d';
22704 monthField.setValue('2006-5-4');
22705 </code></pre>
22706      * @param {String/Date} date The date or valid date string
22707      */
22708     setValue : function(date){
22709         Roo.log('month setValue' + date);
22710         // can only be first of month..
22711         
22712         var val = this.parseDate(date);
22713         
22714         if (this.hiddenField) {
22715             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22716         }
22717         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22718         this.value = this.parseDate(date);
22719     },
22720
22721     // private
22722     parseDate : function(value){
22723         if(!value || value instanceof Date){
22724             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22725             return value;
22726         }
22727         var v = Date.parseDate(value, this.format);
22728         if (!v && this.useIso) {
22729             v = Date.parseDate(value, 'Y-m-d');
22730         }
22731         if (v) {
22732             // 
22733             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22734         }
22735         
22736         
22737         if(!v && this.altFormats){
22738             if(!this.altFormatsArray){
22739                 this.altFormatsArray = this.altFormats.split("|");
22740             }
22741             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22742                 v = Date.parseDate(value, this.altFormatsArray[i]);
22743             }
22744         }
22745         return v;
22746     },
22747
22748     // private
22749     formatDate : function(date, fmt){
22750         return (!date || !(date instanceof Date)) ?
22751                date : date.dateFormat(fmt || this.format);
22752     },
22753
22754     // private
22755     menuListeners : {
22756         select: function(m, d){
22757             this.setValue(d);
22758             this.fireEvent('select', this, d);
22759         },
22760         show : function(){ // retain focus styling
22761             this.onFocus();
22762         },
22763         hide : function(){
22764             this.focus.defer(10, this);
22765             var ml = this.menuListeners;
22766             this.menu.un("select", ml.select,  this);
22767             this.menu.un("show", ml.show,  this);
22768             this.menu.un("hide", ml.hide,  this);
22769         }
22770     },
22771     // private
22772     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22773     onTriggerClick : function(){
22774         if(this.disabled){
22775             return;
22776         }
22777         if(this.menu == null){
22778             this.menu = new Roo.menu.DateMenu();
22779            
22780         }
22781         
22782         Roo.apply(this.menu.picker,  {
22783             
22784             showClear: this.allowBlank,
22785             minDate : this.minValue,
22786             maxDate : this.maxValue,
22787             disabledDatesRE : this.ddMatch,
22788             disabledDatesText : this.disabledDatesText,
22789             
22790             format : this.useIso ? 'Y-m-d' : this.format,
22791             minText : String.format(this.minText, this.formatDate(this.minValue)),
22792             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22793             
22794         });
22795          this.menu.on(Roo.apply({}, this.menuListeners, {
22796             scope:this
22797         }));
22798        
22799         
22800         var m = this.menu;
22801         var p = m.picker;
22802         
22803         // hide month picker get's called when we called by 'before hide';
22804         
22805         var ignorehide = true;
22806         p.hideMonthPicker  = function(disableAnim){
22807             if (ignorehide) {
22808                 return;
22809             }
22810              if(this.monthPicker){
22811                 Roo.log("hideMonthPicker called");
22812                 if(disableAnim === true){
22813                     this.monthPicker.hide();
22814                 }else{
22815                     this.monthPicker.slideOut('t', {duration:.2});
22816                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22817                     p.fireEvent("select", this, this.value);
22818                     m.hide();
22819                 }
22820             }
22821         }
22822         
22823         Roo.log('picker set value');
22824         Roo.log(this.getValue());
22825         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22826         m.show(this.el, 'tl-bl?');
22827         ignorehide  = false;
22828         // this will trigger hideMonthPicker..
22829         
22830         
22831         // hidden the day picker
22832         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22833         
22834         
22835         
22836       
22837         
22838         p.showMonthPicker.defer(100, p);
22839     
22840         
22841        
22842     },
22843
22844     beforeBlur : function(){
22845         var v = this.parseDate(this.getRawValue());
22846         if(v){
22847             this.setValue(v);
22848         }
22849     }
22850
22851     /** @cfg {Boolean} grow @hide */
22852     /** @cfg {Number} growMin @hide */
22853     /** @cfg {Number} growMax @hide */
22854     /**
22855      * @hide
22856      * @method autoSize
22857      */
22858 });/*
22859  * Based on:
22860  * Ext JS Library 1.1.1
22861  * Copyright(c) 2006-2007, Ext JS, LLC.
22862  *
22863  * Originally Released Under LGPL - original licence link has changed is not relivant.
22864  *
22865  * Fork - LGPL
22866  * <script type="text/javascript">
22867  */
22868  
22869
22870 /**
22871  * @class Roo.form.ComboBox
22872  * @extends Roo.form.TriggerField
22873  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22874  * @constructor
22875  * Create a new ComboBox.
22876  * @param {Object} config Configuration options
22877  */
22878 Roo.form.ComboBox = function(config){
22879     Roo.form.ComboBox.superclass.constructor.call(this, config);
22880     this.addEvents({
22881         /**
22882          * @event expand
22883          * Fires when the dropdown list is expanded
22884              * @param {Roo.form.ComboBox} combo This combo box
22885              */
22886         'expand' : true,
22887         /**
22888          * @event collapse
22889          * Fires when the dropdown list is collapsed
22890              * @param {Roo.form.ComboBox} combo This combo box
22891              */
22892         'collapse' : true,
22893         /**
22894          * @event beforeselect
22895          * Fires before a list item is selected. Return false to cancel the selection.
22896              * @param {Roo.form.ComboBox} combo This combo box
22897              * @param {Roo.data.Record} record The data record returned from the underlying store
22898              * @param {Number} index The index of the selected item in the dropdown list
22899              */
22900         'beforeselect' : true,
22901         /**
22902          * @event select
22903          * Fires when a list item is selected
22904              * @param {Roo.form.ComboBox} combo This combo box
22905              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22906              * @param {Number} index The index of the selected item in the dropdown list
22907              */
22908         'select' : true,
22909         /**
22910          * @event beforequery
22911          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22912          * The event object passed has these properties:
22913              * @param {Roo.form.ComboBox} combo This combo box
22914              * @param {String} query The query
22915              * @param {Boolean} forceAll true to force "all" query
22916              * @param {Boolean} cancel true to cancel the query
22917              * @param {Object} e The query event object
22918              */
22919         'beforequery': true,
22920          /**
22921          * @event add
22922          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22923              * @param {Roo.form.ComboBox} combo This combo box
22924              */
22925         'add' : true,
22926         /**
22927          * @event edit
22928          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22929              * @param {Roo.form.ComboBox} combo This combo box
22930              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22931              */
22932         'edit' : true
22933         
22934         
22935     });
22936     if(this.transform){
22937         this.allowDomMove = false;
22938         var s = Roo.getDom(this.transform);
22939         if(!this.hiddenName){
22940             this.hiddenName = s.name;
22941         }
22942         if(!this.store){
22943             this.mode = 'local';
22944             var d = [], opts = s.options;
22945             for(var i = 0, len = opts.length;i < len; i++){
22946                 var o = opts[i];
22947                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22948                 if(o.selected) {
22949                     this.value = value;
22950                 }
22951                 d.push([value, o.text]);
22952             }
22953             this.store = new Roo.data.SimpleStore({
22954                 'id': 0,
22955                 fields: ['value', 'text'],
22956                 data : d
22957             });
22958             this.valueField = 'value';
22959             this.displayField = 'text';
22960         }
22961         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22962         if(!this.lazyRender){
22963             this.target = true;
22964             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22965             s.parentNode.removeChild(s); // remove it
22966             this.render(this.el.parentNode);
22967         }else{
22968             s.parentNode.removeChild(s); // remove it
22969         }
22970
22971     }
22972     if (this.store) {
22973         this.store = Roo.factory(this.store, Roo.data);
22974     }
22975     
22976     this.selectedIndex = -1;
22977     if(this.mode == 'local'){
22978         if(config.queryDelay === undefined){
22979             this.queryDelay = 10;
22980         }
22981         if(config.minChars === undefined){
22982             this.minChars = 0;
22983         }
22984     }
22985 };
22986
22987 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22988     /**
22989      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22990      */
22991     /**
22992      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22993      * rendering into an Roo.Editor, defaults to false)
22994      */
22995     /**
22996      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22997      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22998      */
22999     /**
23000      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23001      */
23002     /**
23003      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23004      * the dropdown list (defaults to undefined, with no header element)
23005      */
23006
23007      /**
23008      * @cfg {String/Roo.Template} tpl The template to use to render the output
23009      */
23010      
23011     // private
23012     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23013     /**
23014      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23015      */
23016     listWidth: undefined,
23017     /**
23018      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23019      * mode = 'remote' or 'text' if mode = 'local')
23020      */
23021     displayField: undefined,
23022     /**
23023      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23024      * mode = 'remote' or 'value' if mode = 'local'). 
23025      * Note: use of a valueField requires the user make a selection
23026      * in order for a value to be mapped.
23027      */
23028     valueField: undefined,
23029     
23030     
23031     /**
23032      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23033      * field's data value (defaults to the underlying DOM element's name)
23034      */
23035     hiddenName: undefined,
23036     /**
23037      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23038      */
23039     listClass: '',
23040     /**
23041      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23042      */
23043     selectedClass: 'x-combo-selected',
23044     /**
23045      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23046      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23047      * which displays a downward arrow icon).
23048      */
23049     triggerClass : 'x-form-arrow-trigger',
23050     /**
23051      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23052      */
23053     shadow:'sides',
23054     /**
23055      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23056      * anchor positions (defaults to 'tl-bl')
23057      */
23058     listAlign: 'tl-bl?',
23059     /**
23060      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23061      */
23062     maxHeight: 300,
23063     /**
23064      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23065      * query specified by the allQuery config option (defaults to 'query')
23066      */
23067     triggerAction: 'query',
23068     /**
23069      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23070      * (defaults to 4, does not apply if editable = false)
23071      */
23072     minChars : 4,
23073     /**
23074      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23075      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23076      */
23077     typeAhead: false,
23078     /**
23079      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23080      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23081      */
23082     queryDelay: 500,
23083     /**
23084      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23085      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23086      */
23087     pageSize: 0,
23088     /**
23089      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23090      * when editable = true (defaults to false)
23091      */
23092     selectOnFocus:false,
23093     /**
23094      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23095      */
23096     queryParam: 'query',
23097     /**
23098      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23099      * when mode = 'remote' (defaults to 'Loading...')
23100      */
23101     loadingText: 'Loading...',
23102     /**
23103      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23104      */
23105     resizable: false,
23106     /**
23107      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23108      */
23109     handleHeight : 8,
23110     /**
23111      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23112      * traditional select (defaults to true)
23113      */
23114     editable: true,
23115     /**
23116      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23117      */
23118     allQuery: '',
23119     /**
23120      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23121      */
23122     mode: 'remote',
23123     /**
23124      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23125      * listWidth has a higher value)
23126      */
23127     minListWidth : 70,
23128     /**
23129      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23130      * allow the user to set arbitrary text into the field (defaults to false)
23131      */
23132     forceSelection:false,
23133     /**
23134      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23135      * if typeAhead = true (defaults to 250)
23136      */
23137     typeAheadDelay : 250,
23138     /**
23139      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23140      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23141      */
23142     valueNotFoundText : undefined,
23143     /**
23144      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23145      */
23146     blockFocus : false,
23147     
23148     /**
23149      * @cfg {Boolean} disableClear Disable showing of clear button.
23150      */
23151     disableClear : false,
23152     /**
23153      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23154      */
23155     alwaysQuery : false,
23156     
23157     //private
23158     addicon : false,
23159     editicon: false,
23160     
23161     // element that contains real text value.. (when hidden is used..)
23162      
23163     // private
23164     onRender : function(ct, position){
23165         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23166         if(this.hiddenName){
23167             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23168                     'before', true);
23169             this.hiddenField.value =
23170                 this.hiddenValue !== undefined ? this.hiddenValue :
23171                 this.value !== undefined ? this.value : '';
23172
23173             // prevent input submission
23174             this.el.dom.removeAttribute('name');
23175              
23176              
23177         }
23178         if(Roo.isGecko){
23179             this.el.dom.setAttribute('autocomplete', 'off');
23180         }
23181
23182         var cls = 'x-combo-list';
23183
23184         this.list = new Roo.Layer({
23185             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23186         });
23187
23188         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23189         this.list.setWidth(lw);
23190         this.list.swallowEvent('mousewheel');
23191         this.assetHeight = 0;
23192
23193         if(this.title){
23194             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23195             this.assetHeight += this.header.getHeight();
23196         }
23197
23198         this.innerList = this.list.createChild({cls:cls+'-inner'});
23199         this.innerList.on('mouseover', this.onViewOver, this);
23200         this.innerList.on('mousemove', this.onViewMove, this);
23201         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23202         
23203         if(this.allowBlank && !this.pageSize && !this.disableClear){
23204             this.footer = this.list.createChild({cls:cls+'-ft'});
23205             this.pageTb = new Roo.Toolbar(this.footer);
23206            
23207         }
23208         if(this.pageSize){
23209             this.footer = this.list.createChild({cls:cls+'-ft'});
23210             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23211                     {pageSize: this.pageSize});
23212             
23213         }
23214         
23215         if (this.pageTb && this.allowBlank && !this.disableClear) {
23216             var _this = this;
23217             this.pageTb.add(new Roo.Toolbar.Fill(), {
23218                 cls: 'x-btn-icon x-btn-clear',
23219                 text: '&#160;',
23220                 handler: function()
23221                 {
23222                     _this.collapse();
23223                     _this.clearValue();
23224                     _this.onSelect(false, -1);
23225                 }
23226             });
23227         }
23228         if (this.footer) {
23229             this.assetHeight += this.footer.getHeight();
23230         }
23231         
23232
23233         if(!this.tpl){
23234             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23235         }
23236
23237         this.view = new Roo.View(this.innerList, this.tpl, {
23238             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23239         });
23240
23241         this.view.on('click', this.onViewClick, this);
23242
23243         this.store.on('beforeload', this.onBeforeLoad, this);
23244         this.store.on('load', this.onLoad, this);
23245         this.store.on('loadexception', this.onLoadException, this);
23246
23247         if(this.resizable){
23248             this.resizer = new Roo.Resizable(this.list,  {
23249                pinned:true, handles:'se'
23250             });
23251             this.resizer.on('resize', function(r, w, h){
23252                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23253                 this.listWidth = w;
23254                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23255                 this.restrictHeight();
23256             }, this);
23257             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23258         }
23259         if(!this.editable){
23260             this.editable = true;
23261             this.setEditable(false);
23262         }  
23263         
23264         
23265         if (typeof(this.events.add.listeners) != 'undefined') {
23266             
23267             this.addicon = this.wrap.createChild(
23268                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23269        
23270             this.addicon.on('click', function(e) {
23271                 this.fireEvent('add', this);
23272             }, this);
23273         }
23274         if (typeof(this.events.edit.listeners) != 'undefined') {
23275             
23276             this.editicon = this.wrap.createChild(
23277                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23278             if (this.addicon) {
23279                 this.editicon.setStyle('margin-left', '40px');
23280             }
23281             this.editicon.on('click', function(e) {
23282                 
23283                 // we fire even  if inothing is selected..
23284                 this.fireEvent('edit', this, this.lastData );
23285                 
23286             }, this);
23287         }
23288         
23289         
23290         
23291     },
23292
23293     // private
23294     initEvents : function(){
23295         Roo.form.ComboBox.superclass.initEvents.call(this);
23296
23297         this.keyNav = new Roo.KeyNav(this.el, {
23298             "up" : function(e){
23299                 this.inKeyMode = true;
23300                 this.selectPrev();
23301             },
23302
23303             "down" : function(e){
23304                 if(!this.isExpanded()){
23305                     this.onTriggerClick();
23306                 }else{
23307                     this.inKeyMode = true;
23308                     this.selectNext();
23309                 }
23310             },
23311
23312             "enter" : function(e){
23313                 this.onViewClick();
23314                 //return true;
23315             },
23316
23317             "esc" : function(e){
23318                 this.collapse();
23319             },
23320
23321             "tab" : function(e){
23322                 this.onViewClick(false);
23323                 this.fireEvent("specialkey", this, e);
23324                 return true;
23325             },
23326
23327             scope : this,
23328
23329             doRelay : function(foo, bar, hname){
23330                 if(hname == 'down' || this.scope.isExpanded()){
23331                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23332                 }
23333                 return true;
23334             },
23335
23336             forceKeyDown: true
23337         });
23338         this.queryDelay = Math.max(this.queryDelay || 10,
23339                 this.mode == 'local' ? 10 : 250);
23340         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23341         if(this.typeAhead){
23342             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23343         }
23344         if(this.editable !== false){
23345             this.el.on("keyup", this.onKeyUp, this);
23346         }
23347         if(this.forceSelection){
23348             this.on('blur', this.doForce, this);
23349         }
23350     },
23351
23352     onDestroy : function(){
23353         if(this.view){
23354             this.view.setStore(null);
23355             this.view.el.removeAllListeners();
23356             this.view.el.remove();
23357             this.view.purgeListeners();
23358         }
23359         if(this.list){
23360             this.list.destroy();
23361         }
23362         if(this.store){
23363             this.store.un('beforeload', this.onBeforeLoad, this);
23364             this.store.un('load', this.onLoad, this);
23365             this.store.un('loadexception', this.onLoadException, this);
23366         }
23367         Roo.form.ComboBox.superclass.onDestroy.call(this);
23368     },
23369
23370     // private
23371     fireKey : function(e){
23372         if(e.isNavKeyPress() && !this.list.isVisible()){
23373             this.fireEvent("specialkey", this, e);
23374         }
23375     },
23376
23377     // private
23378     onResize: function(w, h){
23379         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23380         
23381         if(typeof w != 'number'){
23382             // we do not handle it!?!?
23383             return;
23384         }
23385         var tw = this.trigger.getWidth();
23386         tw += this.addicon ? this.addicon.getWidth() : 0;
23387         tw += this.editicon ? this.editicon.getWidth() : 0;
23388         var x = w - tw;
23389         this.el.setWidth( this.adjustWidth('input', x));
23390             
23391         this.trigger.setStyle('left', x+'px');
23392         
23393         if(this.list && this.listWidth === undefined){
23394             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23395             this.list.setWidth(lw);
23396             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23397         }
23398         
23399     
23400         
23401     },
23402
23403     /**
23404      * Allow or prevent the user from directly editing the field text.  If false is passed,
23405      * the user will only be able to select from the items defined in the dropdown list.  This method
23406      * is the runtime equivalent of setting the 'editable' config option at config time.
23407      * @param {Boolean} value True to allow the user to directly edit the field text
23408      */
23409     setEditable : function(value){
23410         if(value == this.editable){
23411             return;
23412         }
23413         this.editable = value;
23414         if(!value){
23415             this.el.dom.setAttribute('readOnly', true);
23416             this.el.on('mousedown', this.onTriggerClick,  this);
23417             this.el.addClass('x-combo-noedit');
23418         }else{
23419             this.el.dom.setAttribute('readOnly', false);
23420             this.el.un('mousedown', this.onTriggerClick,  this);
23421             this.el.removeClass('x-combo-noedit');
23422         }
23423     },
23424
23425     // private
23426     onBeforeLoad : function(){
23427         if(!this.hasFocus){
23428             return;
23429         }
23430         this.innerList.update(this.loadingText ?
23431                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23432         this.restrictHeight();
23433         this.selectedIndex = -1;
23434     },
23435
23436     // private
23437     onLoad : function(){
23438         if(!this.hasFocus){
23439             return;
23440         }
23441         if(this.store.getCount() > 0){
23442             this.expand();
23443             this.restrictHeight();
23444             if(this.lastQuery == this.allQuery){
23445                 if(this.editable){
23446                     this.el.dom.select();
23447                 }
23448                 if(!this.selectByValue(this.value, true)){
23449                     this.select(0, true);
23450                 }
23451             }else{
23452                 this.selectNext();
23453                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23454                     this.taTask.delay(this.typeAheadDelay);
23455                 }
23456             }
23457         }else{
23458             this.onEmptyResults();
23459         }
23460         //this.el.focus();
23461     },
23462     // private
23463     onLoadException : function()
23464     {
23465         this.collapse();
23466         Roo.log(this.store.reader.jsonData);
23467         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23468             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23469         }
23470         
23471         
23472     },
23473     // private
23474     onTypeAhead : function(){
23475         if(this.store.getCount() > 0){
23476             var r = this.store.getAt(0);
23477             var newValue = r.data[this.displayField];
23478             var len = newValue.length;
23479             var selStart = this.getRawValue().length;
23480             if(selStart != len){
23481                 this.setRawValue(newValue);
23482                 this.selectText(selStart, newValue.length);
23483             }
23484         }
23485     },
23486
23487     // private
23488     onSelect : function(record, index){
23489         if(this.fireEvent('beforeselect', this, record, index) !== false){
23490             this.setFromData(index > -1 ? record.data : false);
23491             this.collapse();
23492             this.fireEvent('select', this, record, index);
23493         }
23494     },
23495
23496     /**
23497      * Returns the currently selected field value or empty string if no value is set.
23498      * @return {String} value The selected value
23499      */
23500     getValue : function(){
23501         if(this.valueField){
23502             return typeof this.value != 'undefined' ? this.value : '';
23503         }
23504         return Roo.form.ComboBox.superclass.getValue.call(this);
23505     },
23506
23507     /**
23508      * Clears any text/value currently set in the field
23509      */
23510     clearValue : function(){
23511         if(this.hiddenField){
23512             this.hiddenField.value = '';
23513         }
23514         this.value = '';
23515         this.setRawValue('');
23516         this.lastSelectionText = '';
23517         
23518     },
23519
23520     /**
23521      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23522      * will be displayed in the field.  If the value does not match the data value of an existing item,
23523      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23524      * Otherwise the field will be blank (although the value will still be set).
23525      * @param {String} value The value to match
23526      */
23527     setValue : function(v){
23528         var text = v;
23529         if(this.valueField){
23530             var r = this.findRecord(this.valueField, v);
23531             if(r){
23532                 text = r.data[this.displayField];
23533             }else if(this.valueNotFoundText !== undefined){
23534                 text = this.valueNotFoundText;
23535             }
23536         }
23537         this.lastSelectionText = text;
23538         if(this.hiddenField){
23539             this.hiddenField.value = v;
23540         }
23541         Roo.form.ComboBox.superclass.setValue.call(this, text);
23542         this.value = v;
23543     },
23544     /**
23545      * @property {Object} the last set data for the element
23546      */
23547     
23548     lastData : false,
23549     /**
23550      * Sets the value of the field based on a object which is related to the record format for the store.
23551      * @param {Object} value the value to set as. or false on reset?
23552      */
23553     setFromData : function(o){
23554         var dv = ''; // display value
23555         var vv = ''; // value value..
23556         this.lastData = o;
23557         if (this.displayField) {
23558             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23559         } else {
23560             // this is an error condition!!!
23561             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23562         }
23563         
23564         if(this.valueField){
23565             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23566         }
23567         if(this.hiddenField){
23568             this.hiddenField.value = vv;
23569             
23570             this.lastSelectionText = dv;
23571             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23572             this.value = vv;
23573             return;
23574         }
23575         // no hidden field.. - we store the value in 'value', but still display
23576         // display field!!!!
23577         this.lastSelectionText = dv;
23578         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23579         this.value = vv;
23580         
23581         
23582     },
23583     // private
23584     reset : function(){
23585         // overridden so that last data is reset..
23586         this.setValue(this.resetValue);
23587         this.clearInvalid();
23588         this.lastData = false;
23589         if (this.view) {
23590             this.view.clearSelections();
23591         }
23592     },
23593     // private
23594     findRecord : function(prop, value){
23595         var record;
23596         if(this.store.getCount() > 0){
23597             this.store.each(function(r){
23598                 if(r.data[prop] == value){
23599                     record = r;
23600                     return false;
23601                 }
23602                 return true;
23603             });
23604         }
23605         return record;
23606     },
23607     
23608     getName: function()
23609     {
23610         // returns hidden if it's set..
23611         if (!this.rendered) {return ''};
23612         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23613         
23614     },
23615     // private
23616     onViewMove : function(e, t){
23617         this.inKeyMode = false;
23618     },
23619
23620     // private
23621     onViewOver : function(e, t){
23622         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23623             return;
23624         }
23625         var item = this.view.findItemFromChild(t);
23626         if(item){
23627             var index = this.view.indexOf(item);
23628             this.select(index, false);
23629         }
23630     },
23631
23632     // private
23633     onViewClick : function(doFocus)
23634     {
23635         var index = this.view.getSelectedIndexes()[0];
23636         var r = this.store.getAt(index);
23637         if(r){
23638             this.onSelect(r, index);
23639         }
23640         if(doFocus !== false && !this.blockFocus){
23641             this.el.focus();
23642         }
23643     },
23644
23645     // private
23646     restrictHeight : function(){
23647         this.innerList.dom.style.height = '';
23648         var inner = this.innerList.dom;
23649         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23650         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23651         this.list.beginUpdate();
23652         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23653         this.list.alignTo(this.el, this.listAlign);
23654         this.list.endUpdate();
23655     },
23656
23657     // private
23658     onEmptyResults : function(){
23659         this.collapse();
23660     },
23661
23662     /**
23663      * Returns true if the dropdown list is expanded, else false.
23664      */
23665     isExpanded : function(){
23666         return this.list.isVisible();
23667     },
23668
23669     /**
23670      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23671      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23672      * @param {String} value The data value of the item to select
23673      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23674      * selected item if it is not currently in view (defaults to true)
23675      * @return {Boolean} True if the value matched an item in the list, else false
23676      */
23677     selectByValue : function(v, scrollIntoView){
23678         if(v !== undefined && v !== null){
23679             var r = this.findRecord(this.valueField || this.displayField, v);
23680             if(r){
23681                 this.select(this.store.indexOf(r), scrollIntoView);
23682                 return true;
23683             }
23684         }
23685         return false;
23686     },
23687
23688     /**
23689      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23690      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23691      * @param {Number} index The zero-based index of the list item to select
23692      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23693      * selected item if it is not currently in view (defaults to true)
23694      */
23695     select : function(index, scrollIntoView){
23696         this.selectedIndex = index;
23697         this.view.select(index);
23698         if(scrollIntoView !== false){
23699             var el = this.view.getNode(index);
23700             if(el){
23701                 this.innerList.scrollChildIntoView(el, false);
23702             }
23703         }
23704     },
23705
23706     // private
23707     selectNext : function(){
23708         var ct = this.store.getCount();
23709         if(ct > 0){
23710             if(this.selectedIndex == -1){
23711                 this.select(0);
23712             }else if(this.selectedIndex < ct-1){
23713                 this.select(this.selectedIndex+1);
23714             }
23715         }
23716     },
23717
23718     // private
23719     selectPrev : function(){
23720         var ct = this.store.getCount();
23721         if(ct > 0){
23722             if(this.selectedIndex == -1){
23723                 this.select(0);
23724             }else if(this.selectedIndex != 0){
23725                 this.select(this.selectedIndex-1);
23726             }
23727         }
23728     },
23729
23730     // private
23731     onKeyUp : function(e){
23732         if(this.editable !== false && !e.isSpecialKey()){
23733             this.lastKey = e.getKey();
23734             this.dqTask.delay(this.queryDelay);
23735         }
23736     },
23737
23738     // private
23739     validateBlur : function(){
23740         return !this.list || !this.list.isVisible();   
23741     },
23742
23743     // private
23744     initQuery : function(){
23745         this.doQuery(this.getRawValue());
23746     },
23747
23748     // private
23749     doForce : function(){
23750         if(this.el.dom.value.length > 0){
23751             this.el.dom.value =
23752                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23753              
23754         }
23755     },
23756
23757     /**
23758      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23759      * query allowing the query action to be canceled if needed.
23760      * @param {String} query The SQL query to execute
23761      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23762      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23763      * saved in the current store (defaults to false)
23764      */
23765     doQuery : function(q, forceAll){
23766         if(q === undefined || q === null){
23767             q = '';
23768         }
23769         var qe = {
23770             query: q,
23771             forceAll: forceAll,
23772             combo: this,
23773             cancel:false
23774         };
23775         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23776             return false;
23777         }
23778         q = qe.query;
23779         forceAll = qe.forceAll;
23780         if(forceAll === true || (q.length >= this.minChars)){
23781             if(this.lastQuery != q || this.alwaysQuery){
23782                 this.lastQuery = q;
23783                 if(this.mode == 'local'){
23784                     this.selectedIndex = -1;
23785                     if(forceAll){
23786                         this.store.clearFilter();
23787                     }else{
23788                         this.store.filter(this.displayField, q);
23789                     }
23790                     this.onLoad();
23791                 }else{
23792                     this.store.baseParams[this.queryParam] = q;
23793                     this.store.load({
23794                         params: this.getParams(q)
23795                     });
23796                     this.expand();
23797                 }
23798             }else{
23799                 this.selectedIndex = -1;
23800                 this.onLoad();   
23801             }
23802         }
23803     },
23804
23805     // private
23806     getParams : function(q){
23807         var p = {};
23808         //p[this.queryParam] = q;
23809         if(this.pageSize){
23810             p.start = 0;
23811             p.limit = this.pageSize;
23812         }
23813         return p;
23814     },
23815
23816     /**
23817      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23818      */
23819     collapse : function(){
23820         if(!this.isExpanded()){
23821             return;
23822         }
23823         this.list.hide();
23824         Roo.get(document).un('mousedown', this.collapseIf, this);
23825         Roo.get(document).un('mousewheel', this.collapseIf, this);
23826         if (!this.editable) {
23827             Roo.get(document).un('keydown', this.listKeyPress, this);
23828         }
23829         this.fireEvent('collapse', this);
23830     },
23831
23832     // private
23833     collapseIf : function(e){
23834         if(!e.within(this.wrap) && !e.within(this.list)){
23835             this.collapse();
23836         }
23837     },
23838
23839     /**
23840      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23841      */
23842     expand : function(){
23843         if(this.isExpanded() || !this.hasFocus){
23844             return;
23845         }
23846         this.list.alignTo(this.el, this.listAlign);
23847         this.list.show();
23848         Roo.get(document).on('mousedown', this.collapseIf, this);
23849         Roo.get(document).on('mousewheel', this.collapseIf, this);
23850         if (!this.editable) {
23851             Roo.get(document).on('keydown', this.listKeyPress, this);
23852         }
23853         
23854         this.fireEvent('expand', this);
23855     },
23856
23857     // private
23858     // Implements the default empty TriggerField.onTriggerClick function
23859     onTriggerClick : function(){
23860         if(this.disabled){
23861             return;
23862         }
23863         if(this.isExpanded()){
23864             this.collapse();
23865             if (!this.blockFocus) {
23866                 this.el.focus();
23867             }
23868             
23869         }else {
23870             this.hasFocus = true;
23871             if(this.triggerAction == 'all') {
23872                 this.doQuery(this.allQuery, true);
23873             } else {
23874                 this.doQuery(this.getRawValue());
23875             }
23876             if (!this.blockFocus) {
23877                 this.el.focus();
23878             }
23879         }
23880     },
23881     listKeyPress : function(e)
23882     {
23883         //Roo.log('listkeypress');
23884         // scroll to first matching element based on key pres..
23885         if (e.isSpecialKey()) {
23886             return false;
23887         }
23888         var k = String.fromCharCode(e.getKey()).toUpperCase();
23889         //Roo.log(k);
23890         var match  = false;
23891         var csel = this.view.getSelectedNodes();
23892         var cselitem = false;
23893         if (csel.length) {
23894             var ix = this.view.indexOf(csel[0]);
23895             cselitem  = this.store.getAt(ix);
23896             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23897                 cselitem = false;
23898             }
23899             
23900         }
23901         
23902         this.store.each(function(v) { 
23903             if (cselitem) {
23904                 // start at existing selection.
23905                 if (cselitem.id == v.id) {
23906                     cselitem = false;
23907                 }
23908                 return;
23909             }
23910                 
23911             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23912                 match = this.store.indexOf(v);
23913                 return false;
23914             }
23915         }, this);
23916         
23917         if (match === false) {
23918             return true; // no more action?
23919         }
23920         // scroll to?
23921         this.view.select(match);
23922         var sn = Roo.get(this.view.getSelectedNodes()[0])
23923         sn.scrollIntoView(sn.dom.parentNode, false);
23924     }
23925
23926     /** 
23927     * @cfg {Boolean} grow 
23928     * @hide 
23929     */
23930     /** 
23931     * @cfg {Number} growMin 
23932     * @hide 
23933     */
23934     /** 
23935     * @cfg {Number} growMax 
23936     * @hide 
23937     */
23938     /**
23939      * @hide
23940      * @method autoSize
23941      */
23942 });/*
23943  * Copyright(c) 2010-2012, Roo J Solutions Limited
23944  *
23945  * Licence LGPL
23946  *
23947  */
23948
23949 /**
23950  * @class Roo.form.ComboBoxArray
23951  * @extends Roo.form.TextField
23952  * A facebook style adder... for lists of email / people / countries  etc...
23953  * pick multiple items from a combo box, and shows each one.
23954  *
23955  *  Fred [x]  Brian [x]  [Pick another |v]
23956  *
23957  *
23958  *  For this to work: it needs various extra information
23959  *    - normal combo problay has
23960  *      name, hiddenName
23961  *    + displayField, valueField
23962  *
23963  *    For our purpose...
23964  *
23965  *
23966  *   If we change from 'extends' to wrapping...
23967  *   
23968  *  
23969  *
23970  
23971  
23972  * @constructor
23973  * Create a new ComboBoxArray.
23974  * @param {Object} config Configuration options
23975  */
23976  
23977
23978 Roo.form.ComboBoxArray = function(config)
23979 {
23980     this.addEvents({
23981         /**
23982          * @event beforeremove
23983          * Fires before remove the value from the list
23984              * @param {Roo.form.ComboBoxArray} _self This combo box array
23985              * @param {Roo.form.ComboBoxArray.Item} item removed item
23986              */
23987         'beforeremove' : true,
23988         /**
23989          * @event remove
23990          * Fires when remove the value from the list
23991              * @param {Roo.form.ComboBoxArray} _self This combo box array
23992              * @param {Roo.form.ComboBoxArray.Item} item removed item
23993              */
23994         'remove' : true
23995         
23996         
23997     });
23998     
23999     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24000     
24001     this.items = new Roo.util.MixedCollection(false);
24002     
24003     // construct the child combo...
24004     
24005     
24006     
24007     
24008    
24009     
24010 }
24011
24012  
24013 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24014
24015     /**
24016      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24017      */
24018     
24019     lastData : false,
24020     
24021     // behavies liek a hiddne field
24022     inputType:      'hidden',
24023     /**
24024      * @cfg {Number} width The width of the box that displays the selected element
24025      */ 
24026     width:          300,
24027
24028     
24029     
24030     /**
24031      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24032      */
24033     name : false,
24034     /**
24035      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24036      */
24037     hiddenName : false,
24038     
24039     
24040     // private the array of items that are displayed..
24041     items  : false,
24042     // private - the hidden field el.
24043     hiddenEl : false,
24044     // private - the filed el..
24045     el : false,
24046     
24047     //validateValue : function() { return true; }, // all values are ok!
24048     //onAddClick: function() { },
24049     
24050     onRender : function(ct, position) 
24051     {
24052         
24053         // create the standard hidden element
24054         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24055         
24056         
24057         // give fake names to child combo;
24058         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24059         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24060         
24061         this.combo = Roo.factory(this.combo, Roo.form);
24062         this.combo.onRender(ct, position);
24063         if (typeof(this.combo.width) != 'undefined') {
24064             this.combo.onResize(this.combo.width,0);
24065         }
24066         
24067         this.combo.initEvents();
24068         
24069         // assigned so form know we need to do this..
24070         this.store          = this.combo.store;
24071         this.valueField     = this.combo.valueField;
24072         this.displayField   = this.combo.displayField ;
24073         
24074         
24075         this.combo.wrap.addClass('x-cbarray-grp');
24076         
24077         var cbwrap = this.combo.wrap.createChild(
24078             {tag: 'div', cls: 'x-cbarray-cb'},
24079             this.combo.el.dom
24080         );
24081         
24082              
24083         this.hiddenEl = this.combo.wrap.createChild({
24084             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24085         });
24086         this.el = this.combo.wrap.createChild({
24087             tag: 'input',  type:'hidden' , name: this.name, value : ''
24088         });
24089          //   this.el.dom.removeAttribute("name");
24090         
24091         
24092         this.outerWrap = this.combo.wrap;
24093         this.wrap = cbwrap;
24094         
24095         this.outerWrap.setWidth(this.width);
24096         this.outerWrap.dom.removeChild(this.el.dom);
24097         
24098         this.wrap.dom.appendChild(this.el.dom);
24099         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24100         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24101         
24102         this.combo.trigger.setStyle('position','relative');
24103         this.combo.trigger.setStyle('left', '0px');
24104         this.combo.trigger.setStyle('top', '2px');
24105         
24106         this.combo.el.setStyle('vertical-align', 'text-bottom');
24107         
24108         //this.trigger.setStyle('vertical-align', 'top');
24109         
24110         // this should use the code from combo really... on('add' ....)
24111         if (this.adder) {
24112             
24113         
24114             this.adder = this.outerWrap.createChild(
24115                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24116             var _t = this;
24117             this.adder.on('click', function(e) {
24118                 _t.fireEvent('adderclick', this, e);
24119             }, _t);
24120         }
24121         //var _t = this;
24122         //this.adder.on('click', this.onAddClick, _t);
24123         
24124         
24125         this.combo.on('select', function(cb, rec, ix) {
24126             this.addItem(rec.data);
24127             
24128             cb.setValue('');
24129             cb.el.dom.value = '';
24130             //cb.lastData = rec.data;
24131             // add to list
24132             
24133         }, this);
24134         
24135         
24136     },
24137     
24138     
24139     getName: function()
24140     {
24141         // returns hidden if it's set..
24142         if (!this.rendered) {return ''};
24143         return  this.hiddenName ? this.hiddenName : this.name;
24144         
24145     },
24146     
24147     
24148     onResize: function(w, h){
24149         
24150         return;
24151         // not sure if this is needed..
24152         //this.combo.onResize(w,h);
24153         
24154         if(typeof w != 'number'){
24155             // we do not handle it!?!?
24156             return;
24157         }
24158         var tw = this.combo.trigger.getWidth();
24159         tw += this.addicon ? this.addicon.getWidth() : 0;
24160         tw += this.editicon ? this.editicon.getWidth() : 0;
24161         var x = w - tw;
24162         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24163             
24164         this.combo.trigger.setStyle('left', '0px');
24165         
24166         if(this.list && this.listWidth === undefined){
24167             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24168             this.list.setWidth(lw);
24169             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24170         }
24171         
24172     
24173         
24174     },
24175     
24176     addItem: function(rec)
24177     {
24178         var valueField = this.combo.valueField;
24179         var displayField = this.combo.displayField;
24180         if (this.items.indexOfKey(rec[valueField]) > -1) {
24181             //console.log("GOT " + rec.data.id);
24182             return;
24183         }
24184         
24185         var x = new Roo.form.ComboBoxArray.Item({
24186             //id : rec[this.idField],
24187             data : rec,
24188             displayField : displayField ,
24189             tipField : displayField ,
24190             cb : this
24191         });
24192         // use the 
24193         this.items.add(rec[valueField],x);
24194         // add it before the element..
24195         this.updateHiddenEl();
24196         x.render(this.outerWrap, this.wrap.dom);
24197         // add the image handler..
24198     },
24199     
24200     updateHiddenEl : function()
24201     {
24202         this.validate();
24203         if (!this.hiddenEl) {
24204             return;
24205         }
24206         var ar = [];
24207         var idField = this.combo.valueField;
24208         
24209         this.items.each(function(f) {
24210             ar.push(f.data[idField]);
24211            
24212         });
24213         this.hiddenEl.dom.value = ar.join(',');
24214         this.validate();
24215     },
24216     
24217     reset : function()
24218     {
24219         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24220         this.items.each(function(f) {
24221            f.remove(); 
24222         });
24223         this.el.dom.value = '';
24224         if (this.hiddenEl) {
24225             this.hiddenEl.dom.value = '';
24226         }
24227         
24228     },
24229     getValue: function()
24230     {
24231         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24232     },
24233     setValue: function(v) // not a valid action - must use addItems..
24234     {
24235          
24236         this.reset();
24237         
24238         
24239         
24240         if (this.store.isLocal && (typeof(v) == 'string')) {
24241             // then we can use the store to find the values..
24242             // comma seperated at present.. this needs to allow JSON based encoding..
24243             this.hiddenEl.value  = v;
24244             var v_ar = [];
24245             Roo.each(v.split(','), function(k) {
24246                 Roo.log("CHECK " + this.valueField + ',' + k);
24247                 var li = this.store.query(this.valueField, k);
24248                 if (!li.length) {
24249                     return;
24250                 }
24251                 var add = {};
24252                 add[this.valueField] = k;
24253                 add[this.displayField] = li.item(0).data[this.displayField];
24254                 
24255                 this.addItem(add);
24256             }, this) 
24257              
24258         }
24259         if (typeof(v) == 'object' ) {
24260             // then let's assume it's an array of objects..
24261             Roo.each(v, function(l) {
24262                 this.addItem(l);
24263             }, this);
24264              
24265         }
24266         
24267         
24268     },
24269     setFromData: function(v)
24270     {
24271         // this recieves an object, if setValues is called.
24272         this.reset();
24273         this.el.dom.value = v[this.displayField];
24274         this.hiddenEl.dom.value = v[this.valueField];
24275         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24276             return;
24277         }
24278         var kv = v[this.valueField];
24279         var dv = v[this.displayField];
24280         kv = typeof(kv) != 'string' ? '' : kv;
24281         dv = typeof(dv) != 'string' ? '' : dv;
24282         
24283         
24284         var keys = kv.split(',');
24285         var display = dv.split(',');
24286         for (var i = 0 ; i < keys.length; i++) {
24287             
24288             add = {};
24289             add[this.valueField] = keys[i];
24290             add[this.displayField] = display[i];
24291             this.addItem(add);
24292         }
24293       
24294         
24295     },
24296     
24297     /**
24298      * Validates the combox array value
24299      * @return {Boolean} True if the value is valid, else false
24300      */
24301     validate : function(){
24302         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24303             this.clearInvalid();
24304             return true;
24305         }
24306         return false;
24307     },
24308     
24309     validateValue : function(value){
24310         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24311         
24312     },
24313     
24314     /*@
24315      * overide
24316      * 
24317      */
24318     isDirty : function() {
24319         if(this.disabled) {
24320             return false;
24321         }
24322         
24323         try {
24324             var d = Roo.decode(String(this.originalValue));
24325         } catch (e) {
24326             return String(this.getValue()) !== String(this.originalValue);
24327         }
24328         
24329         var originalValue = [];
24330         
24331         for (var i = 0; i < d.length; i++){
24332             originalValue.push(d[i][this.valueField]);
24333         }
24334         
24335         return String(this.getValue()) !== String(originalValue.join(','));
24336         
24337     }
24338     
24339 });
24340
24341
24342
24343 /**
24344  * @class Roo.form.ComboBoxArray.Item
24345  * @extends Roo.BoxComponent
24346  * A selected item in the list
24347  *  Fred [x]  Brian [x]  [Pick another |v]
24348  * 
24349  * @constructor
24350  * Create a new item.
24351  * @param {Object} config Configuration options
24352  */
24353  
24354 Roo.form.ComboBoxArray.Item = function(config) {
24355     config.id = Roo.id();
24356     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24357 }
24358
24359 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24360     data : {},
24361     cb: false,
24362     displayField : false,
24363     tipField : false,
24364     
24365     
24366     defaultAutoCreate : {
24367         tag: 'div',
24368         cls: 'x-cbarray-item',
24369         cn : [ 
24370             { tag: 'div' },
24371             {
24372                 tag: 'img',
24373                 width:16,
24374                 height : 16,
24375                 src : Roo.BLANK_IMAGE_URL ,
24376                 align: 'center'
24377             }
24378         ]
24379         
24380     },
24381     
24382  
24383     onRender : function(ct, position)
24384     {
24385         Roo.form.Field.superclass.onRender.call(this, ct, position);
24386         
24387         if(!this.el){
24388             var cfg = this.getAutoCreate();
24389             this.el = ct.createChild(cfg, position);
24390         }
24391         
24392         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24393         
24394         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24395             this.cb.renderer(this.data) :
24396             String.format('{0}',this.data[this.displayField]);
24397         
24398             
24399         this.el.child('div').dom.setAttribute('qtip',
24400                         String.format('{0}',this.data[this.tipField])
24401         );
24402         
24403         this.el.child('img').on('click', this.remove, this);
24404         
24405     },
24406    
24407     remove : function()
24408     {
24409         if(this.cb.disabled){
24410             return;
24411         }
24412         
24413         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24414             this.cb.items.remove(this);
24415             this.el.child('img').un('click', this.remove, this);
24416             this.el.remove();
24417             this.cb.updateHiddenEl();
24418
24419             this.cb.fireEvent('remove', this.cb, this);
24420         }
24421         
24422     }
24423 });/*
24424  * Based on:
24425  * Ext JS Library 1.1.1
24426  * Copyright(c) 2006-2007, Ext JS, LLC.
24427  *
24428  * Originally Released Under LGPL - original licence link has changed is not relivant.
24429  *
24430  * Fork - LGPL
24431  * <script type="text/javascript">
24432  */
24433 /**
24434  * @class Roo.form.Checkbox
24435  * @extends Roo.form.Field
24436  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24437  * @constructor
24438  * Creates a new Checkbox
24439  * @param {Object} config Configuration options
24440  */
24441 Roo.form.Checkbox = function(config){
24442     Roo.form.Checkbox.superclass.constructor.call(this, config);
24443     this.addEvents({
24444         /**
24445          * @event check
24446          * Fires when the checkbox is checked or unchecked.
24447              * @param {Roo.form.Checkbox} this This checkbox
24448              * @param {Boolean} checked The new checked value
24449              */
24450         check : true
24451     });
24452 };
24453
24454 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24455     /**
24456      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24457      */
24458     focusClass : undefined,
24459     /**
24460      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24461      */
24462     fieldClass: "x-form-field",
24463     /**
24464      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24465      */
24466     checked: false,
24467     /**
24468      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24469      * {tag: "input", type: "checkbox", autocomplete: "off"})
24470      */
24471     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24472     /**
24473      * @cfg {String} boxLabel The text that appears beside the checkbox
24474      */
24475     boxLabel : "",
24476     /**
24477      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24478      */  
24479     inputValue : '1',
24480     /**
24481      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24482      */
24483      valueOff: '0', // value when not checked..
24484
24485     actionMode : 'viewEl', 
24486     //
24487     // private
24488     itemCls : 'x-menu-check-item x-form-item',
24489     groupClass : 'x-menu-group-item',
24490     inputType : 'hidden',
24491     
24492     
24493     inSetChecked: false, // check that we are not calling self...
24494     
24495     inputElement: false, // real input element?
24496     basedOn: false, // ????
24497     
24498     isFormField: true, // not sure where this is needed!!!!
24499
24500     onResize : function(){
24501         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24502         if(!this.boxLabel){
24503             this.el.alignTo(this.wrap, 'c-c');
24504         }
24505     },
24506
24507     initEvents : function(){
24508         Roo.form.Checkbox.superclass.initEvents.call(this);
24509         this.el.on("click", this.onClick,  this);
24510         this.el.on("change", this.onClick,  this);
24511     },
24512
24513
24514     getResizeEl : function(){
24515         return this.wrap;
24516     },
24517
24518     getPositionEl : function(){
24519         return this.wrap;
24520     },
24521
24522     // private
24523     onRender : function(ct, position){
24524         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24525         /*
24526         if(this.inputValue !== undefined){
24527             this.el.dom.value = this.inputValue;
24528         }
24529         */
24530         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24531         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24532         var viewEl = this.wrap.createChild({ 
24533             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24534         this.viewEl = viewEl;   
24535         this.wrap.on('click', this.onClick,  this); 
24536         
24537         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24538         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24539         
24540         
24541         
24542         if(this.boxLabel){
24543             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24544         //    viewEl.on('click', this.onClick,  this); 
24545         }
24546         //if(this.checked){
24547             this.setChecked(this.checked);
24548         //}else{
24549             //this.checked = this.el.dom;
24550         //}
24551
24552     },
24553
24554     // private
24555     initValue : Roo.emptyFn,
24556
24557     /**
24558      * Returns the checked state of the checkbox.
24559      * @return {Boolean} True if checked, else false
24560      */
24561     getValue : function(){
24562         if(this.el){
24563             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24564         }
24565         return this.valueOff;
24566         
24567     },
24568
24569         // private
24570     onClick : function(){ 
24571         if (this.disabled) {
24572             return;
24573         }
24574         this.setChecked(!this.checked);
24575
24576         //if(this.el.dom.checked != this.checked){
24577         //    this.setValue(this.el.dom.checked);
24578        // }
24579     },
24580
24581     /**
24582      * Sets the checked state of the checkbox.
24583      * On is always based on a string comparison between inputValue and the param.
24584      * @param {Boolean/String} value - the value to set 
24585      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24586      */
24587     setValue : function(v,suppressEvent){
24588         
24589         
24590         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24591         //if(this.el && this.el.dom){
24592         //    this.el.dom.checked = this.checked;
24593         //    this.el.dom.defaultChecked = this.checked;
24594         //}
24595         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24596         //this.fireEvent("check", this, this.checked);
24597     },
24598     // private..
24599     setChecked : function(state,suppressEvent)
24600     {
24601         if (this.inSetChecked) {
24602             this.checked = state;
24603             return;
24604         }
24605         
24606     
24607         if(this.wrap){
24608             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24609         }
24610         this.checked = state;
24611         if(suppressEvent !== true){
24612             this.fireEvent('check', this, state);
24613         }
24614         this.inSetChecked = true;
24615         this.el.dom.value = state ? this.inputValue : this.valueOff;
24616         this.inSetChecked = false;
24617         
24618     },
24619     // handle setting of hidden value by some other method!!?!?
24620     setFromHidden: function()
24621     {
24622         if(!this.el){
24623             return;
24624         }
24625         //console.log("SET FROM HIDDEN");
24626         //alert('setFrom hidden');
24627         this.setValue(this.el.dom.value);
24628     },
24629     
24630     onDestroy : function()
24631     {
24632         if(this.viewEl){
24633             Roo.get(this.viewEl).remove();
24634         }
24635          
24636         Roo.form.Checkbox.superclass.onDestroy.call(this);
24637     }
24638
24639 });/*
24640  * Based on:
24641  * Ext JS Library 1.1.1
24642  * Copyright(c) 2006-2007, Ext JS, LLC.
24643  *
24644  * Originally Released Under LGPL - original licence link has changed is not relivant.
24645  *
24646  * Fork - LGPL
24647  * <script type="text/javascript">
24648  */
24649  
24650 /**
24651  * @class Roo.form.Radio
24652  * @extends Roo.form.Checkbox
24653  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24654  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24655  * @constructor
24656  * Creates a new Radio
24657  * @param {Object} config Configuration options
24658  */
24659 Roo.form.Radio = function(){
24660     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24661 };
24662 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24663     inputType: 'radio',
24664
24665     /**
24666      * If this radio is part of a group, it will return the selected value
24667      * @return {String}
24668      */
24669     getGroupValue : function(){
24670         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24671     },
24672     
24673     
24674     onRender : function(ct, position){
24675         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24676         
24677         if(this.inputValue !== undefined){
24678             this.el.dom.value = this.inputValue;
24679         }
24680          
24681         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24682         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24683         //var viewEl = this.wrap.createChild({ 
24684         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24685         //this.viewEl = viewEl;   
24686         //this.wrap.on('click', this.onClick,  this); 
24687         
24688         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24689         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24690         
24691         
24692         
24693         if(this.boxLabel){
24694             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24695         //    viewEl.on('click', this.onClick,  this); 
24696         }
24697          if(this.checked){
24698             this.el.dom.checked =   'checked' ;
24699         }
24700          
24701     } 
24702     
24703     
24704 });//<script type="text/javascript">
24705
24706 /*
24707  * Based  Ext JS Library 1.1.1
24708  * Copyright(c) 2006-2007, Ext JS, LLC.
24709  * LGPL
24710  *
24711  */
24712  
24713 /**
24714  * @class Roo.HtmlEditorCore
24715  * @extends Roo.Component
24716  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24717  *
24718  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24719  */
24720
24721 Roo.HtmlEditorCore = function(config){
24722     
24723     
24724     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24725     
24726     
24727     this.addEvents({
24728         /**
24729          * @event initialize
24730          * Fires when the editor is fully initialized (including the iframe)
24731          * @param {Roo.HtmlEditorCore} this
24732          */
24733         initialize: true,
24734         /**
24735          * @event activate
24736          * Fires when the editor is first receives the focus. Any insertion must wait
24737          * until after this event.
24738          * @param {Roo.HtmlEditorCore} this
24739          */
24740         activate: true,
24741          /**
24742          * @event beforesync
24743          * Fires before the textarea is updated with content from the editor iframe. Return false
24744          * to cancel the sync.
24745          * @param {Roo.HtmlEditorCore} this
24746          * @param {String} html
24747          */
24748         beforesync: true,
24749          /**
24750          * @event beforepush
24751          * Fires before the iframe editor is updated with content from the textarea. Return false
24752          * to cancel the push.
24753          * @param {Roo.HtmlEditorCore} this
24754          * @param {String} html
24755          */
24756         beforepush: true,
24757          /**
24758          * @event sync
24759          * Fires when the textarea is updated with content from the editor iframe.
24760          * @param {Roo.HtmlEditorCore} this
24761          * @param {String} html
24762          */
24763         sync: true,
24764          /**
24765          * @event push
24766          * Fires when the iframe editor is updated with content from the textarea.
24767          * @param {Roo.HtmlEditorCore} this
24768          * @param {String} html
24769          */
24770         push: true,
24771         
24772         /**
24773          * @event editorevent
24774          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24775          * @param {Roo.HtmlEditorCore} this
24776          */
24777         editorevent: true
24778         
24779     });
24780     
24781     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24782     
24783     // defaults : white / black...
24784     this.applyBlacklists();
24785     
24786     
24787     
24788 };
24789
24790
24791 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24792
24793
24794      /**
24795      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24796      */
24797     
24798     owner : false,
24799     
24800      /**
24801      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24802      *                        Roo.resizable.
24803      */
24804     resizable : false,
24805      /**
24806      * @cfg {Number} height (in pixels)
24807      */   
24808     height: 300,
24809    /**
24810      * @cfg {Number} width (in pixels)
24811      */   
24812     width: 500,
24813     
24814     /**
24815      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24816      * 
24817      */
24818     stylesheets: false,
24819     
24820     // id of frame..
24821     frameId: false,
24822     
24823     // private properties
24824     validationEvent : false,
24825     deferHeight: true,
24826     initialized : false,
24827     activated : false,
24828     sourceEditMode : false,
24829     onFocus : Roo.emptyFn,
24830     iframePad:3,
24831     hideMode:'offsets',
24832     
24833     clearUp: true,
24834     
24835     // blacklist + whitelisted elements..
24836     black: false,
24837     white: false,
24838      
24839     
24840
24841     /**
24842      * Protected method that will not generally be called directly. It
24843      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24844      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24845      */
24846     getDocMarkup : function(){
24847         // body styles..
24848         var st = '';
24849         
24850         // inherit styels from page...?? 
24851         if (this.stylesheets === false) {
24852             
24853             Roo.get(document.head).select('style').each(function(node) {
24854                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24855             });
24856             
24857             Roo.get(document.head).select('link').each(function(node) { 
24858                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24859             });
24860             
24861         } else if (!this.stylesheets.length) {
24862                 // simple..
24863                 st = '<style type="text/css">' +
24864                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24865                    '</style>';
24866         } else { 
24867             
24868         }
24869         
24870         st +=  '<style type="text/css">' +
24871             'IMG { cursor: pointer } ' +
24872         '</style>';
24873
24874         
24875         return '<html><head>' + st  +
24876             //<style type="text/css">' +
24877             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24878             //'</style>' +
24879             ' </head><body class="roo-htmleditor-body"></body></html>';
24880     },
24881
24882     // private
24883     onRender : function(ct, position)
24884     {
24885         var _t = this;
24886         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24887         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24888         
24889         
24890         this.el.dom.style.border = '0 none';
24891         this.el.dom.setAttribute('tabIndex', -1);
24892         this.el.addClass('x-hidden hide');
24893         
24894         
24895         
24896         if(Roo.isIE){ // fix IE 1px bogus margin
24897             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24898         }
24899        
24900         
24901         this.frameId = Roo.id();
24902         
24903          
24904         
24905         var iframe = this.owner.wrap.createChild({
24906             tag: 'iframe',
24907             cls: 'form-control', // bootstrap..
24908             id: this.frameId,
24909             name: this.frameId,
24910             frameBorder : 'no',
24911             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24912         }, this.el
24913         );
24914         
24915         
24916         this.iframe = iframe.dom;
24917
24918          this.assignDocWin();
24919         
24920         this.doc.designMode = 'on';
24921        
24922         this.doc.open();
24923         this.doc.write(this.getDocMarkup());
24924         this.doc.close();
24925
24926         
24927         var task = { // must defer to wait for browser to be ready
24928             run : function(){
24929                 //console.log("run task?" + this.doc.readyState);
24930                 this.assignDocWin();
24931                 if(this.doc.body || this.doc.readyState == 'complete'){
24932                     try {
24933                         this.doc.designMode="on";
24934                     } catch (e) {
24935                         return;
24936                     }
24937                     Roo.TaskMgr.stop(task);
24938                     this.initEditor.defer(10, this);
24939                 }
24940             },
24941             interval : 10,
24942             duration: 10000,
24943             scope: this
24944         };
24945         Roo.TaskMgr.start(task);
24946
24947     },
24948
24949     // private
24950     onResize : function(w, h)
24951     {
24952          Roo.log('resize: ' +w + ',' + h );
24953         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24954         if(!this.iframe){
24955             return;
24956         }
24957         if(typeof w == 'number'){
24958             
24959             this.iframe.style.width = w + 'px';
24960         }
24961         if(typeof h == 'number'){
24962             
24963             this.iframe.style.height = h + 'px';
24964             if(this.doc){
24965                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24966             }
24967         }
24968         
24969     },
24970
24971     /**
24972      * Toggles the editor between standard and source edit mode.
24973      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24974      */
24975     toggleSourceEdit : function(sourceEditMode){
24976         
24977         this.sourceEditMode = sourceEditMode === true;
24978         
24979         if(this.sourceEditMode){
24980  
24981             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24982             
24983         }else{
24984             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24985             //this.iframe.className = '';
24986             this.deferFocus();
24987         }
24988         //this.setSize(this.owner.wrap.getSize());
24989         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24990     },
24991
24992     
24993   
24994
24995     /**
24996      * Protected method that will not generally be called directly. If you need/want
24997      * custom HTML cleanup, this is the method you should override.
24998      * @param {String} html The HTML to be cleaned
24999      * return {String} The cleaned HTML
25000      */
25001     cleanHtml : function(html){
25002         html = String(html);
25003         if(html.length > 5){
25004             if(Roo.isSafari){ // strip safari nonsense
25005                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25006             }
25007         }
25008         if(html == '&nbsp;'){
25009             html = '';
25010         }
25011         return html;
25012     },
25013
25014     /**
25015      * HTML Editor -> Textarea
25016      * Protected method that will not generally be called directly. Syncs the contents
25017      * of the editor iframe with the textarea.
25018      */
25019     syncValue : function(){
25020         if(this.initialized){
25021             var bd = (this.doc.body || this.doc.documentElement);
25022             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25023             var html = bd.innerHTML;
25024             if(Roo.isSafari){
25025                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25026                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25027                 if(m && m[1]){
25028                     html = '<div style="'+m[0]+'">' + html + '</div>';
25029                 }
25030             }
25031             html = this.cleanHtml(html);
25032             // fix up the special chars.. normaly like back quotes in word...
25033             // however we do not want to do this with chinese..
25034             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25035                 var cc = b.charCodeAt();
25036                 if (
25037                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25038                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25039                     (cc >= 0xf900 && cc < 0xfb00 )
25040                 ) {
25041                         return b;
25042                 }
25043                 return "&#"+cc+";" 
25044             });
25045             if(this.owner.fireEvent('beforesync', this, html) !== false){
25046                 this.el.dom.value = html;
25047                 this.owner.fireEvent('sync', this, html);
25048             }
25049         }
25050     },
25051
25052     /**
25053      * Protected method that will not generally be called directly. Pushes the value of the textarea
25054      * into the iframe editor.
25055      */
25056     pushValue : function(){
25057         if(this.initialized){
25058             var v = this.el.dom.value.trim();
25059             
25060 //            if(v.length < 1){
25061 //                v = '&#160;';
25062 //            }
25063             
25064             if(this.owner.fireEvent('beforepush', this, v) !== false){
25065                 var d = (this.doc.body || this.doc.documentElement);
25066                 d.innerHTML = v;
25067                 this.cleanUpPaste();
25068                 this.el.dom.value = d.innerHTML;
25069                 this.owner.fireEvent('push', this, v);
25070             }
25071         }
25072     },
25073
25074     // private
25075     deferFocus : function(){
25076         this.focus.defer(10, this);
25077     },
25078
25079     // doc'ed in Field
25080     focus : function(){
25081         if(this.win && !this.sourceEditMode){
25082             this.win.focus();
25083         }else{
25084             this.el.focus();
25085         }
25086     },
25087     
25088     assignDocWin: function()
25089     {
25090         var iframe = this.iframe;
25091         
25092          if(Roo.isIE){
25093             this.doc = iframe.contentWindow.document;
25094             this.win = iframe.contentWindow;
25095         } else {
25096 //            if (!Roo.get(this.frameId)) {
25097 //                return;
25098 //            }
25099 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25100 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25101             
25102             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25103                 return;
25104             }
25105             
25106             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25107             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25108         }
25109     },
25110     
25111     // private
25112     initEditor : function(){
25113         //console.log("INIT EDITOR");
25114         this.assignDocWin();
25115         
25116         
25117         
25118         this.doc.designMode="on";
25119         this.doc.open();
25120         this.doc.write(this.getDocMarkup());
25121         this.doc.close();
25122         
25123         var dbody = (this.doc.body || this.doc.documentElement);
25124         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25125         // this copies styles from the containing element into thsi one..
25126         // not sure why we need all of this..
25127         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25128         
25129         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25130         //ss['background-attachment'] = 'fixed'; // w3c
25131         dbody.bgProperties = 'fixed'; // ie
25132         //Roo.DomHelper.applyStyles(dbody, ss);
25133         Roo.EventManager.on(this.doc, {
25134             //'mousedown': this.onEditorEvent,
25135             'mouseup': this.onEditorEvent,
25136             'dblclick': this.onEditorEvent,
25137             'click': this.onEditorEvent,
25138             'keyup': this.onEditorEvent,
25139             buffer:100,
25140             scope: this
25141         });
25142         if(Roo.isGecko){
25143             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25144         }
25145         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25146             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25147         }
25148         this.initialized = true;
25149
25150         this.owner.fireEvent('initialize', this);
25151         this.pushValue();
25152     },
25153
25154     // private
25155     onDestroy : function(){
25156         
25157         
25158         
25159         if(this.rendered){
25160             
25161             //for (var i =0; i < this.toolbars.length;i++) {
25162             //    // fixme - ask toolbars for heights?
25163             //    this.toolbars[i].onDestroy();
25164            // }
25165             
25166             //this.wrap.dom.innerHTML = '';
25167             //this.wrap.remove();
25168         }
25169     },
25170
25171     // private
25172     onFirstFocus : function(){
25173         
25174         this.assignDocWin();
25175         
25176         
25177         this.activated = true;
25178          
25179     
25180         if(Roo.isGecko){ // prevent silly gecko errors
25181             this.win.focus();
25182             var s = this.win.getSelection();
25183             if(!s.focusNode || s.focusNode.nodeType != 3){
25184                 var r = s.getRangeAt(0);
25185                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25186                 r.collapse(true);
25187                 this.deferFocus();
25188             }
25189             try{
25190                 this.execCmd('useCSS', true);
25191                 this.execCmd('styleWithCSS', false);
25192             }catch(e){}
25193         }
25194         this.owner.fireEvent('activate', this);
25195     },
25196
25197     // private
25198     adjustFont: function(btn){
25199         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25200         //if(Roo.isSafari){ // safari
25201         //    adjust *= 2;
25202        // }
25203         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25204         if(Roo.isSafari){ // safari
25205             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25206             v =  (v < 10) ? 10 : v;
25207             v =  (v > 48) ? 48 : v;
25208             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25209             
25210         }
25211         
25212         
25213         v = Math.max(1, v+adjust);
25214         
25215         this.execCmd('FontSize', v  );
25216     },
25217
25218     onEditorEvent : function(e){
25219         this.owner.fireEvent('editorevent', this, e);
25220       //  this.updateToolbar();
25221         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25222     },
25223
25224     insertTag : function(tg)
25225     {
25226         // could be a bit smarter... -> wrap the current selected tRoo..
25227         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25228             
25229             range = this.createRange(this.getSelection());
25230             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25231             wrappingNode.appendChild(range.extractContents());
25232             range.insertNode(wrappingNode);
25233
25234             return;
25235             
25236             
25237             
25238         }
25239         this.execCmd("formatblock",   tg);
25240         
25241     },
25242     
25243     insertText : function(txt)
25244     {
25245         
25246         
25247         var range = this.createRange();
25248         range.deleteContents();
25249                //alert(Sender.getAttribute('label'));
25250                
25251         range.insertNode(this.doc.createTextNode(txt));
25252     } ,
25253     
25254      
25255
25256     /**
25257      * Executes a Midas editor command on the editor document and performs necessary focus and
25258      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25259      * @param {String} cmd The Midas command
25260      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25261      */
25262     relayCmd : function(cmd, value){
25263         this.win.focus();
25264         this.execCmd(cmd, value);
25265         this.owner.fireEvent('editorevent', this);
25266         //this.updateToolbar();
25267         this.owner.deferFocus();
25268     },
25269
25270     /**
25271      * Executes a Midas editor command directly on the editor document.
25272      * For visual commands, you should use {@link #relayCmd} instead.
25273      * <b>This should only be called after the editor is initialized.</b>
25274      * @param {String} cmd The Midas command
25275      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25276      */
25277     execCmd : function(cmd, value){
25278         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25279         this.syncValue();
25280     },
25281  
25282  
25283    
25284     /**
25285      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25286      * to insert tRoo.
25287      * @param {String} text | dom node.. 
25288      */
25289     insertAtCursor : function(text)
25290     {
25291         
25292         
25293         
25294         if(!this.activated){
25295             return;
25296         }
25297         /*
25298         if(Roo.isIE){
25299             this.win.focus();
25300             var r = this.doc.selection.createRange();
25301             if(r){
25302                 r.collapse(true);
25303                 r.pasteHTML(text);
25304                 this.syncValue();
25305                 this.deferFocus();
25306             
25307             }
25308             return;
25309         }
25310         */
25311         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25312             this.win.focus();
25313             
25314             
25315             // from jquery ui (MIT licenced)
25316             var range, node;
25317             var win = this.win;
25318             
25319             if (win.getSelection && win.getSelection().getRangeAt) {
25320                 range = win.getSelection().getRangeAt(0);
25321                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25322                 range.insertNode(node);
25323             } else if (win.document.selection && win.document.selection.createRange) {
25324                 // no firefox support
25325                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25326                 win.document.selection.createRange().pasteHTML(txt);
25327             } else {
25328                 // no firefox support
25329                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25330                 this.execCmd('InsertHTML', txt);
25331             } 
25332             
25333             this.syncValue();
25334             
25335             this.deferFocus();
25336         }
25337     },
25338  // private
25339     mozKeyPress : function(e){
25340         if(e.ctrlKey){
25341             var c = e.getCharCode(), cmd;
25342           
25343             if(c > 0){
25344                 c = String.fromCharCode(c).toLowerCase();
25345                 switch(c){
25346                     case 'b':
25347                         cmd = 'bold';
25348                         break;
25349                     case 'i':
25350                         cmd = 'italic';
25351                         break;
25352                     
25353                     case 'u':
25354                         cmd = 'underline';
25355                         break;
25356                     
25357                     case 'v':
25358                         this.cleanUpPaste.defer(100, this);
25359                         return;
25360                         
25361                 }
25362                 if(cmd){
25363                     this.win.focus();
25364                     this.execCmd(cmd);
25365                     this.deferFocus();
25366                     e.preventDefault();
25367                 }
25368                 
25369             }
25370         }
25371     },
25372
25373     // private
25374     fixKeys : function(){ // load time branching for fastest keydown performance
25375         if(Roo.isIE){
25376             return function(e){
25377                 var k = e.getKey(), r;
25378                 if(k == e.TAB){
25379                     e.stopEvent();
25380                     r = this.doc.selection.createRange();
25381                     if(r){
25382                         r.collapse(true);
25383                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25384                         this.deferFocus();
25385                     }
25386                     return;
25387                 }
25388                 
25389                 if(k == e.ENTER){
25390                     r = this.doc.selection.createRange();
25391                     if(r){
25392                         var target = r.parentElement();
25393                         if(!target || target.tagName.toLowerCase() != 'li'){
25394                             e.stopEvent();
25395                             r.pasteHTML('<br />');
25396                             r.collapse(false);
25397                             r.select();
25398                         }
25399                     }
25400                 }
25401                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25402                     this.cleanUpPaste.defer(100, this);
25403                     return;
25404                 }
25405                 
25406                 
25407             };
25408         }else if(Roo.isOpera){
25409             return function(e){
25410                 var k = e.getKey();
25411                 if(k == e.TAB){
25412                     e.stopEvent();
25413                     this.win.focus();
25414                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25415                     this.deferFocus();
25416                 }
25417                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25418                     this.cleanUpPaste.defer(100, this);
25419                     return;
25420                 }
25421                 
25422             };
25423         }else if(Roo.isSafari){
25424             return function(e){
25425                 var k = e.getKey();
25426                 
25427                 if(k == e.TAB){
25428                     e.stopEvent();
25429                     this.execCmd('InsertText','\t');
25430                     this.deferFocus();
25431                     return;
25432                 }
25433                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25434                     this.cleanUpPaste.defer(100, this);
25435                     return;
25436                 }
25437                 
25438              };
25439         }
25440     }(),
25441     
25442     getAllAncestors: function()
25443     {
25444         var p = this.getSelectedNode();
25445         var a = [];
25446         if (!p) {
25447             a.push(p); // push blank onto stack..
25448             p = this.getParentElement();
25449         }
25450         
25451         
25452         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25453             a.push(p);
25454             p = p.parentNode;
25455         }
25456         a.push(this.doc.body);
25457         return a;
25458     },
25459     lastSel : false,
25460     lastSelNode : false,
25461     
25462     
25463     getSelection : function() 
25464     {
25465         this.assignDocWin();
25466         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25467     },
25468     
25469     getSelectedNode: function() 
25470     {
25471         // this may only work on Gecko!!!
25472         
25473         // should we cache this!!!!
25474         
25475         
25476         
25477          
25478         var range = this.createRange(this.getSelection()).cloneRange();
25479         
25480         if (Roo.isIE) {
25481             var parent = range.parentElement();
25482             while (true) {
25483                 var testRange = range.duplicate();
25484                 testRange.moveToElementText(parent);
25485                 if (testRange.inRange(range)) {
25486                     break;
25487                 }
25488                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25489                     break;
25490                 }
25491                 parent = parent.parentElement;
25492             }
25493             return parent;
25494         }
25495         
25496         // is ancestor a text element.
25497         var ac =  range.commonAncestorContainer;
25498         if (ac.nodeType == 3) {
25499             ac = ac.parentNode;
25500         }
25501         
25502         var ar = ac.childNodes;
25503          
25504         var nodes = [];
25505         var other_nodes = [];
25506         var has_other_nodes = false;
25507         for (var i=0;i<ar.length;i++) {
25508             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25509                 continue;
25510             }
25511             // fullly contained node.
25512             
25513             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25514                 nodes.push(ar[i]);
25515                 continue;
25516             }
25517             
25518             // probably selected..
25519             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25520                 other_nodes.push(ar[i]);
25521                 continue;
25522             }
25523             // outer..
25524             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25525                 continue;
25526             }
25527             
25528             
25529             has_other_nodes = true;
25530         }
25531         if (!nodes.length && other_nodes.length) {
25532             nodes= other_nodes;
25533         }
25534         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25535             return false;
25536         }
25537         
25538         return nodes[0];
25539     },
25540     createRange: function(sel)
25541     {
25542         // this has strange effects when using with 
25543         // top toolbar - not sure if it's a great idea.
25544         //this.editor.contentWindow.focus();
25545         if (typeof sel != "undefined") {
25546             try {
25547                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25548             } catch(e) {
25549                 return this.doc.createRange();
25550             }
25551         } else {
25552             return this.doc.createRange();
25553         }
25554     },
25555     getParentElement: function()
25556     {
25557         
25558         this.assignDocWin();
25559         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25560         
25561         var range = this.createRange(sel);
25562          
25563         try {
25564             var p = range.commonAncestorContainer;
25565             while (p.nodeType == 3) { // text node
25566                 p = p.parentNode;
25567             }
25568             return p;
25569         } catch (e) {
25570             return null;
25571         }
25572     
25573     },
25574     /***
25575      *
25576      * Range intersection.. the hard stuff...
25577      *  '-1' = before
25578      *  '0' = hits..
25579      *  '1' = after.
25580      *         [ -- selected range --- ]
25581      *   [fail]                        [fail]
25582      *
25583      *    basically..
25584      *      if end is before start or  hits it. fail.
25585      *      if start is after end or hits it fail.
25586      *
25587      *   if either hits (but other is outside. - then it's not 
25588      *   
25589      *    
25590      **/
25591     
25592     
25593     // @see http://www.thismuchiknow.co.uk/?p=64.
25594     rangeIntersectsNode : function(range, node)
25595     {
25596         var nodeRange = node.ownerDocument.createRange();
25597         try {
25598             nodeRange.selectNode(node);
25599         } catch (e) {
25600             nodeRange.selectNodeContents(node);
25601         }
25602     
25603         var rangeStartRange = range.cloneRange();
25604         rangeStartRange.collapse(true);
25605     
25606         var rangeEndRange = range.cloneRange();
25607         rangeEndRange.collapse(false);
25608     
25609         var nodeStartRange = nodeRange.cloneRange();
25610         nodeStartRange.collapse(true);
25611     
25612         var nodeEndRange = nodeRange.cloneRange();
25613         nodeEndRange.collapse(false);
25614     
25615         return rangeStartRange.compareBoundaryPoints(
25616                  Range.START_TO_START, nodeEndRange) == -1 &&
25617                rangeEndRange.compareBoundaryPoints(
25618                  Range.START_TO_START, nodeStartRange) == 1;
25619         
25620          
25621     },
25622     rangeCompareNode : function(range, node)
25623     {
25624         var nodeRange = node.ownerDocument.createRange();
25625         try {
25626             nodeRange.selectNode(node);
25627         } catch (e) {
25628             nodeRange.selectNodeContents(node);
25629         }
25630         
25631         
25632         range.collapse(true);
25633     
25634         nodeRange.collapse(true);
25635      
25636         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25637         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25638          
25639         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25640         
25641         var nodeIsBefore   =  ss == 1;
25642         var nodeIsAfter    = ee == -1;
25643         
25644         if (nodeIsBefore && nodeIsAfter)
25645             return 0; // outer
25646         if (!nodeIsBefore && nodeIsAfter)
25647             return 1; //right trailed.
25648         
25649         if (nodeIsBefore && !nodeIsAfter)
25650             return 2;  // left trailed.
25651         // fully contined.
25652         return 3;
25653     },
25654
25655     // private? - in a new class?
25656     cleanUpPaste :  function()
25657     {
25658         // cleans up the whole document..
25659         Roo.log('cleanuppaste');
25660         
25661         this.cleanUpChildren(this.doc.body);
25662         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25663         if (clean != this.doc.body.innerHTML) {
25664             this.doc.body.innerHTML = clean;
25665         }
25666         
25667     },
25668     
25669     cleanWordChars : function(input) {// change the chars to hex code
25670         var he = Roo.HtmlEditorCore;
25671         
25672         var output = input;
25673         Roo.each(he.swapCodes, function(sw) { 
25674             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25675             
25676             output = output.replace(swapper, sw[1]);
25677         });
25678         
25679         return output;
25680     },
25681     
25682     
25683     cleanUpChildren : function (n)
25684     {
25685         if (!n.childNodes.length) {
25686             return;
25687         }
25688         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25689            this.cleanUpChild(n.childNodes[i]);
25690         }
25691     },
25692     
25693     
25694         
25695     
25696     cleanUpChild : function (node)
25697     {
25698         var ed = this;
25699         //console.log(node);
25700         if (node.nodeName == "#text") {
25701             // clean up silly Windows -- stuff?
25702             return; 
25703         }
25704         if (node.nodeName == "#comment") {
25705             node.parentNode.removeChild(node);
25706             // clean up silly Windows -- stuff?
25707             return; 
25708         }
25709         var lcname = node.tagName.toLowerCase();
25710         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25711         // whitelist of tags..
25712         
25713         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25714             // remove node.
25715             node.parentNode.removeChild(node);
25716             return;
25717             
25718         }
25719         
25720         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25721         
25722         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25723         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25724         
25725         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25726         //    remove_keep_children = true;
25727         //}
25728         
25729         if (remove_keep_children) {
25730             this.cleanUpChildren(node);
25731             // inserts everything just before this node...
25732             while (node.childNodes.length) {
25733                 var cn = node.childNodes[0];
25734                 node.removeChild(cn);
25735                 node.parentNode.insertBefore(cn, node);
25736             }
25737             node.parentNode.removeChild(node);
25738             return;
25739         }
25740         
25741         if (!node.attributes || !node.attributes.length) {
25742             this.cleanUpChildren(node);
25743             return;
25744         }
25745         
25746         function cleanAttr(n,v)
25747         {
25748             
25749             if (v.match(/^\./) || v.match(/^\//)) {
25750                 return;
25751             }
25752             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25753                 return;
25754             }
25755             if (v.match(/^#/)) {
25756                 return;
25757             }
25758 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25759             node.removeAttribute(n);
25760             
25761         }
25762         
25763         var cwhite = this.cwhite;
25764         var cblack = this.cblack;
25765             
25766         function cleanStyle(n,v)
25767         {
25768             if (v.match(/expression/)) { //XSS?? should we even bother..
25769                 node.removeAttribute(n);
25770                 return;
25771             }
25772             
25773             var parts = v.split(/;/);
25774             var clean = [];
25775             
25776             Roo.each(parts, function(p) {
25777                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25778                 if (!p.length) {
25779                     return true;
25780                 }
25781                 var l = p.split(':').shift().replace(/\s+/g,'');
25782                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25783                 
25784                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25785 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25786                     //node.removeAttribute(n);
25787                     return true;
25788                 }
25789                 //Roo.log()
25790                 // only allow 'c whitelisted system attributes'
25791                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25792 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25793                     //node.removeAttribute(n);
25794                     return true;
25795                 }
25796                 
25797                 
25798                  
25799                 
25800                 clean.push(p);
25801                 return true;
25802             });
25803             if (clean.length) { 
25804                 node.setAttribute(n, clean.join(';'));
25805             } else {
25806                 node.removeAttribute(n);
25807             }
25808             
25809         }
25810         
25811         
25812         for (var i = node.attributes.length-1; i > -1 ; i--) {
25813             var a = node.attributes[i];
25814             //console.log(a);
25815             
25816             if (a.name.toLowerCase().substr(0,2)=='on')  {
25817                 node.removeAttribute(a.name);
25818                 continue;
25819             }
25820             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25821                 node.removeAttribute(a.name);
25822                 continue;
25823             }
25824             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25825                 cleanAttr(a.name,a.value); // fixme..
25826                 continue;
25827             }
25828             if (a.name == 'style') {
25829                 cleanStyle(a.name,a.value);
25830                 continue;
25831             }
25832             /// clean up MS crap..
25833             // tecnically this should be a list of valid class'es..
25834             
25835             
25836             if (a.name == 'class') {
25837                 if (a.value.match(/^Mso/)) {
25838                     node.className = '';
25839                 }
25840                 
25841                 if (a.value.match(/body/)) {
25842                     node.className = '';
25843                 }
25844                 continue;
25845             }
25846             
25847             // style cleanup!?
25848             // class cleanup?
25849             
25850         }
25851         
25852         
25853         this.cleanUpChildren(node);
25854         
25855         
25856     },
25857     /**
25858      * Clean up MS wordisms...
25859      */
25860     cleanWord : function(node)
25861     {
25862         var _t = this;
25863         var cleanWordChildren = function()
25864         {
25865             if (!node.childNodes.length) {
25866                 return;
25867             }
25868             for (var i = node.childNodes.length-1; i > -1 ; i--) {
25869                _t.cleanWord(node.childNodes[i]);
25870             }
25871         }
25872         
25873         
25874         if (!node) {
25875             this.cleanWord(this.doc.body);
25876             return;
25877         }
25878         if (node.nodeName == "#text") {
25879             // clean up silly Windows -- stuff?
25880             return; 
25881         }
25882         if (node.nodeName == "#comment") {
25883             node.parentNode.removeChild(node);
25884             // clean up silly Windows -- stuff?
25885             return; 
25886         }
25887         
25888         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25889             node.parentNode.removeChild(node);
25890             return;
25891         }
25892         
25893         // remove - but keep children..
25894         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25895             while (node.childNodes.length) {
25896                 var cn = node.childNodes[0];
25897                 node.removeChild(cn);
25898                 node.parentNode.insertBefore(cn, node);
25899             }
25900             node.parentNode.removeChild(node);
25901             cleanWordChildren();
25902             return;
25903         }
25904         // clean styles
25905         if (node.className.length) {
25906             
25907             var cn = node.className.split(/\W+/);
25908             var cna = [];
25909             Roo.each(cn, function(cls) {
25910                 if (cls.match(/Mso[a-zA-Z]+/)) {
25911                     return;
25912                 }
25913                 cna.push(cls);
25914             });
25915             node.className = cna.length ? cna.join(' ') : '';
25916             if (!cna.length) {
25917                 node.removeAttribute("class");
25918             }
25919         }
25920         
25921         if (node.hasAttribute("lang")) {
25922             node.removeAttribute("lang");
25923         }
25924         
25925         if (node.hasAttribute("style")) {
25926             
25927             var styles = node.getAttribute("style").split(";");
25928             var nstyle = [];
25929             Roo.each(styles, function(s) {
25930                 if (!s.match(/:/)) {
25931                     return;
25932                 }
25933                 var kv = s.split(":");
25934                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25935                     return;
25936                 }
25937                 // what ever is left... we allow.
25938                 nstyle.push(s);
25939             });
25940             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25941             if (!nstyle.length) {
25942                 node.removeAttribute('style');
25943             }
25944         }
25945         
25946         cleanWordChildren();
25947         
25948         
25949     },
25950     domToHTML : function(currentElement, depth, nopadtext) {
25951         
25952         depth = depth || 0;
25953         nopadtext = nopadtext || false;
25954     
25955         if (!currentElement) {
25956             return this.domToHTML(this.doc.body);
25957         }
25958         
25959         //Roo.log(currentElement);
25960         var j;
25961         var allText = false;
25962         var nodeName = currentElement.nodeName;
25963         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25964         
25965         if  (nodeName == '#text') {
25966             
25967             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25968         }
25969         
25970         
25971         var ret = '';
25972         if (nodeName != 'BODY') {
25973              
25974             var i = 0;
25975             // Prints the node tagName, such as <A>, <IMG>, etc
25976             if (tagName) {
25977                 var attr = [];
25978                 for(i = 0; i < currentElement.attributes.length;i++) {
25979                     // quoting?
25980                     var aname = currentElement.attributes.item(i).name;
25981                     if (!currentElement.attributes.item(i).value.length) {
25982                         continue;
25983                     }
25984                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25985                 }
25986                 
25987                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25988             } 
25989             else {
25990                 
25991                 // eack
25992             }
25993         } else {
25994             tagName = false;
25995         }
25996         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25997             return ret;
25998         }
25999         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26000             nopadtext = true;
26001         }
26002         
26003         
26004         // Traverse the tree
26005         i = 0;
26006         var currentElementChild = currentElement.childNodes.item(i);
26007         var allText = true;
26008         var innerHTML  = '';
26009         lastnode = '';
26010         while (currentElementChild) {
26011             // Formatting code (indent the tree so it looks nice on the screen)
26012             var nopad = nopadtext;
26013             if (lastnode == 'SPAN') {
26014                 nopad  = true;
26015             }
26016             // text
26017             if  (currentElementChild.nodeName == '#text') {
26018                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26019                 toadd = nopadtext ? toadd : toadd.trim();
26020                 if (!nopad && toadd.length > 80) {
26021                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26022                 }
26023                 innerHTML  += toadd;
26024                 
26025                 i++;
26026                 currentElementChild = currentElement.childNodes.item(i);
26027                 lastNode = '';
26028                 continue;
26029             }
26030             allText = false;
26031             
26032             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26033                 
26034             // Recursively traverse the tree structure of the child node
26035             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26036             lastnode = currentElementChild.nodeName;
26037             i++;
26038             currentElementChild=currentElement.childNodes.item(i);
26039         }
26040         
26041         ret += innerHTML;
26042         
26043         if (!allText) {
26044                 // The remaining code is mostly for formatting the tree
26045             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26046         }
26047         
26048         
26049         if (tagName) {
26050             ret+= "</"+tagName+">";
26051         }
26052         return ret;
26053         
26054     },
26055         
26056     applyBlacklists : function()
26057     {
26058         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26059         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26060         
26061         this.white = [];
26062         this.black = [];
26063         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26064             if (b.indexOf(tag) > -1) {
26065                 return;
26066             }
26067             this.white.push(tag);
26068             
26069         }, this);
26070         
26071         Roo.each(w, function(tag) {
26072             if (b.indexOf(tag) > -1) {
26073                 return;
26074             }
26075             if (this.white.indexOf(tag) > -1) {
26076                 return;
26077             }
26078             this.white.push(tag);
26079             
26080         }, this);
26081         
26082         
26083         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26084             if (w.indexOf(tag) > -1) {
26085                 return;
26086             }
26087             this.black.push(tag);
26088             
26089         }, this);
26090         
26091         Roo.each(b, function(tag) {
26092             if (w.indexOf(tag) > -1) {
26093                 return;
26094             }
26095             if (this.black.indexOf(tag) > -1) {
26096                 return;
26097             }
26098             this.black.push(tag);
26099             
26100         }, this);
26101         
26102         
26103         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26104         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26105         
26106         this.cwhite = [];
26107         this.cblack = [];
26108         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26109             if (b.indexOf(tag) > -1) {
26110                 return;
26111             }
26112             this.cwhite.push(tag);
26113             
26114         }, this);
26115         
26116         Roo.each(w, function(tag) {
26117             if (b.indexOf(tag) > -1) {
26118                 return;
26119             }
26120             if (this.cwhite.indexOf(tag) > -1) {
26121                 return;
26122             }
26123             this.cwhite.push(tag);
26124             
26125         }, this);
26126         
26127         
26128         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26129             if (w.indexOf(tag) > -1) {
26130                 return;
26131             }
26132             this.cblack.push(tag);
26133             
26134         }, this);
26135         
26136         Roo.each(b, function(tag) {
26137             if (w.indexOf(tag) > -1) {
26138                 return;
26139             }
26140             if (this.cblack.indexOf(tag) > -1) {
26141                 return;
26142             }
26143             this.cblack.push(tag);
26144             
26145         }, this);
26146     },
26147     
26148     setStylesheets : function(stylesheets)
26149     {
26150         if(typeof(stylesheets) == 'string'){
26151             Roo.get(this.iframe.contentDocument.head).createChild({
26152                 tag : 'link',
26153                 rel : 'stylesheet',
26154                 type : 'text/css',
26155                 href : stylesheets
26156             });
26157             
26158             return;
26159         }
26160         var _this = this;
26161      
26162         Roo.each(stylesheets, function(s) {
26163             if(!s.length){
26164                 return;
26165             }
26166             
26167             Roo.get(_this.iframe.contentDocument.head).createChild({
26168                 tag : 'link',
26169                 rel : 'stylesheet',
26170                 type : 'text/css',
26171                 href : s
26172             });
26173         });
26174
26175         
26176     },
26177     
26178     removeStylesheets : function()
26179     {
26180         var _this = this;
26181         
26182         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26183             s.remove();
26184         });
26185     }
26186     
26187     // hide stuff that is not compatible
26188     /**
26189      * @event blur
26190      * @hide
26191      */
26192     /**
26193      * @event change
26194      * @hide
26195      */
26196     /**
26197      * @event focus
26198      * @hide
26199      */
26200     /**
26201      * @event specialkey
26202      * @hide
26203      */
26204     /**
26205      * @cfg {String} fieldClass @hide
26206      */
26207     /**
26208      * @cfg {String} focusClass @hide
26209      */
26210     /**
26211      * @cfg {String} autoCreate @hide
26212      */
26213     /**
26214      * @cfg {String} inputType @hide
26215      */
26216     /**
26217      * @cfg {String} invalidClass @hide
26218      */
26219     /**
26220      * @cfg {String} invalidText @hide
26221      */
26222     /**
26223      * @cfg {String} msgFx @hide
26224      */
26225     /**
26226      * @cfg {String} validateOnBlur @hide
26227      */
26228 });
26229
26230 Roo.HtmlEditorCore.white = [
26231         'area', 'br', 'img', 'input', 'hr', 'wbr',
26232         
26233        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26234        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26235        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26236        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26237        'table',   'ul',         'xmp', 
26238        
26239        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26240       'thead',   'tr', 
26241      
26242       'dir', 'menu', 'ol', 'ul', 'dl',
26243        
26244       'embed',  'object'
26245 ];
26246
26247
26248 Roo.HtmlEditorCore.black = [
26249     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26250         'applet', // 
26251         'base',   'basefont', 'bgsound', 'blink',  'body', 
26252         'frame',  'frameset', 'head',    'html',   'ilayer', 
26253         'iframe', 'layer',  'link',     'meta',    'object',   
26254         'script', 'style' ,'title',  'xml' // clean later..
26255 ];
26256 Roo.HtmlEditorCore.clean = [
26257     'script', 'style', 'title', 'xml'
26258 ];
26259 Roo.HtmlEditorCore.remove = [
26260     'font'
26261 ];
26262 // attributes..
26263
26264 Roo.HtmlEditorCore.ablack = [
26265     'on'
26266 ];
26267     
26268 Roo.HtmlEditorCore.aclean = [ 
26269     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26270 ];
26271
26272 // protocols..
26273 Roo.HtmlEditorCore.pwhite= [
26274         'http',  'https',  'mailto'
26275 ];
26276
26277 // white listed style attributes.
26278 Roo.HtmlEditorCore.cwhite= [
26279       //  'text-align', /// default is to allow most things..
26280       
26281          
26282 //        'font-size'//??
26283 ];
26284
26285 // black listed style attributes.
26286 Roo.HtmlEditorCore.cblack= [
26287       //  'font-size' -- this can be set by the project 
26288 ];
26289
26290
26291 Roo.HtmlEditorCore.swapCodes   =[ 
26292     [    8211, "--" ], 
26293     [    8212, "--" ], 
26294     [    8216,  "'" ],  
26295     [    8217, "'" ],  
26296     [    8220, '"' ],  
26297     [    8221, '"' ],  
26298     [    8226, "*" ],  
26299     [    8230, "..." ]
26300 ]; 
26301
26302     //<script type="text/javascript">
26303
26304 /*
26305  * Ext JS Library 1.1.1
26306  * Copyright(c) 2006-2007, Ext JS, LLC.
26307  * Licence LGPL
26308  * 
26309  */
26310  
26311  
26312 Roo.form.HtmlEditor = function(config){
26313     
26314     
26315     
26316     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26317     
26318     if (!this.toolbars) {
26319         this.toolbars = [];
26320     }
26321     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26322     
26323     
26324 };
26325
26326 /**
26327  * @class Roo.form.HtmlEditor
26328  * @extends Roo.form.Field
26329  * Provides a lightweight HTML Editor component.
26330  *
26331  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26332  * 
26333  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26334  * supported by this editor.</b><br/><br/>
26335  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26336  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26337  */
26338 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26339     /**
26340      * @cfg {Boolean} clearUp
26341      */
26342     clearUp : true,
26343       /**
26344      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26345      */
26346     toolbars : false,
26347    
26348      /**
26349      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26350      *                        Roo.resizable.
26351      */
26352     resizable : false,
26353      /**
26354      * @cfg {Number} height (in pixels)
26355      */   
26356     height: 300,
26357    /**
26358      * @cfg {Number} width (in pixels)
26359      */   
26360     width: 500,
26361     
26362     /**
26363      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26364      * 
26365      */
26366     stylesheets: false,
26367     
26368     
26369      /**
26370      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26371      * 
26372      */
26373     cblack: false,
26374     /**
26375      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26376      * 
26377      */
26378     cwhite: false,
26379     
26380      /**
26381      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26382      * 
26383      */
26384     black: false,
26385     /**
26386      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26387      * 
26388      */
26389     white: false,
26390     
26391     // id of frame..
26392     frameId: false,
26393     
26394     // private properties
26395     validationEvent : false,
26396     deferHeight: true,
26397     initialized : false,
26398     activated : false,
26399     
26400     onFocus : Roo.emptyFn,
26401     iframePad:3,
26402     hideMode:'offsets',
26403     
26404     actionMode : 'container', // defaults to hiding it...
26405     
26406     defaultAutoCreate : { // modified by initCompnoent..
26407         tag: "textarea",
26408         style:"width:500px;height:300px;",
26409         autocomplete: "new-password"
26410     },
26411
26412     // private
26413     initComponent : function(){
26414         this.addEvents({
26415             /**
26416              * @event initialize
26417              * Fires when the editor is fully initialized (including the iframe)
26418              * @param {HtmlEditor} this
26419              */
26420             initialize: true,
26421             /**
26422              * @event activate
26423              * Fires when the editor is first receives the focus. Any insertion must wait
26424              * until after this event.
26425              * @param {HtmlEditor} this
26426              */
26427             activate: true,
26428              /**
26429              * @event beforesync
26430              * Fires before the textarea is updated with content from the editor iframe. Return false
26431              * to cancel the sync.
26432              * @param {HtmlEditor} this
26433              * @param {String} html
26434              */
26435             beforesync: true,
26436              /**
26437              * @event beforepush
26438              * Fires before the iframe editor is updated with content from the textarea. Return false
26439              * to cancel the push.
26440              * @param {HtmlEditor} this
26441              * @param {String} html
26442              */
26443             beforepush: true,
26444              /**
26445              * @event sync
26446              * Fires when the textarea is updated with content from the editor iframe.
26447              * @param {HtmlEditor} this
26448              * @param {String} html
26449              */
26450             sync: true,
26451              /**
26452              * @event push
26453              * Fires when the iframe editor is updated with content from the textarea.
26454              * @param {HtmlEditor} this
26455              * @param {String} html
26456              */
26457             push: true,
26458              /**
26459              * @event editmodechange
26460              * Fires when the editor switches edit modes
26461              * @param {HtmlEditor} this
26462              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26463              */
26464             editmodechange: true,
26465             /**
26466              * @event editorevent
26467              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26468              * @param {HtmlEditor} this
26469              */
26470             editorevent: true,
26471             /**
26472              * @event firstfocus
26473              * Fires when on first focus - needed by toolbars..
26474              * @param {HtmlEditor} this
26475              */
26476             firstfocus: true,
26477             /**
26478              * @event autosave
26479              * Auto save the htmlEditor value as a file into Events
26480              * @param {HtmlEditor} this
26481              */
26482             autosave: true,
26483             /**
26484              * @event savedpreview
26485              * preview the saved version of htmlEditor
26486              * @param {HtmlEditor} this
26487              */
26488             savedpreview: true,
26489             
26490             /**
26491             * @event stylesheetsclick
26492             * Fires when press the Sytlesheets button
26493             * @param {Roo.HtmlEditorCore} this
26494             */
26495             stylesheetsclick: true
26496         });
26497         this.defaultAutoCreate =  {
26498             tag: "textarea",
26499             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26500             autocomplete: "new-password"
26501         };
26502     },
26503
26504     /**
26505      * Protected method that will not generally be called directly. It
26506      * is called when the editor creates its toolbar. Override this method if you need to
26507      * add custom toolbar buttons.
26508      * @param {HtmlEditor} editor
26509      */
26510     createToolbar : function(editor){
26511         Roo.log("create toolbars");
26512         if (!editor.toolbars || !editor.toolbars.length) {
26513             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26514         }
26515         
26516         for (var i =0 ; i < editor.toolbars.length;i++) {
26517             editor.toolbars[i] = Roo.factory(
26518                     typeof(editor.toolbars[i]) == 'string' ?
26519                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26520                 Roo.form.HtmlEditor);
26521             editor.toolbars[i].init(editor);
26522         }
26523          
26524         
26525     },
26526
26527      
26528     // private
26529     onRender : function(ct, position)
26530     {
26531         var _t = this;
26532         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26533         
26534         this.wrap = this.el.wrap({
26535             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26536         });
26537         
26538         this.editorcore.onRender(ct, position);
26539          
26540         if (this.resizable) {
26541             this.resizeEl = new Roo.Resizable(this.wrap, {
26542                 pinned : true,
26543                 wrap: true,
26544                 dynamic : true,
26545                 minHeight : this.height,
26546                 height: this.height,
26547                 handles : this.resizable,
26548                 width: this.width,
26549                 listeners : {
26550                     resize : function(r, w, h) {
26551                         _t.onResize(w,h); // -something
26552                     }
26553                 }
26554             });
26555             
26556         }
26557         this.createToolbar(this);
26558        
26559         
26560         if(!this.width){
26561             this.setSize(this.wrap.getSize());
26562         }
26563         if (this.resizeEl) {
26564             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26565             // should trigger onReize..
26566         }
26567         
26568         this.keyNav = new Roo.KeyNav(this.el, {
26569             
26570             "tab" : function(e){
26571                 e.preventDefault();
26572                 
26573                 var value = this.getValue();
26574                 
26575                 var start = this.el.dom.selectionStart;
26576                 var end = this.el.dom.selectionEnd;
26577                 
26578                 if(!e.shiftKey){
26579                     
26580                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26581                     this.el.dom.setSelectionRange(end + 1, end + 1);
26582                     return;
26583                 }
26584                 
26585                 var f = value.substring(0, start).split("\t");
26586                 
26587                 if(f.pop().length != 0){
26588                     return;
26589                 }
26590                 
26591                 this.setValue(f.join("\t") + value.substring(end));
26592                 this.el.dom.setSelectionRange(start - 1, start - 1);
26593                 
26594             },
26595             
26596             "home" : function(e){
26597                 e.preventDefault();
26598                 
26599                 var curr = this.el.dom.selectionStart;
26600                 var lines = this.getValue().split("\n");
26601                 
26602                 if(!lines.length){
26603                     return;
26604                 }
26605                 
26606                 if(e.ctrlKey){
26607                     this.el.dom.setSelectionRange(0, 0);
26608                     return;
26609                 }
26610                 
26611                 var pos = 0;
26612                 
26613                 for (var i = 0; i < lines.length;i++) {
26614                     pos += lines[i].length;
26615                     
26616                     if(i != 0){
26617                         pos += 1;
26618                     }
26619                     
26620                     if(pos < curr){
26621                         continue;
26622                     }
26623                     
26624                     pos -= lines[i].length;
26625                     
26626                     break;
26627                 }
26628                 
26629                 if(!e.shiftKey){
26630                     this.el.dom.setSelectionRange(pos, pos);
26631                     return;
26632                 }
26633                 
26634                 this.el.dom.selectionStart = pos;
26635                 this.el.dom.selectionEnd = curr;
26636             },
26637             
26638             "end" : function(e){
26639                 e.preventDefault();
26640                 
26641                 var curr = this.el.dom.selectionStart;
26642                 var lines = this.getValue().split("\n");
26643                 
26644                 if(!lines.length){
26645                     return;
26646                 }
26647                 
26648                 if(e.ctrlKey){
26649                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26650                     return;
26651                 }
26652                 
26653                 var pos = 0;
26654                 
26655                 for (var i = 0; i < lines.length;i++) {
26656                     
26657                     pos += lines[i].length;
26658                     
26659                     if(i != 0){
26660                         pos += 1;
26661                     }
26662                     
26663                     if(pos < curr){
26664                         continue;
26665                     }
26666                     
26667                     break;
26668                 }
26669                 
26670                 if(!e.shiftKey){
26671                     this.el.dom.setSelectionRange(pos, pos);
26672                     return;
26673                 }
26674                 
26675                 this.el.dom.selectionStart = curr;
26676                 this.el.dom.selectionEnd = pos;
26677             },
26678
26679             scope : this,
26680
26681             doRelay : function(foo, bar, hname){
26682                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26683             },
26684
26685             forceKeyDown: true
26686         });
26687         
26688 //        if(this.autosave && this.w){
26689 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26690 //        }
26691     },
26692
26693     // private
26694     onResize : function(w, h)
26695     {
26696         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26697         var ew = false;
26698         var eh = false;
26699         
26700         if(this.el ){
26701             if(typeof w == 'number'){
26702                 var aw = w - this.wrap.getFrameWidth('lr');
26703                 this.el.setWidth(this.adjustWidth('textarea', aw));
26704                 ew = aw;
26705             }
26706             if(typeof h == 'number'){
26707                 var tbh = 0;
26708                 for (var i =0; i < this.toolbars.length;i++) {
26709                     // fixme - ask toolbars for heights?
26710                     tbh += this.toolbars[i].tb.el.getHeight();
26711                     if (this.toolbars[i].footer) {
26712                         tbh += this.toolbars[i].footer.el.getHeight();
26713                     }
26714                 }
26715                 
26716                 
26717                 
26718                 
26719                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26720                 ah -= 5; // knock a few pixes off for look..
26721 //                Roo.log(ah);
26722                 this.el.setHeight(this.adjustWidth('textarea', ah));
26723                 var eh = ah;
26724             }
26725         }
26726         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26727         this.editorcore.onResize(ew,eh);
26728         
26729     },
26730
26731     /**
26732      * Toggles the editor between standard and source edit mode.
26733      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26734      */
26735     toggleSourceEdit : function(sourceEditMode)
26736     {
26737         this.editorcore.toggleSourceEdit(sourceEditMode);
26738         
26739         if(this.editorcore.sourceEditMode){
26740             Roo.log('editor - showing textarea');
26741             
26742 //            Roo.log('in');
26743 //            Roo.log(this.syncValue());
26744             this.editorcore.syncValue();
26745             this.el.removeClass('x-hidden');
26746             this.el.dom.removeAttribute('tabIndex');
26747             this.el.focus();
26748             
26749             for (var i = 0; i < this.toolbars.length; i++) {
26750                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26751                     this.toolbars[i].tb.hide();
26752                     this.toolbars[i].footer.hide();
26753                 }
26754             }
26755             
26756         }else{
26757             Roo.log('editor - hiding textarea');
26758 //            Roo.log('out')
26759 //            Roo.log(this.pushValue()); 
26760             this.editorcore.pushValue();
26761             
26762             this.el.addClass('x-hidden');
26763             this.el.dom.setAttribute('tabIndex', -1);
26764             
26765             for (var i = 0; i < this.toolbars.length; i++) {
26766                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26767                     this.toolbars[i].tb.show();
26768                     this.toolbars[i].footer.show();
26769                 }
26770             }
26771             
26772             //this.deferFocus();
26773         }
26774         
26775         this.setSize(this.wrap.getSize());
26776         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
26777         
26778         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26779     },
26780  
26781     // private (for BoxComponent)
26782     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26783
26784     // private (for BoxComponent)
26785     getResizeEl : function(){
26786         return this.wrap;
26787     },
26788
26789     // private (for BoxComponent)
26790     getPositionEl : function(){
26791         return this.wrap;
26792     },
26793
26794     // private
26795     initEvents : function(){
26796         this.originalValue = this.getValue();
26797     },
26798
26799     /**
26800      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26801      * @method
26802      */
26803     markInvalid : Roo.emptyFn,
26804     /**
26805      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26806      * @method
26807      */
26808     clearInvalid : Roo.emptyFn,
26809
26810     setValue : function(v){
26811         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26812         this.editorcore.pushValue();
26813     },
26814
26815      
26816     // private
26817     deferFocus : function(){
26818         this.focus.defer(10, this);
26819     },
26820
26821     // doc'ed in Field
26822     focus : function(){
26823         this.editorcore.focus();
26824         
26825     },
26826       
26827
26828     // private
26829     onDestroy : function(){
26830         
26831         
26832         
26833         if(this.rendered){
26834             
26835             for (var i =0; i < this.toolbars.length;i++) {
26836                 // fixme - ask toolbars for heights?
26837                 this.toolbars[i].onDestroy();
26838             }
26839             
26840             this.wrap.dom.innerHTML = '';
26841             this.wrap.remove();
26842         }
26843     },
26844
26845     // private
26846     onFirstFocus : function(){
26847         //Roo.log("onFirstFocus");
26848         this.editorcore.onFirstFocus();
26849          for (var i =0; i < this.toolbars.length;i++) {
26850             this.toolbars[i].onFirstFocus();
26851         }
26852         
26853     },
26854     
26855     // private
26856     syncValue : function()
26857     {
26858         this.editorcore.syncValue();
26859     },
26860     
26861     pushValue : function()
26862     {
26863         this.editorcore.pushValue();
26864     },
26865     
26866     setStylesheets : function(stylesheets)
26867     {
26868         this.editorcore.setStylesheets(stylesheets);
26869     },
26870     
26871     removeStylesheets : function()
26872     {
26873         this.editorcore.removeStylesheets();
26874     }
26875      
26876     
26877     // hide stuff that is not compatible
26878     /**
26879      * @event blur
26880      * @hide
26881      */
26882     /**
26883      * @event change
26884      * @hide
26885      */
26886     /**
26887      * @event focus
26888      * @hide
26889      */
26890     /**
26891      * @event specialkey
26892      * @hide
26893      */
26894     /**
26895      * @cfg {String} fieldClass @hide
26896      */
26897     /**
26898      * @cfg {String} focusClass @hide
26899      */
26900     /**
26901      * @cfg {String} autoCreate @hide
26902      */
26903     /**
26904      * @cfg {String} inputType @hide
26905      */
26906     /**
26907      * @cfg {String} invalidClass @hide
26908      */
26909     /**
26910      * @cfg {String} invalidText @hide
26911      */
26912     /**
26913      * @cfg {String} msgFx @hide
26914      */
26915     /**
26916      * @cfg {String} validateOnBlur @hide
26917      */
26918 });
26919  
26920     // <script type="text/javascript">
26921 /*
26922  * Based on
26923  * Ext JS Library 1.1.1
26924  * Copyright(c) 2006-2007, Ext JS, LLC.
26925  *  
26926  
26927  */
26928
26929 /**
26930  * @class Roo.form.HtmlEditorToolbar1
26931  * Basic Toolbar
26932  * 
26933  * Usage:
26934  *
26935  new Roo.form.HtmlEditor({
26936     ....
26937     toolbars : [
26938         new Roo.form.HtmlEditorToolbar1({
26939             disable : { fonts: 1 , format: 1, ..., ... , ...],
26940             btns : [ .... ]
26941         })
26942     }
26943      
26944  * 
26945  * @cfg {Object} disable List of elements to disable..
26946  * @cfg {Array} btns List of additional buttons.
26947  * 
26948  * 
26949  * NEEDS Extra CSS? 
26950  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26951  */
26952  
26953 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26954 {
26955     
26956     Roo.apply(this, config);
26957     
26958     // default disabled, based on 'good practice'..
26959     this.disable = this.disable || {};
26960     Roo.applyIf(this.disable, {
26961         fontSize : true,
26962         colors : true,
26963         specialElements : true
26964     });
26965     
26966     
26967     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26968     // dont call parent... till later.
26969 }
26970
26971 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26972     
26973     tb: false,
26974     
26975     rendered: false,
26976     
26977     editor : false,
26978     editorcore : false,
26979     /**
26980      * @cfg {Object} disable  List of toolbar elements to disable
26981          
26982      */
26983     disable : false,
26984     
26985     
26986      /**
26987      * @cfg {String} createLinkText The default text for the create link prompt
26988      */
26989     createLinkText : 'Please enter the URL for the link:',
26990     /**
26991      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
26992      */
26993     defaultLinkValue : 'http:/'+'/',
26994    
26995     
26996       /**
26997      * @cfg {Array} fontFamilies An array of available font families
26998      */
26999     fontFamilies : [
27000         'Arial',
27001         'Courier New',
27002         'Tahoma',
27003         'Times New Roman',
27004         'Verdana'
27005     ],
27006     
27007     specialChars : [
27008            "&#169;",
27009           "&#174;",     
27010           "&#8482;",    
27011           "&#163;" ,    
27012          // "&#8212;",    
27013           "&#8230;",    
27014           "&#247;" ,    
27015         //  "&#225;" ,     ?? a acute?
27016            "&#8364;"    , //Euro
27017        //   "&#8220;"    ,
27018         //  "&#8221;"    ,
27019         //  "&#8226;"    ,
27020           "&#176;"  //   , // degrees
27021
27022          // "&#233;"     , // e ecute
27023          // "&#250;"     , // u ecute?
27024     ],
27025     
27026     specialElements : [
27027         {
27028             text: "Insert Table",
27029             xtype: 'MenuItem',
27030             xns : Roo.Menu,
27031             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27032                 
27033         },
27034         {    
27035             text: "Insert Image",
27036             xtype: 'MenuItem',
27037             xns : Roo.Menu,
27038             ihtml : '<img src="about:blank"/>'
27039             
27040         }
27041         
27042          
27043     ],
27044     
27045     
27046     inputElements : [ 
27047             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27048             "input:submit", "input:button", "select", "textarea", "label" ],
27049     formats : [
27050         ["p"] ,  
27051         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27052         ["pre"],[ "code"], 
27053         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27054         ['div'],['span']
27055     ],
27056     
27057     cleanStyles : [
27058         "font-size"
27059     ],
27060      /**
27061      * @cfg {String} defaultFont default font to use.
27062      */
27063     defaultFont: 'tahoma',
27064    
27065     fontSelect : false,
27066     
27067     
27068     formatCombo : false,
27069     
27070     init : function(editor)
27071     {
27072         this.editor = editor;
27073         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27074         var editorcore = this.editorcore;
27075         
27076         var _t = this;
27077         
27078         var fid = editorcore.frameId;
27079         var etb = this;
27080         function btn(id, toggle, handler){
27081             var xid = fid + '-'+ id ;
27082             return {
27083                 id : xid,
27084                 cmd : id,
27085                 cls : 'x-btn-icon x-edit-'+id,
27086                 enableToggle:toggle !== false,
27087                 scope: _t, // was editor...
27088                 handler:handler||_t.relayBtnCmd,
27089                 clickEvent:'mousedown',
27090                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27091                 tabIndex:-1
27092             };
27093         }
27094         
27095         
27096         
27097         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27098         this.tb = tb;
27099          // stop form submits
27100         tb.el.on('click', function(e){
27101             e.preventDefault(); // what does this do?
27102         });
27103
27104         if(!this.disable.font) { // && !Roo.isSafari){
27105             /* why no safari for fonts 
27106             editor.fontSelect = tb.el.createChild({
27107                 tag:'select',
27108                 tabIndex: -1,
27109                 cls:'x-font-select',
27110                 html: this.createFontOptions()
27111             });
27112             
27113             editor.fontSelect.on('change', function(){
27114                 var font = editor.fontSelect.dom.value;
27115                 editor.relayCmd('fontname', font);
27116                 editor.deferFocus();
27117             }, editor);
27118             
27119             tb.add(
27120                 editor.fontSelect.dom,
27121                 '-'
27122             );
27123             */
27124             
27125         };
27126         if(!this.disable.formats){
27127             this.formatCombo = new Roo.form.ComboBox({
27128                 store: new Roo.data.SimpleStore({
27129                     id : 'tag',
27130                     fields: ['tag'],
27131                     data : this.formats // from states.js
27132                 }),
27133                 blockFocus : true,
27134                 name : '',
27135                 //autoCreate : {tag: "div",  size: "20"},
27136                 displayField:'tag',
27137                 typeAhead: false,
27138                 mode: 'local',
27139                 editable : false,
27140                 triggerAction: 'all',
27141                 emptyText:'Add tag',
27142                 selectOnFocus:true,
27143                 width:135,
27144                 listeners : {
27145                     'select': function(c, r, i) {
27146                         editorcore.insertTag(r.get('tag'));
27147                         editor.focus();
27148                     }
27149                 }
27150
27151             });
27152             tb.addField(this.formatCombo);
27153             
27154         }
27155         
27156         if(!this.disable.format){
27157             tb.add(
27158                 btn('bold'),
27159                 btn('italic'),
27160                 btn('underline')
27161             );
27162         };
27163         if(!this.disable.fontSize){
27164             tb.add(
27165                 '-',
27166                 
27167                 
27168                 btn('increasefontsize', false, editorcore.adjustFont),
27169                 btn('decreasefontsize', false, editorcore.adjustFont)
27170             );
27171         };
27172         
27173         
27174         if(!this.disable.colors){
27175             tb.add(
27176                 '-', {
27177                     id:editorcore.frameId +'-forecolor',
27178                     cls:'x-btn-icon x-edit-forecolor',
27179                     clickEvent:'mousedown',
27180                     tooltip: this.buttonTips['forecolor'] || undefined,
27181                     tabIndex:-1,
27182                     menu : new Roo.menu.ColorMenu({
27183                         allowReselect: true,
27184                         focus: Roo.emptyFn,
27185                         value:'000000',
27186                         plain:true,
27187                         selectHandler: function(cp, color){
27188                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27189                             editor.deferFocus();
27190                         },
27191                         scope: editorcore,
27192                         clickEvent:'mousedown'
27193                     })
27194                 }, {
27195                     id:editorcore.frameId +'backcolor',
27196                     cls:'x-btn-icon x-edit-backcolor',
27197                     clickEvent:'mousedown',
27198                     tooltip: this.buttonTips['backcolor'] || undefined,
27199                     tabIndex:-1,
27200                     menu : new Roo.menu.ColorMenu({
27201                         focus: Roo.emptyFn,
27202                         value:'FFFFFF',
27203                         plain:true,
27204                         allowReselect: true,
27205                         selectHandler: function(cp, color){
27206                             if(Roo.isGecko){
27207                                 editorcore.execCmd('useCSS', false);
27208                                 editorcore.execCmd('hilitecolor', color);
27209                                 editorcore.execCmd('useCSS', true);
27210                                 editor.deferFocus();
27211                             }else{
27212                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27213                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27214                                 editor.deferFocus();
27215                             }
27216                         },
27217                         scope:editorcore,
27218                         clickEvent:'mousedown'
27219                     })
27220                 }
27221             );
27222         };
27223         // now add all the items...
27224         
27225
27226         if(!this.disable.alignments){
27227             tb.add(
27228                 '-',
27229                 btn('justifyleft'),
27230                 btn('justifycenter'),
27231                 btn('justifyright')
27232             );
27233         };
27234
27235         //if(!Roo.isSafari){
27236             if(!this.disable.links){
27237                 tb.add(
27238                     '-',
27239                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27240                 );
27241             };
27242
27243             if(!this.disable.lists){
27244                 tb.add(
27245                     '-',
27246                     btn('insertorderedlist'),
27247                     btn('insertunorderedlist')
27248                 );
27249             }
27250             if(!this.disable.sourceEdit){
27251                 tb.add(
27252                     '-',
27253                     btn('sourceedit', true, function(btn){
27254                         this.toggleSourceEdit(btn.pressed);
27255                     })
27256                 );
27257             }
27258         //}
27259         
27260         var smenu = { };
27261         // special menu.. - needs to be tidied up..
27262         if (!this.disable.special) {
27263             smenu = {
27264                 text: "&#169;",
27265                 cls: 'x-edit-none',
27266                 
27267                 menu : {
27268                     items : []
27269                 }
27270             };
27271             for (var i =0; i < this.specialChars.length; i++) {
27272                 smenu.menu.items.push({
27273                     
27274                     html: this.specialChars[i],
27275                     handler: function(a,b) {
27276                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27277                         //editor.insertAtCursor(a.html);
27278                         
27279                     },
27280                     tabIndex:-1
27281                 });
27282             }
27283             
27284             
27285             tb.add(smenu);
27286             
27287             
27288         }
27289         
27290         var cmenu = { };
27291         if (!this.disable.cleanStyles) {
27292             cmenu = {
27293                 cls: 'x-btn-icon x-btn-clear',
27294                 
27295                 menu : {
27296                     items : []
27297                 }
27298             };
27299             for (var i =0; i < this.cleanStyles.length; i++) {
27300                 cmenu.menu.items.push({
27301                     actiontype : this.cleanStyles[i],
27302                     html: 'Remove ' + this.cleanStyles[i],
27303                     handler: function(a,b) {
27304 //                        Roo.log(a);
27305 //                        Roo.log(b);
27306                         var c = Roo.get(editorcore.doc.body);
27307                         c.select('[style]').each(function(s) {
27308                             s.dom.style.removeProperty(a.actiontype);
27309                         });
27310                         editorcore.syncValue();
27311                     },
27312                     tabIndex:-1
27313                 });
27314             }
27315             cmenu.menu.items.push({
27316                 actiontype : 'word',
27317                 html: 'Remove MS Word Formating',
27318                 handler: function(a,b) {
27319                     editorcore.cleanWord();
27320                     editorcore.syncValue();
27321                 },
27322                 tabIndex:-1
27323             });
27324             
27325             cmenu.menu.items.push({
27326                 actiontype : 'all',
27327                 html: 'Remove All Styles',
27328                 handler: function(a,b) {
27329                     
27330                     var c = Roo.get(editorcore.doc.body);
27331                     c.select('[style]').each(function(s) {
27332                         s.dom.removeAttribute('style');
27333                     });
27334                     editorcore.syncValue();
27335                 },
27336                 tabIndex:-1
27337             });
27338              cmenu.menu.items.push({
27339                 actiontype : 'word',
27340                 html: 'Tidy HTML Source',
27341                 handler: function(a,b) {
27342                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27343                     editorcore.syncValue();
27344                 },
27345                 tabIndex:-1
27346             });
27347             
27348             
27349             tb.add(cmenu);
27350         }
27351          
27352         if (!this.disable.specialElements) {
27353             var semenu = {
27354                 text: "Other;",
27355                 cls: 'x-edit-none',
27356                 menu : {
27357                     items : []
27358                 }
27359             };
27360             for (var i =0; i < this.specialElements.length; i++) {
27361                 semenu.menu.items.push(
27362                     Roo.apply({ 
27363                         handler: function(a,b) {
27364                             editor.insertAtCursor(this.ihtml);
27365                         }
27366                     }, this.specialElements[i])
27367                 );
27368                     
27369             }
27370             
27371             tb.add(semenu);
27372             
27373             
27374         }
27375          
27376         
27377         if (this.btns) {
27378             for(var i =0; i< this.btns.length;i++) {
27379                 var b = Roo.factory(this.btns[i],Roo.form);
27380                 b.cls =  'x-edit-none';
27381                 
27382                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27383                     b.cls += ' x-init-enable';
27384                 }
27385                 
27386                 b.scope = editorcore;
27387                 tb.add(b);
27388             }
27389         
27390         }
27391         
27392         
27393         
27394         // disable everything...
27395         
27396         this.tb.items.each(function(item){
27397             
27398            if(
27399                 item.id != editorcore.frameId+ '-sourceedit' && 
27400                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27401             ){
27402                 
27403                 item.disable();
27404             }
27405         });
27406         this.rendered = true;
27407         
27408         // the all the btns;
27409         editor.on('editorevent', this.updateToolbar, this);
27410         // other toolbars need to implement this..
27411         //editor.on('editmodechange', this.updateToolbar, this);
27412     },
27413     
27414     
27415     relayBtnCmd : function(btn) {
27416         this.editorcore.relayCmd(btn.cmd);
27417     },
27418     // private used internally
27419     createLink : function(){
27420         Roo.log("create link?");
27421         var url = prompt(this.createLinkText, this.defaultLinkValue);
27422         if(url && url != 'http:/'+'/'){
27423             this.editorcore.relayCmd('createlink', url);
27424         }
27425     },
27426
27427     
27428     /**
27429      * Protected method that will not generally be called directly. It triggers
27430      * a toolbar update by reading the markup state of the current selection in the editor.
27431      */
27432     updateToolbar: function(){
27433
27434         if(!this.editorcore.activated){
27435             this.editor.onFirstFocus();
27436             return;
27437         }
27438
27439         var btns = this.tb.items.map, 
27440             doc = this.editorcore.doc,
27441             frameId = this.editorcore.frameId;
27442
27443         if(!this.disable.font && !Roo.isSafari){
27444             /*
27445             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27446             if(name != this.fontSelect.dom.value){
27447                 this.fontSelect.dom.value = name;
27448             }
27449             */
27450         }
27451         if(!this.disable.format){
27452             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27453             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27454             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27455         }
27456         if(!this.disable.alignments){
27457             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27458             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27459             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27460         }
27461         if(!Roo.isSafari && !this.disable.lists){
27462             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27463             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27464         }
27465         
27466         var ans = this.editorcore.getAllAncestors();
27467         if (this.formatCombo) {
27468             
27469             
27470             var store = this.formatCombo.store;
27471             this.formatCombo.setValue("");
27472             for (var i =0; i < ans.length;i++) {
27473                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27474                     // select it..
27475                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27476                     break;
27477                 }
27478             }
27479         }
27480         
27481         
27482         
27483         // hides menus... - so this cant be on a menu...
27484         Roo.menu.MenuMgr.hideAll();
27485
27486         //this.editorsyncValue();
27487     },
27488    
27489     
27490     createFontOptions : function(){
27491         var buf = [], fs = this.fontFamilies, ff, lc;
27492         
27493         
27494         
27495         for(var i = 0, len = fs.length; i< len; i++){
27496             ff = fs[i];
27497             lc = ff.toLowerCase();
27498             buf.push(
27499                 '<option value="',lc,'" style="font-family:',ff,';"',
27500                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27501                     ff,
27502                 '</option>'
27503             );
27504         }
27505         return buf.join('');
27506     },
27507     
27508     toggleSourceEdit : function(sourceEditMode){
27509         
27510         Roo.log("toolbar toogle");
27511         if(sourceEditMode === undefined){
27512             sourceEditMode = !this.sourceEditMode;
27513         }
27514         this.sourceEditMode = sourceEditMode === true;
27515         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27516         // just toggle the button?
27517         if(btn.pressed !== this.sourceEditMode){
27518             btn.toggle(this.sourceEditMode);
27519             return;
27520         }
27521         
27522         if(sourceEditMode){
27523             Roo.log("disabling buttons");
27524             this.tb.items.each(function(item){
27525                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27526                     item.disable();
27527                 }
27528             });
27529           
27530         }else{
27531             Roo.log("enabling buttons");
27532             if(this.editorcore.initialized){
27533                 this.tb.items.each(function(item){
27534                     item.enable();
27535                 });
27536             }
27537             
27538         }
27539         Roo.log("calling toggole on editor");
27540         // tell the editor that it's been pressed..
27541         this.editor.toggleSourceEdit(sourceEditMode);
27542        
27543     },
27544      /**
27545      * Object collection of toolbar tooltips for the buttons in the editor. The key
27546      * is the command id associated with that button and the value is a valid QuickTips object.
27547      * For example:
27548 <pre><code>
27549 {
27550     bold : {
27551         title: 'Bold (Ctrl+B)',
27552         text: 'Make the selected text bold.',
27553         cls: 'x-html-editor-tip'
27554     },
27555     italic : {
27556         title: 'Italic (Ctrl+I)',
27557         text: 'Make the selected text italic.',
27558         cls: 'x-html-editor-tip'
27559     },
27560     ...
27561 </code></pre>
27562     * @type Object
27563      */
27564     buttonTips : {
27565         bold : {
27566             title: 'Bold (Ctrl+B)',
27567             text: 'Make the selected text bold.',
27568             cls: 'x-html-editor-tip'
27569         },
27570         italic : {
27571             title: 'Italic (Ctrl+I)',
27572             text: 'Make the selected text italic.',
27573             cls: 'x-html-editor-tip'
27574         },
27575         underline : {
27576             title: 'Underline (Ctrl+U)',
27577             text: 'Underline the selected text.',
27578             cls: 'x-html-editor-tip'
27579         },
27580         increasefontsize : {
27581             title: 'Grow Text',
27582             text: 'Increase the font size.',
27583             cls: 'x-html-editor-tip'
27584         },
27585         decreasefontsize : {
27586             title: 'Shrink Text',
27587             text: 'Decrease the font size.',
27588             cls: 'x-html-editor-tip'
27589         },
27590         backcolor : {
27591             title: 'Text Highlight Color',
27592             text: 'Change the background color of the selected text.',
27593             cls: 'x-html-editor-tip'
27594         },
27595         forecolor : {
27596             title: 'Font Color',
27597             text: 'Change the color of the selected text.',
27598             cls: 'x-html-editor-tip'
27599         },
27600         justifyleft : {
27601             title: 'Align Text Left',
27602             text: 'Align text to the left.',
27603             cls: 'x-html-editor-tip'
27604         },
27605         justifycenter : {
27606             title: 'Center Text',
27607             text: 'Center text in the editor.',
27608             cls: 'x-html-editor-tip'
27609         },
27610         justifyright : {
27611             title: 'Align Text Right',
27612             text: 'Align text to the right.',
27613             cls: 'x-html-editor-tip'
27614         },
27615         insertunorderedlist : {
27616             title: 'Bullet List',
27617             text: 'Start a bulleted list.',
27618             cls: 'x-html-editor-tip'
27619         },
27620         insertorderedlist : {
27621             title: 'Numbered List',
27622             text: 'Start a numbered list.',
27623             cls: 'x-html-editor-tip'
27624         },
27625         createlink : {
27626             title: 'Hyperlink',
27627             text: 'Make the selected text a hyperlink.',
27628             cls: 'x-html-editor-tip'
27629         },
27630         sourceedit : {
27631             title: 'Source Edit',
27632             text: 'Switch to source editing mode.',
27633             cls: 'x-html-editor-tip'
27634         }
27635     },
27636     // private
27637     onDestroy : function(){
27638         if(this.rendered){
27639             
27640             this.tb.items.each(function(item){
27641                 if(item.menu){
27642                     item.menu.removeAll();
27643                     if(item.menu.el){
27644                         item.menu.el.destroy();
27645                     }
27646                 }
27647                 item.destroy();
27648             });
27649              
27650         }
27651     },
27652     onFirstFocus: function() {
27653         this.tb.items.each(function(item){
27654            item.enable();
27655         });
27656     }
27657 });
27658
27659
27660
27661
27662 // <script type="text/javascript">
27663 /*
27664  * Based on
27665  * Ext JS Library 1.1.1
27666  * Copyright(c) 2006-2007, Ext JS, LLC.
27667  *  
27668  
27669  */
27670
27671  
27672 /**
27673  * @class Roo.form.HtmlEditor.ToolbarContext
27674  * Context Toolbar
27675  * 
27676  * Usage:
27677  *
27678  new Roo.form.HtmlEditor({
27679     ....
27680     toolbars : [
27681         { xtype: 'ToolbarStandard', styles : {} }
27682         { xtype: 'ToolbarContext', disable : {} }
27683     ]
27684 })
27685
27686      
27687  * 
27688  * @config : {Object} disable List of elements to disable.. (not done yet.)
27689  * @config : {Object} styles  Map of styles available.
27690  * 
27691  */
27692
27693 Roo.form.HtmlEditor.ToolbarContext = function(config)
27694 {
27695     
27696     Roo.apply(this, config);
27697     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27698     // dont call parent... till later.
27699     this.styles = this.styles || {};
27700 }
27701
27702  
27703
27704 Roo.form.HtmlEditor.ToolbarContext.types = {
27705     'IMG' : {
27706         width : {
27707             title: "Width",
27708             width: 40
27709         },
27710         height:  {
27711             title: "Height",
27712             width: 40
27713         },
27714         align: {
27715             title: "Align",
27716             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27717             width : 80
27718             
27719         },
27720         border: {
27721             title: "Border",
27722             width: 40
27723         },
27724         alt: {
27725             title: "Alt",
27726             width: 120
27727         },
27728         src : {
27729             title: "Src",
27730             width: 220
27731         }
27732         
27733     },
27734     'A' : {
27735         name : {
27736             title: "Name",
27737             width: 50
27738         },
27739         target:  {
27740             title: "Target",
27741             width: 120
27742         },
27743         href:  {
27744             title: "Href",
27745             width: 220
27746         } // border?
27747         
27748     },
27749     'TABLE' : {
27750         rows : {
27751             title: "Rows",
27752             width: 20
27753         },
27754         cols : {
27755             title: "Cols",
27756             width: 20
27757         },
27758         width : {
27759             title: "Width",
27760             width: 40
27761         },
27762         height : {
27763             title: "Height",
27764             width: 40
27765         },
27766         border : {
27767             title: "Border",
27768             width: 20
27769         }
27770     },
27771     'TD' : {
27772         width : {
27773             title: "Width",
27774             width: 40
27775         },
27776         height : {
27777             title: "Height",
27778             width: 40
27779         },   
27780         align: {
27781             title: "Align",
27782             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27783             width: 80
27784         },
27785         valign: {
27786             title: "Valign",
27787             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27788             width: 80
27789         },
27790         colspan: {
27791             title: "Colspan",
27792             width: 20
27793             
27794         },
27795          'font-family'  : {
27796             title : "Font",
27797             style : 'fontFamily',
27798             displayField: 'display',
27799             optname : 'font-family',
27800             width: 140
27801         }
27802     },
27803     'INPUT' : {
27804         name : {
27805             title: "name",
27806             width: 120
27807         },
27808         value : {
27809             title: "Value",
27810             width: 120
27811         },
27812         width : {
27813             title: "Width",
27814             width: 40
27815         }
27816     },
27817     'LABEL' : {
27818         'for' : {
27819             title: "For",
27820             width: 120
27821         }
27822     },
27823     'TEXTAREA' : {
27824           name : {
27825             title: "name",
27826             width: 120
27827         },
27828         rows : {
27829             title: "Rows",
27830             width: 20
27831         },
27832         cols : {
27833             title: "Cols",
27834             width: 20
27835         }
27836     },
27837     'SELECT' : {
27838         name : {
27839             title: "name",
27840             width: 120
27841         },
27842         selectoptions : {
27843             title: "Options",
27844             width: 200
27845         }
27846     },
27847     
27848     // should we really allow this??
27849     // should this just be 
27850     'BODY' : {
27851         title : {
27852             title: "Title",
27853             width: 200,
27854             disabled : true
27855         }
27856     },
27857     'SPAN' : {
27858         'font-family'  : {
27859             title : "Font",
27860             style : 'fontFamily',
27861             displayField: 'display',
27862             optname : 'font-family',
27863             width: 140
27864         }
27865     },
27866     'DIV' : {
27867         'font-family'  : {
27868             title : "Font",
27869             style : 'fontFamily',
27870             displayField: 'display',
27871             optname : 'font-family',
27872             width: 140
27873         }
27874     },
27875      'P' : {
27876         'font-family'  : {
27877             title : "Font",
27878             style : 'fontFamily',
27879             displayField: 'display',
27880             optname : 'font-family',
27881             width: 140
27882         }
27883     },
27884     
27885     '*' : {
27886         // empty..
27887     }
27888
27889 };
27890
27891 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27892 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27893
27894 Roo.form.HtmlEditor.ToolbarContext.options = {
27895         'font-family'  : [ 
27896                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27897                 [ 'Courier New', 'Courier New'],
27898                 [ 'Tahoma', 'Tahoma'],
27899                 [ 'Times New Roman,serif', 'Times'],
27900                 [ 'Verdana','Verdana' ]
27901         ]
27902 };
27903
27904 // fixme - these need to be configurable..
27905  
27906
27907 Roo.form.HtmlEditor.ToolbarContext.types
27908
27909
27910 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27911     
27912     tb: false,
27913     
27914     rendered: false,
27915     
27916     editor : false,
27917     editorcore : false,
27918     /**
27919      * @cfg {Object} disable  List of toolbar elements to disable
27920          
27921      */
27922     disable : false,
27923     /**
27924      * @cfg {Object} styles List of styles 
27925      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27926      *
27927      * These must be defined in the page, so they get rendered correctly..
27928      * .headline { }
27929      * TD.underline { }
27930      * 
27931      */
27932     styles : false,
27933     
27934     options: false,
27935     
27936     toolbars : false,
27937     
27938     init : function(editor)
27939     {
27940         this.editor = editor;
27941         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27942         var editorcore = this.editorcore;
27943         
27944         var fid = editorcore.frameId;
27945         var etb = this;
27946         function btn(id, toggle, handler){
27947             var xid = fid + '-'+ id ;
27948             return {
27949                 id : xid,
27950                 cmd : id,
27951                 cls : 'x-btn-icon x-edit-'+id,
27952                 enableToggle:toggle !== false,
27953                 scope: editorcore, // was editor...
27954                 handler:handler||editorcore.relayBtnCmd,
27955                 clickEvent:'mousedown',
27956                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27957                 tabIndex:-1
27958             };
27959         }
27960         // create a new element.
27961         var wdiv = editor.wrap.createChild({
27962                 tag: 'div'
27963             }, editor.wrap.dom.firstChild.nextSibling, true);
27964         
27965         // can we do this more than once??
27966         
27967          // stop form submits
27968       
27969  
27970         // disable everything...
27971         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27972         this.toolbars = {};
27973            
27974         for (var i in  ty) {
27975           
27976             this.toolbars[i] = this.buildToolbar(ty[i],i);
27977         }
27978         this.tb = this.toolbars.BODY;
27979         this.tb.el.show();
27980         this.buildFooter();
27981         this.footer.show();
27982         editor.on('hide', function( ) { this.footer.hide() }, this);
27983         editor.on('show', function( ) { this.footer.show() }, this);
27984         
27985          
27986         this.rendered = true;
27987         
27988         // the all the btns;
27989         editor.on('editorevent', this.updateToolbar, this);
27990         // other toolbars need to implement this..
27991         //editor.on('editmodechange', this.updateToolbar, this);
27992     },
27993     
27994     
27995     
27996     /**
27997      * Protected method that will not generally be called directly. It triggers
27998      * a toolbar update by reading the markup state of the current selection in the editor.
27999      */
28000     updateToolbar: function(editor,ev,sel){
28001
28002         //Roo.log(ev);
28003         // capture mouse up - this is handy for selecting images..
28004         // perhaps should go somewhere else...
28005         if(!this.editorcore.activated){
28006              this.editor.onFirstFocus();
28007             return;
28008         }
28009         
28010         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28011         // selectNode - might want to handle IE?
28012         if (ev &&
28013             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28014             ev.target && ev.target.tagName == 'IMG') {
28015             // they have click on an image...
28016             // let's see if we can change the selection...
28017             sel = ev.target;
28018          
28019               var nodeRange = sel.ownerDocument.createRange();
28020             try {
28021                 nodeRange.selectNode(sel);
28022             } catch (e) {
28023                 nodeRange.selectNodeContents(sel);
28024             }
28025             //nodeRange.collapse(true);
28026             var s = this.editorcore.win.getSelection();
28027             s.removeAllRanges();
28028             s.addRange(nodeRange);
28029         }  
28030         
28031       
28032         var updateFooter = sel ? false : true;
28033         
28034         
28035         var ans = this.editorcore.getAllAncestors();
28036         
28037         // pick
28038         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28039         
28040         if (!sel) { 
28041             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28042             sel = sel ? sel : this.editorcore.doc.body;
28043             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28044             
28045         }
28046         // pick a menu that exists..
28047         var tn = sel.tagName.toUpperCase();
28048         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28049         
28050         tn = sel.tagName.toUpperCase();
28051         
28052         var lastSel = this.tb.selectedNode
28053         
28054         this.tb.selectedNode = sel;
28055         
28056         // if current menu does not match..
28057         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
28058                 
28059             this.tb.el.hide();
28060             ///console.log("show: " + tn);
28061             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28062             this.tb.el.show();
28063             // update name
28064             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28065             
28066             
28067             // update attributes
28068             if (this.tb.fields) {
28069                 this.tb.fields.each(function(e) {
28070                     if (e.stylename) {
28071                         e.setValue(sel.style[e.stylename]);
28072                         return;
28073                     } 
28074                    e.setValue(sel.getAttribute(e.attrname));
28075                 });
28076             }
28077             
28078             var hasStyles = false;
28079             for(var i in this.styles) {
28080                 hasStyles = true;
28081                 break;
28082             }
28083             
28084             // update styles
28085             if (hasStyles) { 
28086                 var st = this.tb.fields.item(0);
28087                 
28088                 st.store.removeAll();
28089                
28090                 
28091                 var cn = sel.className.split(/\s+/);
28092                 
28093                 var avs = [];
28094                 if (this.styles['*']) {
28095                     
28096                     Roo.each(this.styles['*'], function(v) {
28097                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28098                     });
28099                 }
28100                 if (this.styles[tn]) { 
28101                     Roo.each(this.styles[tn], function(v) {
28102                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28103                     });
28104                 }
28105                 
28106                 st.store.loadData(avs);
28107                 st.collapse();
28108                 st.setValue(cn);
28109             }
28110             // flag our selected Node.
28111             this.tb.selectedNode = sel;
28112            
28113            
28114             Roo.menu.MenuMgr.hideAll();
28115
28116         }
28117         
28118         if (!updateFooter) {
28119             //this.footDisp.dom.innerHTML = ''; 
28120             return;
28121         }
28122         // update the footer
28123         //
28124         var html = '';
28125         
28126         this.footerEls = ans.reverse();
28127         Roo.each(this.footerEls, function(a,i) {
28128             if (!a) { return; }
28129             html += html.length ? ' &gt; '  :  '';
28130             
28131             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28132             
28133         });
28134        
28135         // 
28136         var sz = this.footDisp.up('td').getSize();
28137         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28138         this.footDisp.dom.style.marginLeft = '5px';
28139         
28140         this.footDisp.dom.style.overflow = 'hidden';
28141         
28142         this.footDisp.dom.innerHTML = html;
28143             
28144         //this.editorsyncValue();
28145     },
28146      
28147     
28148    
28149        
28150     // private
28151     onDestroy : function(){
28152         if(this.rendered){
28153             
28154             this.tb.items.each(function(item){
28155                 if(item.menu){
28156                     item.menu.removeAll();
28157                     if(item.menu.el){
28158                         item.menu.el.destroy();
28159                     }
28160                 }
28161                 item.destroy();
28162             });
28163              
28164         }
28165     },
28166     onFirstFocus: function() {
28167         // need to do this for all the toolbars..
28168         this.tb.items.each(function(item){
28169            item.enable();
28170         });
28171     },
28172     buildToolbar: function(tlist, nm)
28173     {
28174         var editor = this.editor;
28175         var editorcore = this.editorcore;
28176          // create a new element.
28177         var wdiv = editor.wrap.createChild({
28178                 tag: 'div'
28179             }, editor.wrap.dom.firstChild.nextSibling, true);
28180         
28181        
28182         var tb = new Roo.Toolbar(wdiv);
28183         // add the name..
28184         
28185         tb.add(nm+ ":&nbsp;");
28186         
28187         var styles = [];
28188         for(var i in this.styles) {
28189             styles.push(i);
28190         }
28191         
28192         // styles...
28193         if (styles && styles.length) {
28194             
28195             // this needs a multi-select checkbox...
28196             tb.addField( new Roo.form.ComboBox({
28197                 store: new Roo.data.SimpleStore({
28198                     id : 'val',
28199                     fields: ['val', 'selected'],
28200                     data : [] 
28201                 }),
28202                 name : '-roo-edit-className',
28203                 attrname : 'className',
28204                 displayField: 'val',
28205                 typeAhead: false,
28206                 mode: 'local',
28207                 editable : false,
28208                 triggerAction: 'all',
28209                 emptyText:'Select Style',
28210                 selectOnFocus:true,
28211                 width: 130,
28212                 listeners : {
28213                     'select': function(c, r, i) {
28214                         // initial support only for on class per el..
28215                         tb.selectedNode.className =  r ? r.get('val') : '';
28216                         editorcore.syncValue();
28217                     }
28218                 }
28219     
28220             }));
28221         }
28222         
28223         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28224         var tbops = tbc.options;
28225         
28226         for (var i in tlist) {
28227             
28228             var item = tlist[i];
28229             tb.add(item.title + ":&nbsp;");
28230             
28231             
28232             //optname == used so you can configure the options available..
28233             var opts = item.opts ? item.opts : false;
28234             if (item.optname) {
28235                 opts = tbops[item.optname];
28236            
28237             }
28238             
28239             if (opts) {
28240                 // opts == pulldown..
28241                 tb.addField( new Roo.form.ComboBox({
28242                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28243                         id : 'val',
28244                         fields: ['val', 'display'],
28245                         data : opts  
28246                     }),
28247                     name : '-roo-edit-' + i,
28248                     attrname : i,
28249                     stylename : item.style ? item.style : false,
28250                     displayField: item.displayField ? item.displayField : 'val',
28251                     valueField :  'val',
28252                     typeAhead: false,
28253                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28254                     editable : false,
28255                     triggerAction: 'all',
28256                     emptyText:'Select',
28257                     selectOnFocus:true,
28258                     width: item.width ? item.width  : 130,
28259                     listeners : {
28260                         'select': function(c, r, i) {
28261                             if (c.stylename) {
28262                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28263                                 return;
28264                             }
28265                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28266                         }
28267                     }
28268
28269                 }));
28270                 continue;
28271                     
28272                  
28273                 
28274                 tb.addField( new Roo.form.TextField({
28275                     name: i,
28276                     width: 100,
28277                     //allowBlank:false,
28278                     value: ''
28279                 }));
28280                 continue;
28281             }
28282             tb.addField( new Roo.form.TextField({
28283                 name: '-roo-edit-' + i,
28284                 attrname : i,
28285                 
28286                 width: item.width,
28287                 //allowBlank:true,
28288                 value: '',
28289                 listeners: {
28290                     'change' : function(f, nv, ov) {
28291                         tb.selectedNode.setAttribute(f.attrname, nv);
28292                     }
28293                 }
28294             }));
28295              
28296         }
28297         
28298         var _this = this;
28299         
28300         if(nm == 'BODY'){
28301             tb.addSeparator();
28302         
28303             tb.addButton( {
28304                 text: 'Stylesheets',
28305
28306                 listeners : {
28307                     click : function ()
28308                     {
28309                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28310                     }
28311                 }
28312             });
28313         }
28314         
28315         tb.addFill();
28316         tb.addButton( {
28317             text: 'Remove Tag',
28318     
28319             listeners : {
28320                 click : function ()
28321                 {
28322                     // remove
28323                     // undo does not work.
28324                      
28325                     var sn = tb.selectedNode;
28326                     
28327                     var pn = sn.parentNode;
28328                     
28329                     var stn =  sn.childNodes[0];
28330                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28331                     while (sn.childNodes.length) {
28332                         var node = sn.childNodes[0];
28333                         sn.removeChild(node);
28334                         //Roo.log(node);
28335                         pn.insertBefore(node, sn);
28336                         
28337                     }
28338                     pn.removeChild(sn);
28339                     var range = editorcore.createRange();
28340         
28341                     range.setStart(stn,0);
28342                     range.setEnd(en,0); //????
28343                     //range.selectNode(sel);
28344                     
28345                     
28346                     var selection = editorcore.getSelection();
28347                     selection.removeAllRanges();
28348                     selection.addRange(range);
28349                     
28350                     
28351                     
28352                     //_this.updateToolbar(null, null, pn);
28353                     _this.updateToolbar(null, null, null);
28354                     _this.footDisp.dom.innerHTML = ''; 
28355                 }
28356             }
28357             
28358                     
28359                 
28360             
28361         });
28362         
28363         
28364         tb.el.on('click', function(e){
28365             e.preventDefault(); // what does this do?
28366         });
28367         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28368         tb.el.hide();
28369         tb.name = nm;
28370         // dont need to disable them... as they will get hidden
28371         return tb;
28372          
28373         
28374     },
28375     buildFooter : function()
28376     {
28377         
28378         var fel = this.editor.wrap.createChild();
28379         this.footer = new Roo.Toolbar(fel);
28380         // toolbar has scrolly on left / right?
28381         var footDisp= new Roo.Toolbar.Fill();
28382         var _t = this;
28383         this.footer.add(
28384             {
28385                 text : '&lt;',
28386                 xtype: 'Button',
28387                 handler : function() {
28388                     _t.footDisp.scrollTo('left',0,true)
28389                 }
28390             }
28391         );
28392         this.footer.add( footDisp );
28393         this.footer.add( 
28394             {
28395                 text : '&gt;',
28396                 xtype: 'Button',
28397                 handler : function() {
28398                     // no animation..
28399                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28400                 }
28401             }
28402         );
28403         var fel = Roo.get(footDisp.el);
28404         fel.addClass('x-editor-context');
28405         this.footDispWrap = fel; 
28406         this.footDispWrap.overflow  = 'hidden';
28407         
28408         this.footDisp = fel.createChild();
28409         this.footDispWrap.on('click', this.onContextClick, this)
28410         
28411         
28412     },
28413     onContextClick : function (ev,dom)
28414     {
28415         ev.preventDefault();
28416         var  cn = dom.className;
28417         //Roo.log(cn);
28418         if (!cn.match(/x-ed-loc-/)) {
28419             return;
28420         }
28421         var n = cn.split('-').pop();
28422         var ans = this.footerEls;
28423         var sel = ans[n];
28424         
28425          // pick
28426         var range = this.editorcore.createRange();
28427         
28428         range.selectNodeContents(sel);
28429         //range.selectNode(sel);
28430         
28431         
28432         var selection = this.editorcore.getSelection();
28433         selection.removeAllRanges();
28434         selection.addRange(range);
28435         
28436         
28437         
28438         this.updateToolbar(null, null, sel);
28439         
28440         
28441     }
28442     
28443     
28444     
28445     
28446     
28447 });
28448
28449
28450
28451
28452
28453 /*
28454  * Based on:
28455  * Ext JS Library 1.1.1
28456  * Copyright(c) 2006-2007, Ext JS, LLC.
28457  *
28458  * Originally Released Under LGPL - original licence link has changed is not relivant.
28459  *
28460  * Fork - LGPL
28461  * <script type="text/javascript">
28462  */
28463  
28464 /**
28465  * @class Roo.form.BasicForm
28466  * @extends Roo.util.Observable
28467  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28468  * @constructor
28469  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28470  * @param {Object} config Configuration options
28471  */
28472 Roo.form.BasicForm = function(el, config){
28473     this.allItems = [];
28474     this.childForms = [];
28475     Roo.apply(this, config);
28476     /*
28477      * The Roo.form.Field items in this form.
28478      * @type MixedCollection
28479      */
28480      
28481      
28482     this.items = new Roo.util.MixedCollection(false, function(o){
28483         return o.id || (o.id = Roo.id());
28484     });
28485     this.addEvents({
28486         /**
28487          * @event beforeaction
28488          * Fires before any action is performed. Return false to cancel the action.
28489          * @param {Form} this
28490          * @param {Action} action The action to be performed
28491          */
28492         beforeaction: true,
28493         /**
28494          * @event actionfailed
28495          * Fires when an action fails.
28496          * @param {Form} this
28497          * @param {Action} action The action that failed
28498          */
28499         actionfailed : true,
28500         /**
28501          * @event actioncomplete
28502          * Fires when an action is completed.
28503          * @param {Form} this
28504          * @param {Action} action The action that completed
28505          */
28506         actioncomplete : true
28507     });
28508     if(el){
28509         this.initEl(el);
28510     }
28511     Roo.form.BasicForm.superclass.constructor.call(this);
28512 };
28513
28514 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28515     /**
28516      * @cfg {String} method
28517      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28518      */
28519     /**
28520      * @cfg {DataReader} reader
28521      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28522      * This is optional as there is built-in support for processing JSON.
28523      */
28524     /**
28525      * @cfg {DataReader} errorReader
28526      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28527      * This is completely optional as there is built-in support for processing JSON.
28528      */
28529     /**
28530      * @cfg {String} url
28531      * The URL to use for form actions if one isn't supplied in the action options.
28532      */
28533     /**
28534      * @cfg {Boolean} fileUpload
28535      * Set to true if this form is a file upload.
28536      */
28537      
28538     /**
28539      * @cfg {Object} baseParams
28540      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28541      */
28542      /**
28543      
28544     /**
28545      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28546      */
28547     timeout: 30,
28548
28549     // private
28550     activeAction : null,
28551
28552     /**
28553      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28554      * or setValues() data instead of when the form was first created.
28555      */
28556     trackResetOnLoad : false,
28557     
28558     
28559     /**
28560      * childForms - used for multi-tab forms
28561      * @type {Array}
28562      */
28563     childForms : false,
28564     
28565     /**
28566      * allItems - full list of fields.
28567      * @type {Array}
28568      */
28569     allItems : false,
28570     
28571     /**
28572      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28573      * element by passing it or its id or mask the form itself by passing in true.
28574      * @type Mixed
28575      */
28576     waitMsgTarget : false,
28577
28578     // private
28579     initEl : function(el){
28580         this.el = Roo.get(el);
28581         this.id = this.el.id || Roo.id();
28582         this.el.on('submit', this.onSubmit, this);
28583         this.el.addClass('x-form');
28584     },
28585
28586     // private
28587     onSubmit : function(e){
28588         e.stopEvent();
28589     },
28590
28591     /**
28592      * Returns true if client-side validation on the form is successful.
28593      * @return Boolean
28594      */
28595     isValid : function(){
28596         var valid = true;
28597         this.items.each(function(f){
28598            if(!f.validate()){
28599                valid = false;
28600            }
28601         });
28602         return valid;
28603     },
28604
28605     /**
28606      * Returns true if any fields in this form have changed since their original load.
28607      * @return Boolean
28608      */
28609     isDirty : function(){
28610         var dirty = false;
28611         this.items.each(function(f){
28612            if(f.isDirty()){
28613                dirty = true;
28614                return false;
28615            }
28616         });
28617         return dirty;
28618     },
28619
28620     /**
28621      * Performs a predefined action (submit or load) or custom actions you define on this form.
28622      * @param {String} actionName The name of the action type
28623      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28624      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28625      * accept other config options):
28626      * <pre>
28627 Property          Type             Description
28628 ----------------  ---------------  ----------------------------------------------------------------------------------
28629 url               String           The url for the action (defaults to the form's url)
28630 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28631 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28632 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28633                                    validate the form on the client (defaults to false)
28634      * </pre>
28635      * @return {BasicForm} this
28636      */
28637     doAction : function(action, options){
28638         if(typeof action == 'string'){
28639             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28640         }
28641         if(this.fireEvent('beforeaction', this, action) !== false){
28642             this.beforeAction(action);
28643             action.run.defer(100, action);
28644         }
28645         return this;
28646     },
28647
28648     /**
28649      * Shortcut to do a submit action.
28650      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28651      * @return {BasicForm} this
28652      */
28653     submit : function(options){
28654         this.doAction('submit', options);
28655         return this;
28656     },
28657
28658     /**
28659      * Shortcut to do a load action.
28660      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28661      * @return {BasicForm} this
28662      */
28663     load : function(options){
28664         this.doAction('load', options);
28665         return this;
28666     },
28667
28668     /**
28669      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28670      * @param {Record} record The record to edit
28671      * @return {BasicForm} this
28672      */
28673     updateRecord : function(record){
28674         record.beginEdit();
28675         var fs = record.fields;
28676         fs.each(function(f){
28677             var field = this.findField(f.name);
28678             if(field){
28679                 record.set(f.name, field.getValue());
28680             }
28681         }, this);
28682         record.endEdit();
28683         return this;
28684     },
28685
28686     /**
28687      * Loads an Roo.data.Record into this form.
28688      * @param {Record} record The record to load
28689      * @return {BasicForm} this
28690      */
28691     loadRecord : function(record){
28692         this.setValues(record.data);
28693         return this;
28694     },
28695
28696     // private
28697     beforeAction : function(action){
28698         var o = action.options;
28699         
28700        
28701         if(this.waitMsgTarget === true){
28702             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28703         }else if(this.waitMsgTarget){
28704             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28705             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28706         }else {
28707             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28708         }
28709          
28710     },
28711
28712     // private
28713     afterAction : function(action, success){
28714         this.activeAction = null;
28715         var o = action.options;
28716         
28717         if(this.waitMsgTarget === true){
28718             this.el.unmask();
28719         }else if(this.waitMsgTarget){
28720             this.waitMsgTarget.unmask();
28721         }else{
28722             Roo.MessageBox.updateProgress(1);
28723             Roo.MessageBox.hide();
28724         }
28725          
28726         if(success){
28727             if(o.reset){
28728                 this.reset();
28729             }
28730             Roo.callback(o.success, o.scope, [this, action]);
28731             this.fireEvent('actioncomplete', this, action);
28732             
28733         }else{
28734             
28735             // failure condition..
28736             // we have a scenario where updates need confirming.
28737             // eg. if a locking scenario exists..
28738             // we look for { errors : { needs_confirm : true }} in the response.
28739             if (
28740                 (typeof(action.result) != 'undefined')  &&
28741                 (typeof(action.result.errors) != 'undefined')  &&
28742                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28743            ){
28744                 var _t = this;
28745                 Roo.MessageBox.confirm(
28746                     "Change requires confirmation",
28747                     action.result.errorMsg,
28748                     function(r) {
28749                         if (r != 'yes') {
28750                             return;
28751                         }
28752                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28753                     }
28754                     
28755                 );
28756                 
28757                 
28758                 
28759                 return;
28760             }
28761             
28762             Roo.callback(o.failure, o.scope, [this, action]);
28763             // show an error message if no failed handler is set..
28764             if (!this.hasListener('actionfailed')) {
28765                 Roo.MessageBox.alert("Error",
28766                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28767                         action.result.errorMsg :
28768                         "Saving Failed, please check your entries or try again"
28769                 );
28770             }
28771             
28772             this.fireEvent('actionfailed', this, action);
28773         }
28774         
28775     },
28776
28777     /**
28778      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28779      * @param {String} id The value to search for
28780      * @return Field
28781      */
28782     findField : function(id){
28783         var field = this.items.get(id);
28784         if(!field){
28785             this.items.each(function(f){
28786                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28787                     field = f;
28788                     return false;
28789                 }
28790             });
28791         }
28792         return field || null;
28793     },
28794
28795     /**
28796      * Add a secondary form to this one, 
28797      * Used to provide tabbed forms. One form is primary, with hidden values 
28798      * which mirror the elements from the other forms.
28799      * 
28800      * @param {Roo.form.Form} form to add.
28801      * 
28802      */
28803     addForm : function(form)
28804     {
28805        
28806         if (this.childForms.indexOf(form) > -1) {
28807             // already added..
28808             return;
28809         }
28810         this.childForms.push(form);
28811         var n = '';
28812         Roo.each(form.allItems, function (fe) {
28813             
28814             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28815             if (this.findField(n)) { // already added..
28816                 return;
28817             }
28818             var add = new Roo.form.Hidden({
28819                 name : n
28820             });
28821             add.render(this.el);
28822             
28823             this.add( add );
28824         }, this);
28825         
28826     },
28827     /**
28828      * Mark fields in this form invalid in bulk.
28829      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28830      * @return {BasicForm} this
28831      */
28832     markInvalid : function(errors){
28833         if(errors instanceof Array){
28834             for(var i = 0, len = errors.length; i < len; i++){
28835                 var fieldError = errors[i];
28836                 var f = this.findField(fieldError.id);
28837                 if(f){
28838                     f.markInvalid(fieldError.msg);
28839                 }
28840             }
28841         }else{
28842             var field, id;
28843             for(id in errors){
28844                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28845                     field.markInvalid(errors[id]);
28846                 }
28847             }
28848         }
28849         Roo.each(this.childForms || [], function (f) {
28850             f.markInvalid(errors);
28851         });
28852         
28853         return this;
28854     },
28855
28856     /**
28857      * Set values for fields in this form in bulk.
28858      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28859      * @return {BasicForm} this
28860      */
28861     setValues : function(values){
28862         if(values instanceof Array){ // array of objects
28863             for(var i = 0, len = values.length; i < len; i++){
28864                 var v = values[i];
28865                 var f = this.findField(v.id);
28866                 if(f){
28867                     f.setValue(v.value);
28868                     if(this.trackResetOnLoad){
28869                         f.originalValue = f.getValue();
28870                     }
28871                 }
28872             }
28873         }else{ // object hash
28874             var field, id;
28875             for(id in values){
28876                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28877                     
28878                     if (field.setFromData && 
28879                         field.valueField && 
28880                         field.displayField &&
28881                         // combos' with local stores can 
28882                         // be queried via setValue()
28883                         // to set their value..
28884                         (field.store && !field.store.isLocal)
28885                         ) {
28886                         // it's a combo
28887                         var sd = { };
28888                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28889                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28890                         field.setFromData(sd);
28891                         
28892                     } else {
28893                         field.setValue(values[id]);
28894                     }
28895                     
28896                     
28897                     if(this.trackResetOnLoad){
28898                         field.originalValue = field.getValue();
28899                     }
28900                 }
28901             }
28902         }
28903          
28904         Roo.each(this.childForms || [], function (f) {
28905             f.setValues(values);
28906         });
28907                 
28908         return this;
28909     },
28910
28911     /**
28912      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28913      * they are returned as an array.
28914      * @param {Boolean} asString
28915      * @return {Object}
28916      */
28917     getValues : function(asString){
28918         if (this.childForms) {
28919             // copy values from the child forms
28920             Roo.each(this.childForms, function (f) {
28921                 this.setValues(f.getValues());
28922             }, this);
28923         }
28924         
28925         
28926         
28927         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28928         if(asString === true){
28929             return fs;
28930         }
28931         return Roo.urlDecode(fs);
28932     },
28933     
28934     /**
28935      * Returns the fields in this form as an object with key/value pairs. 
28936      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28937      * @return {Object}
28938      */
28939     getFieldValues : function(with_hidden)
28940     {
28941         if (this.childForms) {
28942             // copy values from the child forms
28943             // should this call getFieldValues - probably not as we do not currently copy
28944             // hidden fields when we generate..
28945             Roo.each(this.childForms, function (f) {
28946                 this.setValues(f.getValues());
28947             }, this);
28948         }
28949         
28950         var ret = {};
28951         this.items.each(function(f){
28952             if (!f.getName()) {
28953                 return;
28954             }
28955             var v = f.getValue();
28956             if (f.inputType =='radio') {
28957                 if (typeof(ret[f.getName()]) == 'undefined') {
28958                     ret[f.getName()] = ''; // empty..
28959                 }
28960                 
28961                 if (!f.el.dom.checked) {
28962                     return;
28963                     
28964                 }
28965                 v = f.el.dom.value;
28966                 
28967             }
28968             
28969             // not sure if this supported any more..
28970             if ((typeof(v) == 'object') && f.getRawValue) {
28971                 v = f.getRawValue() ; // dates..
28972             }
28973             // combo boxes where name != hiddenName...
28974             if (f.name != f.getName()) {
28975                 ret[f.name] = f.getRawValue();
28976             }
28977             ret[f.getName()] = v;
28978         });
28979         
28980         return ret;
28981     },
28982
28983     /**
28984      * Clears all invalid messages in this form.
28985      * @return {BasicForm} this
28986      */
28987     clearInvalid : function(){
28988         this.items.each(function(f){
28989            f.clearInvalid();
28990         });
28991         
28992         Roo.each(this.childForms || [], function (f) {
28993             f.clearInvalid();
28994         });
28995         
28996         
28997         return this;
28998     },
28999
29000     /**
29001      * Resets this form.
29002      * @return {BasicForm} this
29003      */
29004     reset : function(){
29005         this.items.each(function(f){
29006             f.reset();
29007         });
29008         
29009         Roo.each(this.childForms || [], function (f) {
29010             f.reset();
29011         });
29012        
29013         
29014         return this;
29015     },
29016
29017     /**
29018      * Add Roo.form components to this form.
29019      * @param {Field} field1
29020      * @param {Field} field2 (optional)
29021      * @param {Field} etc (optional)
29022      * @return {BasicForm} this
29023      */
29024     add : function(){
29025         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29026         return this;
29027     },
29028
29029
29030     /**
29031      * Removes a field from the items collection (does NOT remove its markup).
29032      * @param {Field} field
29033      * @return {BasicForm} this
29034      */
29035     remove : function(field){
29036         this.items.remove(field);
29037         return this;
29038     },
29039
29040     /**
29041      * Looks at the fields in this form, checks them for an id attribute,
29042      * and calls applyTo on the existing dom element with that id.
29043      * @return {BasicForm} this
29044      */
29045     render : function(){
29046         this.items.each(function(f){
29047             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29048                 f.applyTo(f.id);
29049             }
29050         });
29051         return this;
29052     },
29053
29054     /**
29055      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29056      * @param {Object} values
29057      * @return {BasicForm} this
29058      */
29059     applyToFields : function(o){
29060         this.items.each(function(f){
29061            Roo.apply(f, o);
29062         });
29063         return this;
29064     },
29065
29066     /**
29067      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29068      * @param {Object} values
29069      * @return {BasicForm} this
29070      */
29071     applyIfToFields : function(o){
29072         this.items.each(function(f){
29073            Roo.applyIf(f, o);
29074         });
29075         return this;
29076     }
29077 });
29078
29079 // back compat
29080 Roo.BasicForm = Roo.form.BasicForm;/*
29081  * Based on:
29082  * Ext JS Library 1.1.1
29083  * Copyright(c) 2006-2007, Ext JS, LLC.
29084  *
29085  * Originally Released Under LGPL - original licence link has changed is not relivant.
29086  *
29087  * Fork - LGPL
29088  * <script type="text/javascript">
29089  */
29090
29091 /**
29092  * @class Roo.form.Form
29093  * @extends Roo.form.BasicForm
29094  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29095  * @constructor
29096  * @param {Object} config Configuration options
29097  */
29098 Roo.form.Form = function(config){
29099     var xitems =  [];
29100     if (config.items) {
29101         xitems = config.items;
29102         delete config.items;
29103     }
29104    
29105     
29106     Roo.form.Form.superclass.constructor.call(this, null, config);
29107     this.url = this.url || this.action;
29108     if(!this.root){
29109         this.root = new Roo.form.Layout(Roo.applyIf({
29110             id: Roo.id()
29111         }, config));
29112     }
29113     this.active = this.root;
29114     /**
29115      * Array of all the buttons that have been added to this form via {@link addButton}
29116      * @type Array
29117      */
29118     this.buttons = [];
29119     this.allItems = [];
29120     this.addEvents({
29121         /**
29122          * @event clientvalidation
29123          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29124          * @param {Form} this
29125          * @param {Boolean} valid true if the form has passed client-side validation
29126          */
29127         clientvalidation: true,
29128         /**
29129          * @event rendered
29130          * Fires when the form is rendered
29131          * @param {Roo.form.Form} form
29132          */
29133         rendered : true
29134     });
29135     
29136     if (this.progressUrl) {
29137             // push a hidden field onto the list of fields..
29138             this.addxtype( {
29139                     xns: Roo.form, 
29140                     xtype : 'Hidden', 
29141                     name : 'UPLOAD_IDENTIFIER' 
29142             });
29143         }
29144         
29145     
29146     Roo.each(xitems, this.addxtype, this);
29147     
29148     
29149     
29150 };
29151
29152 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29153     /**
29154      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29155      */
29156     /**
29157      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29158      */
29159     /**
29160      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29161      */
29162     buttonAlign:'center',
29163
29164     /**
29165      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29166      */
29167     minButtonWidth:75,
29168
29169     /**
29170      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29171      * This property cascades to child containers if not set.
29172      */
29173     labelAlign:'left',
29174
29175     /**
29176      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29177      * fires a looping event with that state. This is required to bind buttons to the valid
29178      * state using the config value formBind:true on the button.
29179      */
29180     monitorValid : false,
29181
29182     /**
29183      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29184      */
29185     monitorPoll : 200,
29186     
29187     /**
29188      * @cfg {String} progressUrl - Url to return progress data 
29189      */
29190     
29191     progressUrl : false,
29192   
29193     /**
29194      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29195      * fields are added and the column is closed. If no fields are passed the column remains open
29196      * until end() is called.
29197      * @param {Object} config The config to pass to the column
29198      * @param {Field} field1 (optional)
29199      * @param {Field} field2 (optional)
29200      * @param {Field} etc (optional)
29201      * @return Column The column container object
29202      */
29203     column : function(c){
29204         var col = new Roo.form.Column(c);
29205         this.start(col);
29206         if(arguments.length > 1){ // duplicate code required because of Opera
29207             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29208             this.end();
29209         }
29210         return col;
29211     },
29212
29213     /**
29214      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29215      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29216      * until end() is called.
29217      * @param {Object} config The config to pass to the fieldset
29218      * @param {Field} field1 (optional)
29219      * @param {Field} field2 (optional)
29220      * @param {Field} etc (optional)
29221      * @return FieldSet The fieldset container object
29222      */
29223     fieldset : function(c){
29224         var fs = new Roo.form.FieldSet(c);
29225         this.start(fs);
29226         if(arguments.length > 1){ // duplicate code required because of Opera
29227             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29228             this.end();
29229         }
29230         return fs;
29231     },
29232
29233     /**
29234      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29235      * fields are added and the container is closed. If no fields are passed the container remains open
29236      * until end() is called.
29237      * @param {Object} config The config to pass to the Layout
29238      * @param {Field} field1 (optional)
29239      * @param {Field} field2 (optional)
29240      * @param {Field} etc (optional)
29241      * @return Layout The container object
29242      */
29243     container : function(c){
29244         var l = new Roo.form.Layout(c);
29245         this.start(l);
29246         if(arguments.length > 1){ // duplicate code required because of Opera
29247             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29248             this.end();
29249         }
29250         return l;
29251     },
29252
29253     /**
29254      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29255      * @param {Object} container A Roo.form.Layout or subclass of Layout
29256      * @return {Form} this
29257      */
29258     start : function(c){
29259         // cascade label info
29260         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29261         this.active.stack.push(c);
29262         c.ownerCt = this.active;
29263         this.active = c;
29264         return this;
29265     },
29266
29267     /**
29268      * Closes the current open container
29269      * @return {Form} this
29270      */
29271     end : function(){
29272         if(this.active == this.root){
29273             return this;
29274         }
29275         this.active = this.active.ownerCt;
29276         return this;
29277     },
29278
29279     /**
29280      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29281      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29282      * as the label of the field.
29283      * @param {Field} field1
29284      * @param {Field} field2 (optional)
29285      * @param {Field} etc. (optional)
29286      * @return {Form} this
29287      */
29288     add : function(){
29289         this.active.stack.push.apply(this.active.stack, arguments);
29290         this.allItems.push.apply(this.allItems,arguments);
29291         var r = [];
29292         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29293             if(a[i].isFormField){
29294                 r.push(a[i]);
29295             }
29296         }
29297         if(r.length > 0){
29298             Roo.form.Form.superclass.add.apply(this, r);
29299         }
29300         return this;
29301     },
29302     
29303
29304     
29305     
29306     
29307      /**
29308      * Find any element that has been added to a form, using it's ID or name
29309      * This can include framesets, columns etc. along with regular fields..
29310      * @param {String} id - id or name to find.
29311      
29312      * @return {Element} e - or false if nothing found.
29313      */
29314     findbyId : function(id)
29315     {
29316         var ret = false;
29317         if (!id) {
29318             return ret;
29319         }
29320         Roo.each(this.allItems, function(f){
29321             if (f.id == id || f.name == id ){
29322                 ret = f;
29323                 return false;
29324             }
29325         });
29326         return ret;
29327     },
29328
29329     
29330     
29331     /**
29332      * Render this form into the passed container. This should only be called once!
29333      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29334      * @return {Form} this
29335      */
29336     render : function(ct)
29337     {
29338         
29339         
29340         
29341         ct = Roo.get(ct);
29342         var o = this.autoCreate || {
29343             tag: 'form',
29344             method : this.method || 'POST',
29345             id : this.id || Roo.id()
29346         };
29347         this.initEl(ct.createChild(o));
29348
29349         this.root.render(this.el);
29350         
29351        
29352              
29353         this.items.each(function(f){
29354             f.render('x-form-el-'+f.id);
29355         });
29356
29357         if(this.buttons.length > 0){
29358             // tables are required to maintain order and for correct IE layout
29359             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29360                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29361                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29362             }}, null, true);
29363             var tr = tb.getElementsByTagName('tr')[0];
29364             for(var i = 0, len = this.buttons.length; i < len; i++) {
29365                 var b = this.buttons[i];
29366                 var td = document.createElement('td');
29367                 td.className = 'x-form-btn-td';
29368                 b.render(tr.appendChild(td));
29369             }
29370         }
29371         if(this.monitorValid){ // initialize after render
29372             this.startMonitoring();
29373         }
29374         this.fireEvent('rendered', this);
29375         return this;
29376     },
29377
29378     /**
29379      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29380      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29381      * object or a valid Roo.DomHelper element config
29382      * @param {Function} handler The function called when the button is clicked
29383      * @param {Object} scope (optional) The scope of the handler function
29384      * @return {Roo.Button}
29385      */
29386     addButton : function(config, handler, scope){
29387         var bc = {
29388             handler: handler,
29389             scope: scope,
29390             minWidth: this.minButtonWidth,
29391             hideParent:true
29392         };
29393         if(typeof config == "string"){
29394             bc.text = config;
29395         }else{
29396             Roo.apply(bc, config);
29397         }
29398         var btn = new Roo.Button(null, bc);
29399         this.buttons.push(btn);
29400         return btn;
29401     },
29402
29403      /**
29404      * Adds a series of form elements (using the xtype property as the factory method.
29405      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29406      * @param {Object} config 
29407      */
29408     
29409     addxtype : function()
29410     {
29411         var ar = Array.prototype.slice.call(arguments, 0);
29412         var ret = false;
29413         for(var i = 0; i < ar.length; i++) {
29414             if (!ar[i]) {
29415                 continue; // skip -- if this happends something invalid got sent, we 
29416                 // should ignore it, as basically that interface element will not show up
29417                 // and that should be pretty obvious!!
29418             }
29419             
29420             if (Roo.form[ar[i].xtype]) {
29421                 ar[i].form = this;
29422                 var fe = Roo.factory(ar[i], Roo.form);
29423                 if (!ret) {
29424                     ret = fe;
29425                 }
29426                 fe.form = this;
29427                 if (fe.store) {
29428                     fe.store.form = this;
29429                 }
29430                 if (fe.isLayout) {  
29431                          
29432                     this.start(fe);
29433                     this.allItems.push(fe);
29434                     if (fe.items && fe.addxtype) {
29435                         fe.addxtype.apply(fe, fe.items);
29436                         delete fe.items;
29437                     }
29438                      this.end();
29439                     continue;
29440                 }
29441                 
29442                 
29443                  
29444                 this.add(fe);
29445               //  console.log('adding ' + ar[i].xtype);
29446             }
29447             if (ar[i].xtype == 'Button') {  
29448                 //console.log('adding button');
29449                 //console.log(ar[i]);
29450                 this.addButton(ar[i]);
29451                 this.allItems.push(fe);
29452                 continue;
29453             }
29454             
29455             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29456                 alert('end is not supported on xtype any more, use items');
29457             //    this.end();
29458             //    //console.log('adding end');
29459             }
29460             
29461         }
29462         return ret;
29463     },
29464     
29465     /**
29466      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29467      * option "monitorValid"
29468      */
29469     startMonitoring : function(){
29470         if(!this.bound){
29471             this.bound = true;
29472             Roo.TaskMgr.start({
29473                 run : this.bindHandler,
29474                 interval : this.monitorPoll || 200,
29475                 scope: this
29476             });
29477         }
29478     },
29479
29480     /**
29481      * Stops monitoring of the valid state of this form
29482      */
29483     stopMonitoring : function(){
29484         this.bound = false;
29485     },
29486
29487     // private
29488     bindHandler : function(){
29489         if(!this.bound){
29490             return false; // stops binding
29491         }
29492         var valid = true;
29493         this.items.each(function(f){
29494             if(!f.isValid(true)){
29495                 valid = false;
29496                 return false;
29497             }
29498         });
29499         for(var i = 0, len = this.buttons.length; i < len; i++){
29500             var btn = this.buttons[i];
29501             if(btn.formBind === true && btn.disabled === valid){
29502                 btn.setDisabled(!valid);
29503             }
29504         }
29505         this.fireEvent('clientvalidation', this, valid);
29506     }
29507     
29508     
29509     
29510     
29511     
29512     
29513     
29514     
29515 });
29516
29517
29518 // back compat
29519 Roo.Form = Roo.form.Form;
29520 /*
29521  * Based on:
29522  * Ext JS Library 1.1.1
29523  * Copyright(c) 2006-2007, Ext JS, LLC.
29524  *
29525  * Originally Released Under LGPL - original licence link has changed is not relivant.
29526  *
29527  * Fork - LGPL
29528  * <script type="text/javascript">
29529  */
29530
29531 // as we use this in bootstrap.
29532 Roo.namespace('Roo.form');
29533  /**
29534  * @class Roo.form.Action
29535  * Internal Class used to handle form actions
29536  * @constructor
29537  * @param {Roo.form.BasicForm} el The form element or its id
29538  * @param {Object} config Configuration options
29539  */
29540
29541  
29542  
29543 // define the action interface
29544 Roo.form.Action = function(form, options){
29545     this.form = form;
29546     this.options = options || {};
29547 };
29548 /**
29549  * Client Validation Failed
29550  * @const 
29551  */
29552 Roo.form.Action.CLIENT_INVALID = 'client';
29553 /**
29554  * Server Validation Failed
29555  * @const 
29556  */
29557 Roo.form.Action.SERVER_INVALID = 'server';
29558  /**
29559  * Connect to Server Failed
29560  * @const 
29561  */
29562 Roo.form.Action.CONNECT_FAILURE = 'connect';
29563 /**
29564  * Reading Data from Server Failed
29565  * @const 
29566  */
29567 Roo.form.Action.LOAD_FAILURE = 'load';
29568
29569 Roo.form.Action.prototype = {
29570     type : 'default',
29571     failureType : undefined,
29572     response : undefined,
29573     result : undefined,
29574
29575     // interface method
29576     run : function(options){
29577
29578     },
29579
29580     // interface method
29581     success : function(response){
29582
29583     },
29584
29585     // interface method
29586     handleResponse : function(response){
29587
29588     },
29589
29590     // default connection failure
29591     failure : function(response){
29592         
29593         this.response = response;
29594         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29595         this.form.afterAction(this, false);
29596     },
29597
29598     processResponse : function(response){
29599         this.response = response;
29600         if(!response.responseText){
29601             return true;
29602         }
29603         this.result = this.handleResponse(response);
29604         return this.result;
29605     },
29606
29607     // utility functions used internally
29608     getUrl : function(appendParams){
29609         var url = this.options.url || this.form.url || this.form.el.dom.action;
29610         if(appendParams){
29611             var p = this.getParams();
29612             if(p){
29613                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29614             }
29615         }
29616         return url;
29617     },
29618
29619     getMethod : function(){
29620         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29621     },
29622
29623     getParams : function(){
29624         var bp = this.form.baseParams;
29625         var p = this.options.params;
29626         if(p){
29627             if(typeof p == "object"){
29628                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29629             }else if(typeof p == 'string' && bp){
29630                 p += '&' + Roo.urlEncode(bp);
29631             }
29632         }else if(bp){
29633             p = Roo.urlEncode(bp);
29634         }
29635         return p;
29636     },
29637
29638     createCallback : function(){
29639         return {
29640             success: this.success,
29641             failure: this.failure,
29642             scope: this,
29643             timeout: (this.form.timeout*1000),
29644             upload: this.form.fileUpload ? this.success : undefined
29645         };
29646     }
29647 };
29648
29649 Roo.form.Action.Submit = function(form, options){
29650     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29651 };
29652
29653 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29654     type : 'submit',
29655
29656     haveProgress : false,
29657     uploadComplete : false,
29658     
29659     // uploadProgress indicator.
29660     uploadProgress : function()
29661     {
29662         if (!this.form.progressUrl) {
29663             return;
29664         }
29665         
29666         if (!this.haveProgress) {
29667             Roo.MessageBox.progress("Uploading", "Uploading");
29668         }
29669         if (this.uploadComplete) {
29670            Roo.MessageBox.hide();
29671            return;
29672         }
29673         
29674         this.haveProgress = true;
29675    
29676         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29677         
29678         var c = new Roo.data.Connection();
29679         c.request({
29680             url : this.form.progressUrl,
29681             params: {
29682                 id : uid
29683             },
29684             method: 'GET',
29685             success : function(req){
29686                //console.log(data);
29687                 var rdata = false;
29688                 var edata;
29689                 try  {
29690                    rdata = Roo.decode(req.responseText)
29691                 } catch (e) {
29692                     Roo.log("Invalid data from server..");
29693                     Roo.log(edata);
29694                     return;
29695                 }
29696                 if (!rdata || !rdata.success) {
29697                     Roo.log(rdata);
29698                     Roo.MessageBox.alert(Roo.encode(rdata));
29699                     return;
29700                 }
29701                 var data = rdata.data;
29702                 
29703                 if (this.uploadComplete) {
29704                    Roo.MessageBox.hide();
29705                    return;
29706                 }
29707                    
29708                 if (data){
29709                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29710                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29711                     );
29712                 }
29713                 this.uploadProgress.defer(2000,this);
29714             },
29715        
29716             failure: function(data) {
29717                 Roo.log('progress url failed ');
29718                 Roo.log(data);
29719             },
29720             scope : this
29721         });
29722            
29723     },
29724     
29725     
29726     run : function()
29727     {
29728         // run get Values on the form, so it syncs any secondary forms.
29729         this.form.getValues();
29730         
29731         var o = this.options;
29732         var method = this.getMethod();
29733         var isPost = method == 'POST';
29734         if(o.clientValidation === false || this.form.isValid()){
29735             
29736             if (this.form.progressUrl) {
29737                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29738                     (new Date() * 1) + '' + Math.random());
29739                     
29740             } 
29741             
29742             
29743             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29744                 form:this.form.el.dom,
29745                 url:this.getUrl(!isPost),
29746                 method: method,
29747                 params:isPost ? this.getParams() : null,
29748                 isUpload: this.form.fileUpload
29749             }));
29750             
29751             this.uploadProgress();
29752
29753         }else if (o.clientValidation !== false){ // client validation failed
29754             this.failureType = Roo.form.Action.CLIENT_INVALID;
29755             this.form.afterAction(this, false);
29756         }
29757     },
29758
29759     success : function(response)
29760     {
29761         this.uploadComplete= true;
29762         if (this.haveProgress) {
29763             Roo.MessageBox.hide();
29764         }
29765         
29766         
29767         var result = this.processResponse(response);
29768         if(result === true || result.success){
29769             this.form.afterAction(this, true);
29770             return;
29771         }
29772         if(result.errors){
29773             this.form.markInvalid(result.errors);
29774             this.failureType = Roo.form.Action.SERVER_INVALID;
29775         }
29776         this.form.afterAction(this, false);
29777     },
29778     failure : function(response)
29779     {
29780         this.uploadComplete= true;
29781         if (this.haveProgress) {
29782             Roo.MessageBox.hide();
29783         }
29784         
29785         this.response = response;
29786         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29787         this.form.afterAction(this, false);
29788     },
29789     
29790     handleResponse : function(response){
29791         if(this.form.errorReader){
29792             var rs = this.form.errorReader.read(response);
29793             var errors = [];
29794             if(rs.records){
29795                 for(var i = 0, len = rs.records.length; i < len; i++) {
29796                     var r = rs.records[i];
29797                     errors[i] = r.data;
29798                 }
29799             }
29800             if(errors.length < 1){
29801                 errors = null;
29802             }
29803             return {
29804                 success : rs.success,
29805                 errors : errors
29806             };
29807         }
29808         var ret = false;
29809         try {
29810             ret = Roo.decode(response.responseText);
29811         } catch (e) {
29812             ret = {
29813                 success: false,
29814                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29815                 errors : []
29816             };
29817         }
29818         return ret;
29819         
29820     }
29821 });
29822
29823
29824 Roo.form.Action.Load = function(form, options){
29825     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29826     this.reader = this.form.reader;
29827 };
29828
29829 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29830     type : 'load',
29831
29832     run : function(){
29833         
29834         Roo.Ajax.request(Roo.apply(
29835                 this.createCallback(), {
29836                     method:this.getMethod(),
29837                     url:this.getUrl(false),
29838                     params:this.getParams()
29839         }));
29840     },
29841
29842     success : function(response){
29843         
29844         var result = this.processResponse(response);
29845         if(result === true || !result.success || !result.data){
29846             this.failureType = Roo.form.Action.LOAD_FAILURE;
29847             this.form.afterAction(this, false);
29848             return;
29849         }
29850         this.form.clearInvalid();
29851         this.form.setValues(result.data);
29852         this.form.afterAction(this, true);
29853     },
29854
29855     handleResponse : function(response){
29856         if(this.form.reader){
29857             var rs = this.form.reader.read(response);
29858             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29859             return {
29860                 success : rs.success,
29861                 data : data
29862             };
29863         }
29864         return Roo.decode(response.responseText);
29865     }
29866 });
29867
29868 Roo.form.Action.ACTION_TYPES = {
29869     'load' : Roo.form.Action.Load,
29870     'submit' : Roo.form.Action.Submit
29871 };/*
29872  * Based on:
29873  * Ext JS Library 1.1.1
29874  * Copyright(c) 2006-2007, Ext JS, LLC.
29875  *
29876  * Originally Released Under LGPL - original licence link has changed is not relivant.
29877  *
29878  * Fork - LGPL
29879  * <script type="text/javascript">
29880  */
29881  
29882 /**
29883  * @class Roo.form.Layout
29884  * @extends Roo.Component
29885  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29886  * @constructor
29887  * @param {Object} config Configuration options
29888  */
29889 Roo.form.Layout = function(config){
29890     var xitems = [];
29891     if (config.items) {
29892         xitems = config.items;
29893         delete config.items;
29894     }
29895     Roo.form.Layout.superclass.constructor.call(this, config);
29896     this.stack = [];
29897     Roo.each(xitems, this.addxtype, this);
29898      
29899 };
29900
29901 Roo.extend(Roo.form.Layout, Roo.Component, {
29902     /**
29903      * @cfg {String/Object} autoCreate
29904      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29905      */
29906     /**
29907      * @cfg {String/Object/Function} style
29908      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29909      * a function which returns such a specification.
29910      */
29911     /**
29912      * @cfg {String} labelAlign
29913      * Valid values are "left," "top" and "right" (defaults to "left")
29914      */
29915     /**
29916      * @cfg {Number} labelWidth
29917      * Fixed width in pixels of all field labels (defaults to undefined)
29918      */
29919     /**
29920      * @cfg {Boolean} clear
29921      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29922      */
29923     clear : true,
29924     /**
29925      * @cfg {String} labelSeparator
29926      * The separator to use after field labels (defaults to ':')
29927      */
29928     labelSeparator : ':',
29929     /**
29930      * @cfg {Boolean} hideLabels
29931      * True to suppress the display of field labels in this layout (defaults to false)
29932      */
29933     hideLabels : false,
29934
29935     // private
29936     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29937     
29938     isLayout : true,
29939     
29940     // private
29941     onRender : function(ct, position){
29942         if(this.el){ // from markup
29943             this.el = Roo.get(this.el);
29944         }else {  // generate
29945             var cfg = this.getAutoCreate();
29946             this.el = ct.createChild(cfg, position);
29947         }
29948         if(this.style){
29949             this.el.applyStyles(this.style);
29950         }
29951         if(this.labelAlign){
29952             this.el.addClass('x-form-label-'+this.labelAlign);
29953         }
29954         if(this.hideLabels){
29955             this.labelStyle = "display:none";
29956             this.elementStyle = "padding-left:0;";
29957         }else{
29958             if(typeof this.labelWidth == 'number'){
29959                 this.labelStyle = "width:"+this.labelWidth+"px;";
29960                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29961             }
29962             if(this.labelAlign == 'top'){
29963                 this.labelStyle = "width:auto;";
29964                 this.elementStyle = "padding-left:0;";
29965             }
29966         }
29967         var stack = this.stack;
29968         var slen = stack.length;
29969         if(slen > 0){
29970             if(!this.fieldTpl){
29971                 var t = new Roo.Template(
29972                     '<div class="x-form-item {5}">',
29973                         '<label for="{0}" style="{2}">{1}{4}</label>',
29974                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29975                         '</div>',
29976                     '</div><div class="x-form-clear-left"></div>'
29977                 );
29978                 t.disableFormats = true;
29979                 t.compile();
29980                 Roo.form.Layout.prototype.fieldTpl = t;
29981             }
29982             for(var i = 0; i < slen; i++) {
29983                 if(stack[i].isFormField){
29984                     this.renderField(stack[i]);
29985                 }else{
29986                     this.renderComponent(stack[i]);
29987                 }
29988             }
29989         }
29990         if(this.clear){
29991             this.el.createChild({cls:'x-form-clear'});
29992         }
29993     },
29994
29995     // private
29996     renderField : function(f){
29997         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29998                f.id, //0
29999                f.fieldLabel, //1
30000                f.labelStyle||this.labelStyle||'', //2
30001                this.elementStyle||'', //3
30002                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30003                f.itemCls||this.itemCls||''  //5
30004        ], true).getPrevSibling());
30005     },
30006
30007     // private
30008     renderComponent : function(c){
30009         c.render(c.isLayout ? this.el : this.el.createChild());    
30010     },
30011     /**
30012      * Adds a object form elements (using the xtype property as the factory method.)
30013      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30014      * @param {Object} config 
30015      */
30016     addxtype : function(o)
30017     {
30018         // create the lement.
30019         o.form = this.form;
30020         var fe = Roo.factory(o, Roo.form);
30021         this.form.allItems.push(fe);
30022         this.stack.push(fe);
30023         
30024         if (fe.isFormField) {
30025             this.form.items.add(fe);
30026         }
30027          
30028         return fe;
30029     }
30030 });
30031
30032 /**
30033  * @class Roo.form.Column
30034  * @extends Roo.form.Layout
30035  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30036  * @constructor
30037  * @param {Object} config Configuration options
30038  */
30039 Roo.form.Column = function(config){
30040     Roo.form.Column.superclass.constructor.call(this, config);
30041 };
30042
30043 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30044     /**
30045      * @cfg {Number/String} width
30046      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30047      */
30048     /**
30049      * @cfg {String/Object} autoCreate
30050      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30051      */
30052
30053     // private
30054     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30055
30056     // private
30057     onRender : function(ct, position){
30058         Roo.form.Column.superclass.onRender.call(this, ct, position);
30059         if(this.width){
30060             this.el.setWidth(this.width);
30061         }
30062     }
30063 });
30064
30065
30066 /**
30067  * @class Roo.form.Row
30068  * @extends Roo.form.Layout
30069  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30070  * @constructor
30071  * @param {Object} config Configuration options
30072  */
30073
30074  
30075 Roo.form.Row = function(config){
30076     Roo.form.Row.superclass.constructor.call(this, config);
30077 };
30078  
30079 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30080       /**
30081      * @cfg {Number/String} width
30082      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30083      */
30084     /**
30085      * @cfg {Number/String} height
30086      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30087      */
30088     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30089     
30090     padWidth : 20,
30091     // private
30092     onRender : function(ct, position){
30093         //console.log('row render');
30094         if(!this.rowTpl){
30095             var t = new Roo.Template(
30096                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30097                     '<label for="{0}" style="{2}">{1}{4}</label>',
30098                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30099                     '</div>',
30100                 '</div>'
30101             );
30102             t.disableFormats = true;
30103             t.compile();
30104             Roo.form.Layout.prototype.rowTpl = t;
30105         }
30106         this.fieldTpl = this.rowTpl;
30107         
30108         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30109         var labelWidth = 100;
30110         
30111         if ((this.labelAlign != 'top')) {
30112             if (typeof this.labelWidth == 'number') {
30113                 labelWidth = this.labelWidth
30114             }
30115             this.padWidth =  20 + labelWidth;
30116             
30117         }
30118         
30119         Roo.form.Column.superclass.onRender.call(this, ct, position);
30120         if(this.width){
30121             this.el.setWidth(this.width);
30122         }
30123         if(this.height){
30124             this.el.setHeight(this.height);
30125         }
30126     },
30127     
30128     // private
30129     renderField : function(f){
30130         f.fieldEl = this.fieldTpl.append(this.el, [
30131                f.id, f.fieldLabel,
30132                f.labelStyle||this.labelStyle||'',
30133                this.elementStyle||'',
30134                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30135                f.itemCls||this.itemCls||'',
30136                f.width ? f.width + this.padWidth : 160 + this.padWidth
30137        ],true);
30138     }
30139 });
30140  
30141
30142 /**
30143  * @class Roo.form.FieldSet
30144  * @extends Roo.form.Layout
30145  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30146  * @constructor
30147  * @param {Object} config Configuration options
30148  */
30149 Roo.form.FieldSet = function(config){
30150     Roo.form.FieldSet.superclass.constructor.call(this, config);
30151 };
30152
30153 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30154     /**
30155      * @cfg {String} legend
30156      * The text to display as the legend for the FieldSet (defaults to '')
30157      */
30158     /**
30159      * @cfg {String/Object} autoCreate
30160      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30161      */
30162
30163     // private
30164     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30165
30166     // private
30167     onRender : function(ct, position){
30168         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30169         if(this.legend){
30170             this.setLegend(this.legend);
30171         }
30172     },
30173
30174     // private
30175     setLegend : function(text){
30176         if(this.rendered){
30177             this.el.child('legend').update(text);
30178         }
30179     }
30180 });/*
30181  * Based on:
30182  * Ext JS Library 1.1.1
30183  * Copyright(c) 2006-2007, Ext JS, LLC.
30184  *
30185  * Originally Released Under LGPL - original licence link has changed is not relivant.
30186  *
30187  * Fork - LGPL
30188  * <script type="text/javascript">
30189  */
30190 /**
30191  * @class Roo.form.VTypes
30192  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30193  * @singleton
30194  */
30195 Roo.form.VTypes = function(){
30196     // closure these in so they are only created once.
30197     var alpha = /^[a-zA-Z_]+$/;
30198     var alphanum = /^[a-zA-Z0-9_]+$/;
30199     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
30200     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30201
30202     // All these messages and functions are configurable
30203     return {
30204         /**
30205          * The function used to validate email addresses
30206          * @param {String} value The email address
30207          */
30208         'email' : function(v){
30209             return email.test(v);
30210         },
30211         /**
30212          * The error text to display when the email validation function returns false
30213          * @type String
30214          */
30215         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30216         /**
30217          * The keystroke filter mask to be applied on email input
30218          * @type RegExp
30219          */
30220         'emailMask' : /[a-z0-9_\.\-@]/i,
30221
30222         /**
30223          * The function used to validate URLs
30224          * @param {String} value The URL
30225          */
30226         'url' : function(v){
30227             return url.test(v);
30228         },
30229         /**
30230          * The error text to display when the url validation function returns false
30231          * @type String
30232          */
30233         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30234         
30235         /**
30236          * The function used to validate alpha values
30237          * @param {String} value The value
30238          */
30239         'alpha' : function(v){
30240             return alpha.test(v);
30241         },
30242         /**
30243          * The error text to display when the alpha validation function returns false
30244          * @type String
30245          */
30246         'alphaText' : 'This field should only contain letters and _',
30247         /**
30248          * The keystroke filter mask to be applied on alpha input
30249          * @type RegExp
30250          */
30251         'alphaMask' : /[a-z_]/i,
30252
30253         /**
30254          * The function used to validate alphanumeric values
30255          * @param {String} value The value
30256          */
30257         'alphanum' : function(v){
30258             return alphanum.test(v);
30259         },
30260         /**
30261          * The error text to display when the alphanumeric validation function returns false
30262          * @type String
30263          */
30264         'alphanumText' : 'This field should only contain letters, numbers and _',
30265         /**
30266          * The keystroke filter mask to be applied on alphanumeric input
30267          * @type RegExp
30268          */
30269         'alphanumMask' : /[a-z0-9_]/i
30270     };
30271 }();//<script type="text/javascript">
30272
30273 /**
30274  * @class Roo.form.FCKeditor
30275  * @extends Roo.form.TextArea
30276  * Wrapper around the FCKEditor http://www.fckeditor.net
30277  * @constructor
30278  * Creates a new FCKeditor
30279  * @param {Object} config Configuration options
30280  */
30281 Roo.form.FCKeditor = function(config){
30282     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30283     this.addEvents({
30284          /**
30285          * @event editorinit
30286          * Fired when the editor is initialized - you can add extra handlers here..
30287          * @param {FCKeditor} this
30288          * @param {Object} the FCK object.
30289          */
30290         editorinit : true
30291     });
30292     
30293     
30294 };
30295 Roo.form.FCKeditor.editors = { };
30296 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30297 {
30298     //defaultAutoCreate : {
30299     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30300     //},
30301     // private
30302     /**
30303      * @cfg {Object} fck options - see fck manual for details.
30304      */
30305     fckconfig : false,
30306     
30307     /**
30308      * @cfg {Object} fck toolbar set (Basic or Default)
30309      */
30310     toolbarSet : 'Basic',
30311     /**
30312      * @cfg {Object} fck BasePath
30313      */ 
30314     basePath : '/fckeditor/',
30315     
30316     
30317     frame : false,
30318     
30319     value : '',
30320     
30321    
30322     onRender : function(ct, position)
30323     {
30324         if(!this.el){
30325             this.defaultAutoCreate = {
30326                 tag: "textarea",
30327                 style:"width:300px;height:60px;",
30328                 autocomplete: "new-password"
30329             };
30330         }
30331         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30332         /*
30333         if(this.grow){
30334             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30335             if(this.preventScrollbars){
30336                 this.el.setStyle("overflow", "hidden");
30337             }
30338             this.el.setHeight(this.growMin);
30339         }
30340         */
30341         //console.log('onrender' + this.getId() );
30342         Roo.form.FCKeditor.editors[this.getId()] = this;
30343          
30344
30345         this.replaceTextarea() ;
30346         
30347     },
30348     
30349     getEditor : function() {
30350         return this.fckEditor;
30351     },
30352     /**
30353      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30354      * @param {Mixed} value The value to set
30355      */
30356     
30357     
30358     setValue : function(value)
30359     {
30360         //console.log('setValue: ' + value);
30361         
30362         if(typeof(value) == 'undefined') { // not sure why this is happending...
30363             return;
30364         }
30365         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30366         
30367         //if(!this.el || !this.getEditor()) {
30368         //    this.value = value;
30369             //this.setValue.defer(100,this,[value]);    
30370         //    return;
30371         //} 
30372         
30373         if(!this.getEditor()) {
30374             return;
30375         }
30376         
30377         this.getEditor().SetData(value);
30378         
30379         //
30380
30381     },
30382
30383     /**
30384      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30385      * @return {Mixed} value The field value
30386      */
30387     getValue : function()
30388     {
30389         
30390         if (this.frame && this.frame.dom.style.display == 'none') {
30391             return Roo.form.FCKeditor.superclass.getValue.call(this);
30392         }
30393         
30394         if(!this.el || !this.getEditor()) {
30395            
30396            // this.getValue.defer(100,this); 
30397             return this.value;
30398         }
30399        
30400         
30401         var value=this.getEditor().GetData();
30402         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30403         return Roo.form.FCKeditor.superclass.getValue.call(this);
30404         
30405
30406     },
30407
30408     /**
30409      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30410      * @return {Mixed} value The field value
30411      */
30412     getRawValue : function()
30413     {
30414         if (this.frame && this.frame.dom.style.display == 'none') {
30415             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30416         }
30417         
30418         if(!this.el || !this.getEditor()) {
30419             //this.getRawValue.defer(100,this); 
30420             return this.value;
30421             return;
30422         }
30423         
30424         
30425         
30426         var value=this.getEditor().GetData();
30427         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30428         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30429          
30430     },
30431     
30432     setSize : function(w,h) {
30433         
30434         
30435         
30436         //if (this.frame && this.frame.dom.style.display == 'none') {
30437         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30438         //    return;
30439         //}
30440         //if(!this.el || !this.getEditor()) {
30441         //    this.setSize.defer(100,this, [w,h]); 
30442         //    return;
30443         //}
30444         
30445         
30446         
30447         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30448         
30449         this.frame.dom.setAttribute('width', w);
30450         this.frame.dom.setAttribute('height', h);
30451         this.frame.setSize(w,h);
30452         
30453     },
30454     
30455     toggleSourceEdit : function(value) {
30456         
30457       
30458          
30459         this.el.dom.style.display = value ? '' : 'none';
30460         this.frame.dom.style.display = value ?  'none' : '';
30461         
30462     },
30463     
30464     
30465     focus: function(tag)
30466     {
30467         if (this.frame.dom.style.display == 'none') {
30468             return Roo.form.FCKeditor.superclass.focus.call(this);
30469         }
30470         if(!this.el || !this.getEditor()) {
30471             this.focus.defer(100,this, [tag]); 
30472             return;
30473         }
30474         
30475         
30476         
30477         
30478         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30479         this.getEditor().Focus();
30480         if (tgs.length) {
30481             if (!this.getEditor().Selection.GetSelection()) {
30482                 this.focus.defer(100,this, [tag]); 
30483                 return;
30484             }
30485             
30486             
30487             var r = this.getEditor().EditorDocument.createRange();
30488             r.setStart(tgs[0],0);
30489             r.setEnd(tgs[0],0);
30490             this.getEditor().Selection.GetSelection().removeAllRanges();
30491             this.getEditor().Selection.GetSelection().addRange(r);
30492             this.getEditor().Focus();
30493         }
30494         
30495     },
30496     
30497     
30498     
30499     replaceTextarea : function()
30500     {
30501         if ( document.getElementById( this.getId() + '___Frame' ) )
30502             return ;
30503         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30504         //{
30505             // We must check the elements firstly using the Id and then the name.
30506         var oTextarea = document.getElementById( this.getId() );
30507         
30508         var colElementsByName = document.getElementsByName( this.getId() ) ;
30509          
30510         oTextarea.style.display = 'none' ;
30511
30512         if ( oTextarea.tabIndex ) {            
30513             this.TabIndex = oTextarea.tabIndex ;
30514         }
30515         
30516         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30517         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30518         this.frame = Roo.get(this.getId() + '___Frame')
30519     },
30520     
30521     _getConfigHtml : function()
30522     {
30523         var sConfig = '' ;
30524
30525         for ( var o in this.fckconfig ) {
30526             sConfig += sConfig.length > 0  ? '&amp;' : '';
30527             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30528         }
30529
30530         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30531     },
30532     
30533     
30534     _getIFrameHtml : function()
30535     {
30536         var sFile = 'fckeditor.html' ;
30537         /* no idea what this is about..
30538         try
30539         {
30540             if ( (/fcksource=true/i).test( window.top.location.search ) )
30541                 sFile = 'fckeditor.original.html' ;
30542         }
30543         catch (e) { 
30544         */
30545
30546         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30547         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30548         
30549         
30550         var html = '<iframe id="' + this.getId() +
30551             '___Frame" src="' + sLink +
30552             '" width="' + this.width +
30553             '" height="' + this.height + '"' +
30554             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30555             ' frameborder="0" scrolling="no"></iframe>' ;
30556
30557         return html ;
30558     },
30559     
30560     _insertHtmlBefore : function( html, element )
30561     {
30562         if ( element.insertAdjacentHTML )       {
30563             // IE
30564             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30565         } else { // Gecko
30566             var oRange = document.createRange() ;
30567             oRange.setStartBefore( element ) ;
30568             var oFragment = oRange.createContextualFragment( html );
30569             element.parentNode.insertBefore( oFragment, element ) ;
30570         }
30571     }
30572     
30573     
30574   
30575     
30576     
30577     
30578     
30579
30580 });
30581
30582 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30583
30584 function FCKeditor_OnComplete(editorInstance){
30585     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30586     f.fckEditor = editorInstance;
30587     //console.log("loaded");
30588     f.fireEvent('editorinit', f, editorInstance);
30589
30590   
30591
30592  
30593
30594
30595
30596
30597
30598
30599
30600
30601
30602
30603
30604
30605
30606
30607
30608 //<script type="text/javascript">
30609 /**
30610  * @class Roo.form.GridField
30611  * @extends Roo.form.Field
30612  * Embed a grid (or editable grid into a form)
30613  * STATUS ALPHA
30614  * 
30615  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30616  * it needs 
30617  * xgrid.store = Roo.data.Store
30618  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30619  * xgrid.store.reader = Roo.data.JsonReader 
30620  * 
30621  * 
30622  * @constructor
30623  * Creates a new GridField
30624  * @param {Object} config Configuration options
30625  */
30626 Roo.form.GridField = function(config){
30627     Roo.form.GridField.superclass.constructor.call(this, config);
30628      
30629 };
30630
30631 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30632     /**
30633      * @cfg {Number} width  - used to restrict width of grid..
30634      */
30635     width : 100,
30636     /**
30637      * @cfg {Number} height - used to restrict height of grid..
30638      */
30639     height : 50,
30640      /**
30641      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30642          * 
30643          *}
30644      */
30645     xgrid : false, 
30646     /**
30647      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30648      * {tag: "input", type: "checkbox", autocomplete: "off"})
30649      */
30650    // defaultAutoCreate : { tag: 'div' },
30651     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30652     /**
30653      * @cfg {String} addTitle Text to include for adding a title.
30654      */
30655     addTitle : false,
30656     //
30657     onResize : function(){
30658         Roo.form.Field.superclass.onResize.apply(this, arguments);
30659     },
30660
30661     initEvents : function(){
30662         // Roo.form.Checkbox.superclass.initEvents.call(this);
30663         // has no events...
30664        
30665     },
30666
30667
30668     getResizeEl : function(){
30669         return this.wrap;
30670     },
30671
30672     getPositionEl : function(){
30673         return this.wrap;
30674     },
30675
30676     // private
30677     onRender : function(ct, position){
30678         
30679         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30680         var style = this.style;
30681         delete this.style;
30682         
30683         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30684         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30685         this.viewEl = this.wrap.createChild({ tag: 'div' });
30686         if (style) {
30687             this.viewEl.applyStyles(style);
30688         }
30689         if (this.width) {
30690             this.viewEl.setWidth(this.width);
30691         }
30692         if (this.height) {
30693             this.viewEl.setHeight(this.height);
30694         }
30695         //if(this.inputValue !== undefined){
30696         //this.setValue(this.value);
30697         
30698         
30699         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30700         
30701         
30702         this.grid.render();
30703         this.grid.getDataSource().on('remove', this.refreshValue, this);
30704         this.grid.getDataSource().on('update', this.refreshValue, this);
30705         this.grid.on('afteredit', this.refreshValue, this);
30706  
30707     },
30708      
30709     
30710     /**
30711      * Sets the value of the item. 
30712      * @param {String} either an object  or a string..
30713      */
30714     setValue : function(v){
30715         //this.value = v;
30716         v = v || []; // empty set..
30717         // this does not seem smart - it really only affects memoryproxy grids..
30718         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30719             var ds = this.grid.getDataSource();
30720             // assumes a json reader..
30721             var data = {}
30722             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30723             ds.loadData( data);
30724         }
30725         // clear selection so it does not get stale.
30726         if (this.grid.sm) { 
30727             this.grid.sm.clearSelections();
30728         }
30729         
30730         Roo.form.GridField.superclass.setValue.call(this, v);
30731         this.refreshValue();
30732         // should load data in the grid really....
30733     },
30734     
30735     // private
30736     refreshValue: function() {
30737          var val = [];
30738         this.grid.getDataSource().each(function(r) {
30739             val.push(r.data);
30740         });
30741         this.el.dom.value = Roo.encode(val);
30742     }
30743     
30744      
30745     
30746     
30747 });/*
30748  * Based on:
30749  * Ext JS Library 1.1.1
30750  * Copyright(c) 2006-2007, Ext JS, LLC.
30751  *
30752  * Originally Released Under LGPL - original licence link has changed is not relivant.
30753  *
30754  * Fork - LGPL
30755  * <script type="text/javascript">
30756  */
30757 /**
30758  * @class Roo.form.DisplayField
30759  * @extends Roo.form.Field
30760  * A generic Field to display non-editable data.
30761  * @constructor
30762  * Creates a new Display Field item.
30763  * @param {Object} config Configuration options
30764  */
30765 Roo.form.DisplayField = function(config){
30766     Roo.form.DisplayField.superclass.constructor.call(this, config);
30767     
30768 };
30769
30770 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30771     inputType:      'hidden',
30772     allowBlank:     true,
30773     readOnly:         true,
30774     
30775  
30776     /**
30777      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30778      */
30779     focusClass : undefined,
30780     /**
30781      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30782      */
30783     fieldClass: 'x-form-field',
30784     
30785      /**
30786      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30787      */
30788     valueRenderer: undefined,
30789     
30790     width: 100,
30791     /**
30792      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30793      * {tag: "input", type: "checkbox", autocomplete: "off"})
30794      */
30795      
30796  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30797
30798     onResize : function(){
30799         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30800         
30801     },
30802
30803     initEvents : function(){
30804         // Roo.form.Checkbox.superclass.initEvents.call(this);
30805         // has no events...
30806        
30807     },
30808
30809
30810     getResizeEl : function(){
30811         return this.wrap;
30812     },
30813
30814     getPositionEl : function(){
30815         return this.wrap;
30816     },
30817
30818     // private
30819     onRender : function(ct, position){
30820         
30821         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30822         //if(this.inputValue !== undefined){
30823         this.wrap = this.el.wrap();
30824         
30825         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30826         
30827         if (this.bodyStyle) {
30828             this.viewEl.applyStyles(this.bodyStyle);
30829         }
30830         //this.viewEl.setStyle('padding', '2px');
30831         
30832         this.setValue(this.value);
30833         
30834     },
30835 /*
30836     // private
30837     initValue : Roo.emptyFn,
30838
30839   */
30840
30841         // private
30842     onClick : function(){
30843         
30844     },
30845
30846     /**
30847      * Sets the checked state of the checkbox.
30848      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30849      */
30850     setValue : function(v){
30851         this.value = v;
30852         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30853         // this might be called before we have a dom element..
30854         if (!this.viewEl) {
30855             return;
30856         }
30857         this.viewEl.dom.innerHTML = html;
30858         Roo.form.DisplayField.superclass.setValue.call(this, v);
30859
30860     }
30861 });/*
30862  * 
30863  * Licence- LGPL
30864  * 
30865  */
30866
30867 /**
30868  * @class Roo.form.DayPicker
30869  * @extends Roo.form.Field
30870  * A Day picker show [M] [T] [W] ....
30871  * @constructor
30872  * Creates a new Day Picker
30873  * @param {Object} config Configuration options
30874  */
30875 Roo.form.DayPicker= function(config){
30876     Roo.form.DayPicker.superclass.constructor.call(this, config);
30877      
30878 };
30879
30880 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30881     /**
30882      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30883      */
30884     focusClass : undefined,
30885     /**
30886      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30887      */
30888     fieldClass: "x-form-field",
30889    
30890     /**
30891      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30892      * {tag: "input", type: "checkbox", autocomplete: "off"})
30893      */
30894     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
30895     
30896    
30897     actionMode : 'viewEl', 
30898     //
30899     // private
30900  
30901     inputType : 'hidden',
30902     
30903      
30904     inputElement: false, // real input element?
30905     basedOn: false, // ????
30906     
30907     isFormField: true, // not sure where this is needed!!!!
30908
30909     onResize : function(){
30910         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30911         if(!this.boxLabel){
30912             this.el.alignTo(this.wrap, 'c-c');
30913         }
30914     },
30915
30916     initEvents : function(){
30917         Roo.form.Checkbox.superclass.initEvents.call(this);
30918         this.el.on("click", this.onClick,  this);
30919         this.el.on("change", this.onClick,  this);
30920     },
30921
30922
30923     getResizeEl : function(){
30924         return this.wrap;
30925     },
30926
30927     getPositionEl : function(){
30928         return this.wrap;
30929     },
30930
30931     
30932     // private
30933     onRender : function(ct, position){
30934         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30935        
30936         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30937         
30938         var r1 = '<table><tr>';
30939         var r2 = '<tr class="x-form-daypick-icons">';
30940         for (var i=0; i < 7; i++) {
30941             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30942             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30943         }
30944         
30945         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30946         viewEl.select('img').on('click', this.onClick, this);
30947         this.viewEl = viewEl;   
30948         
30949         
30950         // this will not work on Chrome!!!
30951         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30952         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30953         
30954         
30955           
30956
30957     },
30958
30959     // private
30960     initValue : Roo.emptyFn,
30961
30962     /**
30963      * Returns the checked state of the checkbox.
30964      * @return {Boolean} True if checked, else false
30965      */
30966     getValue : function(){
30967         return this.el.dom.value;
30968         
30969     },
30970
30971         // private
30972     onClick : function(e){ 
30973         //this.setChecked(!this.checked);
30974         Roo.get(e.target).toggleClass('x-menu-item-checked');
30975         this.refreshValue();
30976         //if(this.el.dom.checked != this.checked){
30977         //    this.setValue(this.el.dom.checked);
30978        // }
30979     },
30980     
30981     // private
30982     refreshValue : function()
30983     {
30984         var val = '';
30985         this.viewEl.select('img',true).each(function(e,i,n)  {
30986             val += e.is(".x-menu-item-checked") ? String(n) : '';
30987         });
30988         this.setValue(val, true);
30989     },
30990
30991     /**
30992      * Sets the checked state of the checkbox.
30993      * On is always based on a string comparison between inputValue and the param.
30994      * @param {Boolean/String} value - the value to set 
30995      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30996      */
30997     setValue : function(v,suppressEvent){
30998         if (!this.el.dom) {
30999             return;
31000         }
31001         var old = this.el.dom.value ;
31002         this.el.dom.value = v;
31003         if (suppressEvent) {
31004             return ;
31005         }
31006          
31007         // update display..
31008         this.viewEl.select('img',true).each(function(e,i,n)  {
31009             
31010             var on = e.is(".x-menu-item-checked");
31011             var newv = v.indexOf(String(n)) > -1;
31012             if (on != newv) {
31013                 e.toggleClass('x-menu-item-checked');
31014             }
31015             
31016         });
31017         
31018         
31019         this.fireEvent('change', this, v, old);
31020         
31021         
31022     },
31023    
31024     // handle setting of hidden value by some other method!!?!?
31025     setFromHidden: function()
31026     {
31027         if(!this.el){
31028             return;
31029         }
31030         //console.log("SET FROM HIDDEN");
31031         //alert('setFrom hidden');
31032         this.setValue(this.el.dom.value);
31033     },
31034     
31035     onDestroy : function()
31036     {
31037         if(this.viewEl){
31038             Roo.get(this.viewEl).remove();
31039         }
31040          
31041         Roo.form.DayPicker.superclass.onDestroy.call(this);
31042     }
31043
31044 });/*
31045  * RooJS Library 1.1.1
31046  * Copyright(c) 2008-2011  Alan Knowles
31047  *
31048  * License - LGPL
31049  */
31050  
31051
31052 /**
31053  * @class Roo.form.ComboCheck
31054  * @extends Roo.form.ComboBox
31055  * A combobox for multiple select items.
31056  *
31057  * FIXME - could do with a reset button..
31058  * 
31059  * @constructor
31060  * Create a new ComboCheck
31061  * @param {Object} config Configuration options
31062  */
31063 Roo.form.ComboCheck = function(config){
31064     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31065     // should verify some data...
31066     // like
31067     // hiddenName = required..
31068     // displayField = required
31069     // valudField == required
31070     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31071     var _t = this;
31072     Roo.each(req, function(e) {
31073         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31074             throw "Roo.form.ComboCheck : missing value for: " + e;
31075         }
31076     });
31077     
31078     
31079 };
31080
31081 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31082      
31083      
31084     editable : false,
31085      
31086     selectedClass: 'x-menu-item-checked', 
31087     
31088     // private
31089     onRender : function(ct, position){
31090         var _t = this;
31091         
31092         
31093         
31094         if(!this.tpl){
31095             var cls = 'x-combo-list';
31096
31097             
31098             this.tpl =  new Roo.Template({
31099                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31100                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31101                    '<span>{' + this.displayField + '}</span>' +
31102                     '</div>' 
31103                 
31104             });
31105         }
31106  
31107         
31108         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31109         this.view.singleSelect = false;
31110         this.view.multiSelect = true;
31111         this.view.toggleSelect = true;
31112         this.pageTb.add(new Roo.Toolbar.Fill(), {
31113             
31114             text: 'Done',
31115             handler: function()
31116             {
31117                 _t.collapse();
31118             }
31119         });
31120     },
31121     
31122     onViewOver : function(e, t){
31123         // do nothing...
31124         return;
31125         
31126     },
31127     
31128     onViewClick : function(doFocus,index){
31129         return;
31130         
31131     },
31132     select: function () {
31133         //Roo.log("SELECT CALLED");
31134     },
31135      
31136     selectByValue : function(xv, scrollIntoView){
31137         var ar = this.getValueArray();
31138         var sels = [];
31139         
31140         Roo.each(ar, function(v) {
31141             if(v === undefined || v === null){
31142                 return;
31143             }
31144             var r = this.findRecord(this.valueField, v);
31145             if(r){
31146                 sels.push(this.store.indexOf(r))
31147                 
31148             }
31149         },this);
31150         this.view.select(sels);
31151         return false;
31152     },
31153     
31154     
31155     
31156     onSelect : function(record, index){
31157        // Roo.log("onselect Called");
31158        // this is only called by the clear button now..
31159         this.view.clearSelections();
31160         this.setValue('[]');
31161         if (this.value != this.valueBefore) {
31162             this.fireEvent('change', this, this.value, this.valueBefore);
31163             this.valueBefore = this.value;
31164         }
31165     },
31166     getValueArray : function()
31167     {
31168         var ar = [] ;
31169         
31170         try {
31171             //Roo.log(this.value);
31172             if (typeof(this.value) == 'undefined') {
31173                 return [];
31174             }
31175             var ar = Roo.decode(this.value);
31176             return  ar instanceof Array ? ar : []; //?? valid?
31177             
31178         } catch(e) {
31179             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31180             return [];
31181         }
31182          
31183     },
31184     expand : function ()
31185     {
31186         
31187         Roo.form.ComboCheck.superclass.expand.call(this);
31188         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31189         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31190         
31191
31192     },
31193     
31194     collapse : function(){
31195         Roo.form.ComboCheck.superclass.collapse.call(this);
31196         var sl = this.view.getSelectedIndexes();
31197         var st = this.store;
31198         var nv = [];
31199         var tv = [];
31200         var r;
31201         Roo.each(sl, function(i) {
31202             r = st.getAt(i);
31203             nv.push(r.get(this.valueField));
31204         },this);
31205         this.setValue(Roo.encode(nv));
31206         if (this.value != this.valueBefore) {
31207
31208             this.fireEvent('change', this, this.value, this.valueBefore);
31209             this.valueBefore = this.value;
31210         }
31211         
31212     },
31213     
31214     setValue : function(v){
31215         // Roo.log(v);
31216         this.value = v;
31217         
31218         var vals = this.getValueArray();
31219         var tv = [];
31220         Roo.each(vals, function(k) {
31221             var r = this.findRecord(this.valueField, k);
31222             if(r){
31223                 tv.push(r.data[this.displayField]);
31224             }else if(this.valueNotFoundText !== undefined){
31225                 tv.push( this.valueNotFoundText );
31226             }
31227         },this);
31228        // Roo.log(tv);
31229         
31230         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31231         this.hiddenField.value = v;
31232         this.value = v;
31233     }
31234     
31235 });/*
31236  * Based on:
31237  * Ext JS Library 1.1.1
31238  * Copyright(c) 2006-2007, Ext JS, LLC.
31239  *
31240  * Originally Released Under LGPL - original licence link has changed is not relivant.
31241  *
31242  * Fork - LGPL
31243  * <script type="text/javascript">
31244  */
31245  
31246 /**
31247  * @class Roo.form.Signature
31248  * @extends Roo.form.Field
31249  * Signature field.  
31250  * @constructor
31251  * 
31252  * @param {Object} config Configuration options
31253  */
31254
31255 Roo.form.Signature = function(config){
31256     Roo.form.Signature.superclass.constructor.call(this, config);
31257     
31258     this.addEvents({// not in used??
31259          /**
31260          * @event confirm
31261          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31262              * @param {Roo.form.Signature} combo This combo box
31263              */
31264         'confirm' : true,
31265         /**
31266          * @event reset
31267          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31268              * @param {Roo.form.ComboBox} combo This combo box
31269              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31270              */
31271         'reset' : true
31272     });
31273 };
31274
31275 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31276     /**
31277      * @cfg {Object} labels Label to use when rendering a form.
31278      * defaults to 
31279      * labels : { 
31280      *      clear : "Clear",
31281      *      confirm : "Confirm"
31282      *  }
31283      */
31284     labels : { 
31285         clear : "Clear",
31286         confirm : "Confirm"
31287     },
31288     /**
31289      * @cfg {Number} width The signature panel width (defaults to 300)
31290      */
31291     width: 300,
31292     /**
31293      * @cfg {Number} height The signature panel height (defaults to 100)
31294      */
31295     height : 100,
31296     /**
31297      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31298      */
31299     allowBlank : false,
31300     
31301     //private
31302     // {Object} signPanel The signature SVG panel element (defaults to {})
31303     signPanel : {},
31304     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31305     isMouseDown : false,
31306     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31307     isConfirmed : false,
31308     // {String} signatureTmp SVG mapping string (defaults to empty string)
31309     signatureTmp : '',
31310     
31311     
31312     defaultAutoCreate : { // modified by initCompnoent..
31313         tag: "input",
31314         type:"hidden"
31315     },
31316
31317     // private
31318     onRender : function(ct, position){
31319         
31320         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31321         
31322         this.wrap = this.el.wrap({
31323             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31324         });
31325         
31326         this.createToolbar(this);
31327         this.signPanel = this.wrap.createChild({
31328                 tag: 'div',
31329                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31330             }, this.el
31331         );
31332             
31333         this.svgID = Roo.id();
31334         this.svgEl = this.signPanel.createChild({
31335               xmlns : 'http://www.w3.org/2000/svg',
31336               tag : 'svg',
31337               id : this.svgID + "-svg",
31338               width: this.width,
31339               height: this.height,
31340               viewBox: '0 0 '+this.width+' '+this.height,
31341               cn : [
31342                 {
31343                     tag: "rect",
31344                     id: this.svgID + "-svg-r",
31345                     width: this.width,
31346                     height: this.height,
31347                     fill: "#ffa"
31348                 },
31349                 {
31350                     tag: "line",
31351                     id: this.svgID + "-svg-l",
31352                     x1: "0", // start
31353                     y1: (this.height*0.8), // start set the line in 80% of height
31354                     x2: this.width, // end
31355                     y2: (this.height*0.8), // end set the line in 80% of height
31356                     'stroke': "#666",
31357                     'stroke-width': "1",
31358                     'stroke-dasharray': "3",
31359                     'shape-rendering': "crispEdges",
31360                     'pointer-events': "none"
31361                 },
31362                 {
31363                     tag: "path",
31364                     id: this.svgID + "-svg-p",
31365                     'stroke': "navy",
31366                     'stroke-width': "3",
31367                     'fill': "none",
31368                     'pointer-events': 'none'
31369                 }
31370               ]
31371         });
31372         this.createSVG();
31373         this.svgBox = this.svgEl.dom.getScreenCTM();
31374     },
31375     createSVG : function(){ 
31376         var svg = this.signPanel;
31377         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31378         var t = this;
31379
31380         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31381         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31382         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31383         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31384         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31385         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31386         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31387         
31388     },
31389     isTouchEvent : function(e){
31390         return e.type.match(/^touch/);
31391     },
31392     getCoords : function (e) {
31393         var pt    = this.svgEl.dom.createSVGPoint();
31394         pt.x = e.clientX; 
31395         pt.y = e.clientY;
31396         if (this.isTouchEvent(e)) {
31397             pt.x =  e.targetTouches[0].clientX 
31398             pt.y = e.targetTouches[0].clientY;
31399         }
31400         var a = this.svgEl.dom.getScreenCTM();
31401         var b = a.inverse();
31402         var mx = pt.matrixTransform(b);
31403         return mx.x + ',' + mx.y;
31404     },
31405     //mouse event headler 
31406     down : function (e) {
31407         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31408         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31409         
31410         this.isMouseDown = true;
31411         
31412         e.preventDefault();
31413     },
31414     move : function (e) {
31415         if (this.isMouseDown) {
31416             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31417             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31418         }
31419         
31420         e.preventDefault();
31421     },
31422     up : function (e) {
31423         this.isMouseDown = false;
31424         var sp = this.signatureTmp.split(' ');
31425         
31426         if(sp.length > 1){
31427             if(!sp[sp.length-2].match(/^L/)){
31428                 sp.pop();
31429                 sp.pop();
31430                 sp.push("");
31431                 this.signatureTmp = sp.join(" ");
31432             }
31433         }
31434         if(this.getValue() != this.signatureTmp){
31435             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31436             this.isConfirmed = false;
31437         }
31438         e.preventDefault();
31439     },
31440     
31441     /**
31442      * Protected method that will not generally be called directly. It
31443      * is called when the editor creates its toolbar. Override this method if you need to
31444      * add custom toolbar buttons.
31445      * @param {HtmlEditor} editor
31446      */
31447     createToolbar : function(editor){
31448          function btn(id, toggle, handler){
31449             var xid = fid + '-'+ id ;
31450             return {
31451                 id : xid,
31452                 cmd : id,
31453                 cls : 'x-btn-icon x-edit-'+id,
31454                 enableToggle:toggle !== false,
31455                 scope: editor, // was editor...
31456                 handler:handler||editor.relayBtnCmd,
31457                 clickEvent:'mousedown',
31458                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31459                 tabIndex:-1
31460             };
31461         }
31462         
31463         
31464         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31465         this.tb = tb;
31466         this.tb.add(
31467            {
31468                 cls : ' x-signature-btn x-signature-'+id,
31469                 scope: editor, // was editor...
31470                 handler: this.reset,
31471                 clickEvent:'mousedown',
31472                 text: this.labels.clear
31473             },
31474             {
31475                  xtype : 'Fill',
31476                  xns: Roo.Toolbar
31477             }, 
31478             {
31479                 cls : '  x-signature-btn x-signature-'+id,
31480                 scope: editor, // was editor...
31481                 handler: this.confirmHandler,
31482                 clickEvent:'mousedown',
31483                 text: this.labels.confirm
31484             }
31485         );
31486     
31487     },
31488     //public
31489     /**
31490      * when user is clicked confirm then show this image.....
31491      * 
31492      * @return {String} Image Data URI
31493      */
31494     getImageDataURI : function(){
31495         var svg = this.svgEl.dom.parentNode.innerHTML;
31496         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31497         return src; 
31498     },
31499     /**
31500      * 
31501      * @return {Boolean} this.isConfirmed
31502      */
31503     getConfirmed : function(){
31504         return this.isConfirmed;
31505     },
31506     /**
31507      * 
31508      * @return {Number} this.width
31509      */
31510     getWidth : function(){
31511         return this.width;
31512     },
31513     /**
31514      * 
31515      * @return {Number} this.height
31516      */
31517     getHeight : function(){
31518         return this.height;
31519     },
31520     // private
31521     getSignature : function(){
31522         return this.signatureTmp;
31523     },
31524     // private
31525     reset : function(){
31526         this.signatureTmp = '';
31527         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31528         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31529         this.isConfirmed = false;
31530         Roo.form.Signature.superclass.reset.call(this);
31531     },
31532     setSignature : function(s){
31533         this.signatureTmp = s;
31534         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31535         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31536         this.setValue(s);
31537         this.isConfirmed = false;
31538         Roo.form.Signature.superclass.reset.call(this);
31539     }, 
31540     test : function(){
31541 //        Roo.log(this.signPanel.dom.contentWindow.up())
31542     },
31543     //private
31544     setConfirmed : function(){
31545         
31546         
31547         
31548 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31549     },
31550     // private
31551     confirmHandler : function(){
31552         if(!this.getSignature()){
31553             return;
31554         }
31555         
31556         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31557         this.setValue(this.getSignature());
31558         this.isConfirmed = true;
31559         
31560         this.fireEvent('confirm', this);
31561     },
31562     // private
31563     // Subclasses should provide the validation implementation by overriding this
31564     validateValue : function(value){
31565         if(this.allowBlank){
31566             return true;
31567         }
31568         
31569         if(this.isConfirmed){
31570             return true;
31571         }
31572         return false;
31573     }
31574 });/*
31575  * Based on:
31576  * Ext JS Library 1.1.1
31577  * Copyright(c) 2006-2007, Ext JS, LLC.
31578  *
31579  * Originally Released Under LGPL - original licence link has changed is not relivant.
31580  *
31581  * Fork - LGPL
31582  * <script type="text/javascript">
31583  */
31584  
31585
31586 /**
31587  * @class Roo.form.ComboBox
31588  * @extends Roo.form.TriggerField
31589  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31590  * @constructor
31591  * Create a new ComboBox.
31592  * @param {Object} config Configuration options
31593  */
31594 Roo.form.Select = function(config){
31595     Roo.form.Select.superclass.constructor.call(this, config);
31596      
31597 };
31598
31599 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31600     /**
31601      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31602      */
31603     /**
31604      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31605      * rendering into an Roo.Editor, defaults to false)
31606      */
31607     /**
31608      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31609      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31610      */
31611     /**
31612      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31613      */
31614     /**
31615      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31616      * the dropdown list (defaults to undefined, with no header element)
31617      */
31618
31619      /**
31620      * @cfg {String/Roo.Template} tpl The template to use to render the output
31621      */
31622      
31623     // private
31624     defaultAutoCreate : {tag: "select"  },
31625     /**
31626      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31627      */
31628     listWidth: undefined,
31629     /**
31630      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31631      * mode = 'remote' or 'text' if mode = 'local')
31632      */
31633     displayField: undefined,
31634     /**
31635      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31636      * mode = 'remote' or 'value' if mode = 'local'). 
31637      * Note: use of a valueField requires the user make a selection
31638      * in order for a value to be mapped.
31639      */
31640     valueField: undefined,
31641     
31642     
31643     /**
31644      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31645      * field's data value (defaults to the underlying DOM element's name)
31646      */
31647     hiddenName: undefined,
31648     /**
31649      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31650      */
31651     listClass: '',
31652     /**
31653      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31654      */
31655     selectedClass: 'x-combo-selected',
31656     /**
31657      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31658      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31659      * which displays a downward arrow icon).
31660      */
31661     triggerClass : 'x-form-arrow-trigger',
31662     /**
31663      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31664      */
31665     shadow:'sides',
31666     /**
31667      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31668      * anchor positions (defaults to 'tl-bl')
31669      */
31670     listAlign: 'tl-bl?',
31671     /**
31672      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31673      */
31674     maxHeight: 300,
31675     /**
31676      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31677      * query specified by the allQuery config option (defaults to 'query')
31678      */
31679     triggerAction: 'query',
31680     /**
31681      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31682      * (defaults to 4, does not apply if editable = false)
31683      */
31684     minChars : 4,
31685     /**
31686      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31687      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31688      */
31689     typeAhead: false,
31690     /**
31691      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31692      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31693      */
31694     queryDelay: 500,
31695     /**
31696      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31697      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31698      */
31699     pageSize: 0,
31700     /**
31701      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31702      * when editable = true (defaults to false)
31703      */
31704     selectOnFocus:false,
31705     /**
31706      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31707      */
31708     queryParam: 'query',
31709     /**
31710      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31711      * when mode = 'remote' (defaults to 'Loading...')
31712      */
31713     loadingText: 'Loading...',
31714     /**
31715      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31716      */
31717     resizable: false,
31718     /**
31719      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31720      */
31721     handleHeight : 8,
31722     /**
31723      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31724      * traditional select (defaults to true)
31725      */
31726     editable: true,
31727     /**
31728      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31729      */
31730     allQuery: '',
31731     /**
31732      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31733      */
31734     mode: 'remote',
31735     /**
31736      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31737      * listWidth has a higher value)
31738      */
31739     minListWidth : 70,
31740     /**
31741      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31742      * allow the user to set arbitrary text into the field (defaults to false)
31743      */
31744     forceSelection:false,
31745     /**
31746      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31747      * if typeAhead = true (defaults to 250)
31748      */
31749     typeAheadDelay : 250,
31750     /**
31751      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31752      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31753      */
31754     valueNotFoundText : undefined,
31755     
31756     /**
31757      * @cfg {String} defaultValue The value displayed after loading the store.
31758      */
31759     defaultValue: '',
31760     
31761     /**
31762      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31763      */
31764     blockFocus : false,
31765     
31766     /**
31767      * @cfg {Boolean} disableClear Disable showing of clear button.
31768      */
31769     disableClear : false,
31770     /**
31771      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31772      */
31773     alwaysQuery : false,
31774     
31775     //private
31776     addicon : false,
31777     editicon: false,
31778     
31779     // element that contains real text value.. (when hidden is used..)
31780      
31781     // private
31782     onRender : function(ct, position){
31783         Roo.form.Field.prototype.onRender.call(this, ct, position);
31784         
31785         if(this.store){
31786             this.store.on('beforeload', this.onBeforeLoad, this);
31787             this.store.on('load', this.onLoad, this);
31788             this.store.on('loadexception', this.onLoadException, this);
31789             this.store.load({});
31790         }
31791         
31792         
31793         
31794     },
31795
31796     // private
31797     initEvents : function(){
31798         //Roo.form.ComboBox.superclass.initEvents.call(this);
31799  
31800     },
31801
31802     onDestroy : function(){
31803        
31804         if(this.store){
31805             this.store.un('beforeload', this.onBeforeLoad, this);
31806             this.store.un('load', this.onLoad, this);
31807             this.store.un('loadexception', this.onLoadException, this);
31808         }
31809         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31810     },
31811
31812     // private
31813     fireKey : function(e){
31814         if(e.isNavKeyPress() && !this.list.isVisible()){
31815             this.fireEvent("specialkey", this, e);
31816         }
31817     },
31818
31819     // private
31820     onResize: function(w, h){
31821         
31822         return; 
31823     
31824         
31825     },
31826
31827     /**
31828      * Allow or prevent the user from directly editing the field text.  If false is passed,
31829      * the user will only be able to select from the items defined in the dropdown list.  This method
31830      * is the runtime equivalent of setting the 'editable' config option at config time.
31831      * @param {Boolean} value True to allow the user to directly edit the field text
31832      */
31833     setEditable : function(value){
31834          
31835     },
31836
31837     // private
31838     onBeforeLoad : function(){
31839         
31840         Roo.log("Select before load");
31841         return;
31842     
31843         this.innerList.update(this.loadingText ?
31844                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31845         //this.restrictHeight();
31846         this.selectedIndex = -1;
31847     },
31848
31849     // private
31850     onLoad : function(){
31851
31852     
31853         var dom = this.el.dom;
31854         dom.innerHTML = '';
31855          var od = dom.ownerDocument;
31856          
31857         if (this.emptyText) {
31858             var op = od.createElement('option');
31859             op.setAttribute('value', '');
31860             op.innerHTML = String.format('{0}', this.emptyText);
31861             dom.appendChild(op);
31862         }
31863         if(this.store.getCount() > 0){
31864            
31865             var vf = this.valueField;
31866             var df = this.displayField;
31867             this.store.data.each(function(r) {
31868                 // which colmsn to use... testing - cdoe / title..
31869                 var op = od.createElement('option');
31870                 op.setAttribute('value', r.data[vf]);
31871                 op.innerHTML = String.format('{0}', r.data[df]);
31872                 dom.appendChild(op);
31873             });
31874             if (typeof(this.defaultValue != 'undefined')) {
31875                 this.setValue(this.defaultValue);
31876             }
31877             
31878              
31879         }else{
31880             //this.onEmptyResults();
31881         }
31882         //this.el.focus();
31883     },
31884     // private
31885     onLoadException : function()
31886     {
31887         dom.innerHTML = '';
31888             
31889         Roo.log("Select on load exception");
31890         return;
31891     
31892         this.collapse();
31893         Roo.log(this.store.reader.jsonData);
31894         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31895             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31896         }
31897         
31898         
31899     },
31900     // private
31901     onTypeAhead : function(){
31902          
31903     },
31904
31905     // private
31906     onSelect : function(record, index){
31907         Roo.log('on select?');
31908         return;
31909         if(this.fireEvent('beforeselect', this, record, index) !== false){
31910             this.setFromData(index > -1 ? record.data : false);
31911             this.collapse();
31912             this.fireEvent('select', this, record, index);
31913         }
31914     },
31915
31916     /**
31917      * Returns the currently selected field value or empty string if no value is set.
31918      * @return {String} value The selected value
31919      */
31920     getValue : function(){
31921         var dom = this.el.dom;
31922         this.value = dom.options[dom.selectedIndex].value;
31923         return this.value;
31924         
31925     },
31926
31927     /**
31928      * Clears any text/value currently set in the field
31929      */
31930     clearValue : function(){
31931         this.value = '';
31932         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31933         
31934     },
31935
31936     /**
31937      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31938      * will be displayed in the field.  If the value does not match the data value of an existing item,
31939      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31940      * Otherwise the field will be blank (although the value will still be set).
31941      * @param {String} value The value to match
31942      */
31943     setValue : function(v){
31944         var d = this.el.dom;
31945         for (var i =0; i < d.options.length;i++) {
31946             if (v == d.options[i].value) {
31947                 d.selectedIndex = i;
31948                 this.value = v;
31949                 return;
31950             }
31951         }
31952         this.clearValue();
31953     },
31954     /**
31955      * @property {Object} the last set data for the element
31956      */
31957     
31958     lastData : false,
31959     /**
31960      * Sets the value of the field based on a object which is related to the record format for the store.
31961      * @param {Object} value the value to set as. or false on reset?
31962      */
31963     setFromData : function(o){
31964         Roo.log('setfrom data?');
31965          
31966         
31967         
31968     },
31969     // private
31970     reset : function(){
31971         this.clearValue();
31972     },
31973     // private
31974     findRecord : function(prop, value){
31975         
31976         return false;
31977     
31978         var record;
31979         if(this.store.getCount() > 0){
31980             this.store.each(function(r){
31981                 if(r.data[prop] == value){
31982                     record = r;
31983                     return false;
31984                 }
31985                 return true;
31986             });
31987         }
31988         return record;
31989     },
31990     
31991     getName: function()
31992     {
31993         // returns hidden if it's set..
31994         if (!this.rendered) {return ''};
31995         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31996         
31997     },
31998      
31999
32000     
32001
32002     // private
32003     onEmptyResults : function(){
32004         Roo.log('empty results');
32005         //this.collapse();
32006     },
32007
32008     /**
32009      * Returns true if the dropdown list is expanded, else false.
32010      */
32011     isExpanded : function(){
32012         return false;
32013     },
32014
32015     /**
32016      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32017      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32018      * @param {String} value The data value of the item to select
32019      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32020      * selected item if it is not currently in view (defaults to true)
32021      * @return {Boolean} True if the value matched an item in the list, else false
32022      */
32023     selectByValue : function(v, scrollIntoView){
32024         Roo.log('select By Value');
32025         return false;
32026     
32027         if(v !== undefined && v !== null){
32028             var r = this.findRecord(this.valueField || this.displayField, v);
32029             if(r){
32030                 this.select(this.store.indexOf(r), scrollIntoView);
32031                 return true;
32032             }
32033         }
32034         return false;
32035     },
32036
32037     /**
32038      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32039      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32040      * @param {Number} index The zero-based index of the list item to select
32041      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32042      * selected item if it is not currently in view (defaults to true)
32043      */
32044     select : function(index, scrollIntoView){
32045         Roo.log('select ');
32046         return  ;
32047         
32048         this.selectedIndex = index;
32049         this.view.select(index);
32050         if(scrollIntoView !== false){
32051             var el = this.view.getNode(index);
32052             if(el){
32053                 this.innerList.scrollChildIntoView(el, false);
32054             }
32055         }
32056     },
32057
32058       
32059
32060     // private
32061     validateBlur : function(){
32062         
32063         return;
32064         
32065     },
32066
32067     // private
32068     initQuery : function(){
32069         this.doQuery(this.getRawValue());
32070     },
32071
32072     // private
32073     doForce : function(){
32074         if(this.el.dom.value.length > 0){
32075             this.el.dom.value =
32076                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32077              
32078         }
32079     },
32080
32081     /**
32082      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32083      * query allowing the query action to be canceled if needed.
32084      * @param {String} query The SQL query to execute
32085      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32086      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32087      * saved in the current store (defaults to false)
32088      */
32089     doQuery : function(q, forceAll){
32090         
32091         Roo.log('doQuery?');
32092         if(q === undefined || q === null){
32093             q = '';
32094         }
32095         var qe = {
32096             query: q,
32097             forceAll: forceAll,
32098             combo: this,
32099             cancel:false
32100         };
32101         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32102             return false;
32103         }
32104         q = qe.query;
32105         forceAll = qe.forceAll;
32106         if(forceAll === true || (q.length >= this.minChars)){
32107             if(this.lastQuery != q || this.alwaysQuery){
32108                 this.lastQuery = q;
32109                 if(this.mode == 'local'){
32110                     this.selectedIndex = -1;
32111                     if(forceAll){
32112                         this.store.clearFilter();
32113                     }else{
32114                         this.store.filter(this.displayField, q);
32115                     }
32116                     this.onLoad();
32117                 }else{
32118                     this.store.baseParams[this.queryParam] = q;
32119                     this.store.load({
32120                         params: this.getParams(q)
32121                     });
32122                     this.expand();
32123                 }
32124             }else{
32125                 this.selectedIndex = -1;
32126                 this.onLoad();   
32127             }
32128         }
32129     },
32130
32131     // private
32132     getParams : function(q){
32133         var p = {};
32134         //p[this.queryParam] = q;
32135         if(this.pageSize){
32136             p.start = 0;
32137             p.limit = this.pageSize;
32138         }
32139         return p;
32140     },
32141
32142     /**
32143      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32144      */
32145     collapse : function(){
32146         
32147     },
32148
32149     // private
32150     collapseIf : function(e){
32151         
32152     },
32153
32154     /**
32155      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32156      */
32157     expand : function(){
32158         
32159     } ,
32160
32161     // private
32162      
32163
32164     /** 
32165     * @cfg {Boolean} grow 
32166     * @hide 
32167     */
32168     /** 
32169     * @cfg {Number} growMin 
32170     * @hide 
32171     */
32172     /** 
32173     * @cfg {Number} growMax 
32174     * @hide 
32175     */
32176     /**
32177      * @hide
32178      * @method autoSize
32179      */
32180     
32181     setWidth : function()
32182     {
32183         
32184     },
32185     getResizeEl : function(){
32186         return this.el;
32187     }
32188 });//<script type="text/javasscript">
32189  
32190
32191 /**
32192  * @class Roo.DDView
32193  * A DnD enabled version of Roo.View.
32194  * @param {Element/String} container The Element in which to create the View.
32195  * @param {String} tpl The template string used to create the markup for each element of the View
32196  * @param {Object} config The configuration properties. These include all the config options of
32197  * {@link Roo.View} plus some specific to this class.<br>
32198  * <p>
32199  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32200  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32201  * <p>
32202  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32203 .x-view-drag-insert-above {
32204         border-top:1px dotted #3366cc;
32205 }
32206 .x-view-drag-insert-below {
32207         border-bottom:1px dotted #3366cc;
32208 }
32209 </code></pre>
32210  * 
32211  */
32212  
32213 Roo.DDView = function(container, tpl, config) {
32214     Roo.DDView.superclass.constructor.apply(this, arguments);
32215     this.getEl().setStyle("outline", "0px none");
32216     this.getEl().unselectable();
32217     if (this.dragGroup) {
32218                 this.setDraggable(this.dragGroup.split(","));
32219     }
32220     if (this.dropGroup) {
32221                 this.setDroppable(this.dropGroup.split(","));
32222     }
32223     if (this.deletable) {
32224         this.setDeletable();
32225     }
32226     this.isDirtyFlag = false;
32227         this.addEvents({
32228                 "drop" : true
32229         });
32230 };
32231
32232 Roo.extend(Roo.DDView, Roo.View, {
32233 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32234 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32235 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32236 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32237
32238         isFormField: true,
32239
32240         reset: Roo.emptyFn,
32241         
32242         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32243
32244         validate: function() {
32245                 return true;
32246         },
32247         
32248         destroy: function() {
32249                 this.purgeListeners();
32250                 this.getEl.removeAllListeners();
32251                 this.getEl().remove();
32252                 if (this.dragZone) {
32253                         if (this.dragZone.destroy) {
32254                                 this.dragZone.destroy();
32255                         }
32256                 }
32257                 if (this.dropZone) {
32258                         if (this.dropZone.destroy) {
32259                                 this.dropZone.destroy();
32260                         }
32261                 }
32262         },
32263
32264 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32265         getName: function() {
32266                 return this.name;
32267         },
32268
32269 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32270         setValue: function(v) {
32271                 if (!this.store) {
32272                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32273                 }
32274                 var data = {};
32275                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32276                 this.store.proxy = new Roo.data.MemoryProxy(data);
32277                 this.store.load();
32278         },
32279
32280 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32281         getValue: function() {
32282                 var result = '(';
32283                 this.store.each(function(rec) {
32284                         result += rec.id + ',';
32285                 });
32286                 return result.substr(0, result.length - 1) + ')';
32287         },
32288         
32289         getIds: function() {
32290                 var i = 0, result = new Array(this.store.getCount());
32291                 this.store.each(function(rec) {
32292                         result[i++] = rec.id;
32293                 });
32294                 return result;
32295         },
32296         
32297         isDirty: function() {
32298                 return this.isDirtyFlag;
32299         },
32300
32301 /**
32302  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32303  *      whole Element becomes the target, and this causes the drop gesture to append.
32304  */
32305     getTargetFromEvent : function(e) {
32306                 var target = e.getTarget();
32307                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32308                 target = target.parentNode;
32309                 }
32310                 if (!target) {
32311                         target = this.el.dom.lastChild || this.el.dom;
32312                 }
32313                 return target;
32314     },
32315
32316 /**
32317  *      Create the drag data which consists of an object which has the property "ddel" as
32318  *      the drag proxy element. 
32319  */
32320     getDragData : function(e) {
32321         var target = this.findItemFromChild(e.getTarget());
32322                 if(target) {
32323                         this.handleSelection(e);
32324                         var selNodes = this.getSelectedNodes();
32325             var dragData = {
32326                 source: this,
32327                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32328                 nodes: selNodes,
32329                 records: []
32330                         };
32331                         var selectedIndices = this.getSelectedIndexes();
32332                         for (var i = 0; i < selectedIndices.length; i++) {
32333                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32334                         }
32335                         if (selNodes.length == 1) {
32336                                 dragData.ddel = target.cloneNode(true); // the div element
32337                         } else {
32338                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32339                                 div.className = 'multi-proxy';
32340                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32341                                         div.appendChild(selNodes[i].cloneNode(true));
32342                                 }
32343                                 dragData.ddel = div;
32344                         }
32345             //console.log(dragData)
32346             //console.log(dragData.ddel.innerHTML)
32347                         return dragData;
32348                 }
32349         //console.log('nodragData')
32350                 return false;
32351     },
32352     
32353 /**     Specify to which ddGroup items in this DDView may be dragged. */
32354     setDraggable: function(ddGroup) {
32355         if (ddGroup instanceof Array) {
32356                 Roo.each(ddGroup, this.setDraggable, this);
32357                 return;
32358         }
32359         if (this.dragZone) {
32360                 this.dragZone.addToGroup(ddGroup);
32361         } else {
32362                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32363                                 containerScroll: true,
32364                                 ddGroup: ddGroup 
32365
32366                         });
32367 //                      Draggability implies selection. DragZone's mousedown selects the element.
32368                         if (!this.multiSelect) { this.singleSelect = true; }
32369
32370 //                      Wire the DragZone's handlers up to methods in *this*
32371                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32372                 }
32373     },
32374
32375 /**     Specify from which ddGroup this DDView accepts drops. */
32376     setDroppable: function(ddGroup) {
32377         if (ddGroup instanceof Array) {
32378                 Roo.each(ddGroup, this.setDroppable, this);
32379                 return;
32380         }
32381         if (this.dropZone) {
32382                 this.dropZone.addToGroup(ddGroup);
32383         } else {
32384                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32385                                 containerScroll: true,
32386                                 ddGroup: ddGroup
32387                         });
32388
32389 //                      Wire the DropZone's handlers up to methods in *this*
32390                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32391                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32392                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32393                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32394                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32395                 }
32396     },
32397
32398 /**     Decide whether to drop above or below a View node. */
32399     getDropPoint : function(e, n, dd){
32400         if (n == this.el.dom) { return "above"; }
32401                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32402                 var c = t + (b - t) / 2;
32403                 var y = Roo.lib.Event.getPageY(e);
32404                 if(y <= c) {
32405                         return "above";
32406                 }else{
32407                         return "below";
32408                 }
32409     },
32410
32411     onNodeEnter : function(n, dd, e, data){
32412                 return false;
32413     },
32414     
32415     onNodeOver : function(n, dd, e, data){
32416                 var pt = this.getDropPoint(e, n, dd);
32417                 // set the insert point style on the target node
32418                 var dragElClass = this.dropNotAllowed;
32419                 if (pt) {
32420                         var targetElClass;
32421                         if (pt == "above"){
32422                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32423                                 targetElClass = "x-view-drag-insert-above";
32424                         } else {
32425                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32426                                 targetElClass = "x-view-drag-insert-below";
32427                         }
32428                         if (this.lastInsertClass != targetElClass){
32429                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32430                                 this.lastInsertClass = targetElClass;
32431                         }
32432                 }
32433                 return dragElClass;
32434         },
32435
32436     onNodeOut : function(n, dd, e, data){
32437                 this.removeDropIndicators(n);
32438     },
32439
32440     onNodeDrop : function(n, dd, e, data){
32441         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32442                 return false;
32443         }
32444         var pt = this.getDropPoint(e, n, dd);
32445                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32446                 if (pt == "below") { insertAt++; }
32447                 for (var i = 0; i < data.records.length; i++) {
32448                         var r = data.records[i];
32449                         var dup = this.store.getById(r.id);
32450                         if (dup && (dd != this.dragZone)) {
32451                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32452                         } else {
32453                                 if (data.copy) {
32454                                         this.store.insert(insertAt++, r.copy());
32455                                 } else {
32456                                         data.source.isDirtyFlag = true;
32457                                         r.store.remove(r);
32458                                         this.store.insert(insertAt++, r);
32459                                 }
32460                                 this.isDirtyFlag = true;
32461                         }
32462                 }
32463                 this.dragZone.cachedTarget = null;
32464                 return true;
32465     },
32466
32467     removeDropIndicators : function(n){
32468                 if(n){
32469                         Roo.fly(n).removeClass([
32470                                 "x-view-drag-insert-above",
32471                                 "x-view-drag-insert-below"]);
32472                         this.lastInsertClass = "_noclass";
32473                 }
32474     },
32475
32476 /**
32477  *      Utility method. Add a delete option to the DDView's context menu.
32478  *      @param {String} imageUrl The URL of the "delete" icon image.
32479  */
32480         setDeletable: function(imageUrl) {
32481                 if (!this.singleSelect && !this.multiSelect) {
32482                         this.singleSelect = true;
32483                 }
32484                 var c = this.getContextMenu();
32485                 this.contextMenu.on("itemclick", function(item) {
32486                         switch (item.id) {
32487                                 case "delete":
32488                                         this.remove(this.getSelectedIndexes());
32489                                         break;
32490                         }
32491                 }, this);
32492                 this.contextMenu.add({
32493                         icon: imageUrl,
32494                         id: "delete",
32495                         text: 'Delete'
32496                 });
32497         },
32498         
32499 /**     Return the context menu for this DDView. */
32500         getContextMenu: function() {
32501                 if (!this.contextMenu) {
32502 //                      Create the View's context menu
32503                         this.contextMenu = new Roo.menu.Menu({
32504                                 id: this.id + "-contextmenu"
32505                         });
32506                         this.el.on("contextmenu", this.showContextMenu, this);
32507                 }
32508                 return this.contextMenu;
32509         },
32510         
32511         disableContextMenu: function() {
32512                 if (this.contextMenu) {
32513                         this.el.un("contextmenu", this.showContextMenu, this);
32514                 }
32515         },
32516
32517         showContextMenu: function(e, item) {
32518         item = this.findItemFromChild(e.getTarget());
32519                 if (item) {
32520                         e.stopEvent();
32521                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32522                         this.contextMenu.showAt(e.getXY());
32523             }
32524     },
32525
32526 /**
32527  *      Remove {@link Roo.data.Record}s at the specified indices.
32528  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32529  */
32530     remove: function(selectedIndices) {
32531                 selectedIndices = [].concat(selectedIndices);
32532                 for (var i = 0; i < selectedIndices.length; i++) {
32533                         var rec = this.store.getAt(selectedIndices[i]);
32534                         this.store.remove(rec);
32535                 }
32536     },
32537
32538 /**
32539  *      Double click fires the event, but also, if this is draggable, and there is only one other
32540  *      related DropZone, it transfers the selected node.
32541  */
32542     onDblClick : function(e){
32543         var item = this.findItemFromChild(e.getTarget());
32544         if(item){
32545             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32546                 return false;
32547             }
32548             if (this.dragGroup) {
32549                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32550                     while (targets.indexOf(this.dropZone) > -1) {
32551                             targets.remove(this.dropZone);
32552                                 }
32553                     if (targets.length == 1) {
32554                                         this.dragZone.cachedTarget = null;
32555                         var el = Roo.get(targets[0].getEl());
32556                         var box = el.getBox(true);
32557                         targets[0].onNodeDrop(el.dom, {
32558                                 target: el.dom,
32559                                 xy: [box.x, box.y + box.height - 1]
32560                         }, null, this.getDragData(e));
32561                     }
32562                 }
32563         }
32564     },
32565     
32566     handleSelection: function(e) {
32567                 this.dragZone.cachedTarget = null;
32568         var item = this.findItemFromChild(e.getTarget());
32569         if (!item) {
32570                 this.clearSelections(true);
32571                 return;
32572         }
32573                 if (item && (this.multiSelect || this.singleSelect)){
32574                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32575                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32576                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32577                                 this.unselect(item);
32578                         } else {
32579                                 this.select(item, this.multiSelect && e.ctrlKey);
32580                                 this.lastSelection = item;
32581                         }
32582                 }
32583     },
32584
32585     onItemClick : function(item, index, e){
32586                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32587                         return false;
32588                 }
32589                 return true;
32590     },
32591
32592     unselect : function(nodeInfo, suppressEvent){
32593                 var node = this.getNode(nodeInfo);
32594                 if(node && this.isSelected(node)){
32595                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32596                                 Roo.fly(node).removeClass(this.selectedClass);
32597                                 this.selections.remove(node);
32598                                 if(!suppressEvent){
32599                                         this.fireEvent("selectionchange", this, this.selections);
32600                                 }
32601                         }
32602                 }
32603     }
32604 });
32605 /*
32606  * Based on:
32607  * Ext JS Library 1.1.1
32608  * Copyright(c) 2006-2007, Ext JS, LLC.
32609  *
32610  * Originally Released Under LGPL - original licence link has changed is not relivant.
32611  *
32612  * Fork - LGPL
32613  * <script type="text/javascript">
32614  */
32615  
32616 /**
32617  * @class Roo.LayoutManager
32618  * @extends Roo.util.Observable
32619  * Base class for layout managers.
32620  */
32621 Roo.LayoutManager = function(container, config){
32622     Roo.LayoutManager.superclass.constructor.call(this);
32623     this.el = Roo.get(container);
32624     // ie scrollbar fix
32625     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32626         document.body.scroll = "no";
32627     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32628         this.el.position('relative');
32629     }
32630     this.id = this.el.id;
32631     this.el.addClass("x-layout-container");
32632     /** false to disable window resize monitoring @type Boolean */
32633     this.monitorWindowResize = true;
32634     this.regions = {};
32635     this.addEvents({
32636         /**
32637          * @event layout
32638          * Fires when a layout is performed. 
32639          * @param {Roo.LayoutManager} this
32640          */
32641         "layout" : true,
32642         /**
32643          * @event regionresized
32644          * Fires when the user resizes a region. 
32645          * @param {Roo.LayoutRegion} region The resized region
32646          * @param {Number} newSize The new size (width for east/west, height for north/south)
32647          */
32648         "regionresized" : true,
32649         /**
32650          * @event regioncollapsed
32651          * Fires when a region is collapsed. 
32652          * @param {Roo.LayoutRegion} region The collapsed region
32653          */
32654         "regioncollapsed" : true,
32655         /**
32656          * @event regionexpanded
32657          * Fires when a region is expanded.  
32658          * @param {Roo.LayoutRegion} region The expanded region
32659          */
32660         "regionexpanded" : true
32661     });
32662     this.updating = false;
32663     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32664 };
32665
32666 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32667     /**
32668      * Returns true if this layout is currently being updated
32669      * @return {Boolean}
32670      */
32671     isUpdating : function(){
32672         return this.updating; 
32673     },
32674     
32675     /**
32676      * Suspend the LayoutManager from doing auto-layouts while
32677      * making multiple add or remove calls
32678      */
32679     beginUpdate : function(){
32680         this.updating = true;    
32681     },
32682     
32683     /**
32684      * Restore auto-layouts and optionally disable the manager from performing a layout
32685      * @param {Boolean} noLayout true to disable a layout update 
32686      */
32687     endUpdate : function(noLayout){
32688         this.updating = false;
32689         if(!noLayout){
32690             this.layout();
32691         }    
32692     },
32693     
32694     layout: function(){
32695         
32696     },
32697     
32698     onRegionResized : function(region, newSize){
32699         this.fireEvent("regionresized", region, newSize);
32700         this.layout();
32701     },
32702     
32703     onRegionCollapsed : function(region){
32704         this.fireEvent("regioncollapsed", region);
32705     },
32706     
32707     onRegionExpanded : function(region){
32708         this.fireEvent("regionexpanded", region);
32709     },
32710         
32711     /**
32712      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32713      * performs box-model adjustments.
32714      * @return {Object} The size as an object {width: (the width), height: (the height)}
32715      */
32716     getViewSize : function(){
32717         var size;
32718         if(this.el.dom != document.body){
32719             size = this.el.getSize();
32720         }else{
32721             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32722         }
32723         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32724         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32725         return size;
32726     },
32727     
32728     /**
32729      * Returns the Element this layout is bound to.
32730      * @return {Roo.Element}
32731      */
32732     getEl : function(){
32733         return this.el;
32734     },
32735     
32736     /**
32737      * Returns the specified region.
32738      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32739      * @return {Roo.LayoutRegion}
32740      */
32741     getRegion : function(target){
32742         return this.regions[target.toLowerCase()];
32743     },
32744     
32745     onWindowResize : function(){
32746         if(this.monitorWindowResize){
32747             this.layout();
32748         }
32749     }
32750 });/*
32751  * Based on:
32752  * Ext JS Library 1.1.1
32753  * Copyright(c) 2006-2007, Ext JS, LLC.
32754  *
32755  * Originally Released Under LGPL - original licence link has changed is not relivant.
32756  *
32757  * Fork - LGPL
32758  * <script type="text/javascript">
32759  */
32760 /**
32761  * @class Roo.BorderLayout
32762  * @extends Roo.LayoutManager
32763  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32764  * please see: <br><br>
32765  * <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>
32766  * <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>
32767  * Example:
32768  <pre><code>
32769  var layout = new Roo.BorderLayout(document.body, {
32770     north: {
32771         initialSize: 25,
32772         titlebar: false
32773     },
32774     west: {
32775         split:true,
32776         initialSize: 200,
32777         minSize: 175,
32778         maxSize: 400,
32779         titlebar: true,
32780         collapsible: true
32781     },
32782     east: {
32783         split:true,
32784         initialSize: 202,
32785         minSize: 175,
32786         maxSize: 400,
32787         titlebar: true,
32788         collapsible: true
32789     },
32790     south: {
32791         split:true,
32792         initialSize: 100,
32793         minSize: 100,
32794         maxSize: 200,
32795         titlebar: true,
32796         collapsible: true
32797     },
32798     center: {
32799         titlebar: true,
32800         autoScroll:true,
32801         resizeTabs: true,
32802         minTabWidth: 50,
32803         preferredTabWidth: 150
32804     }
32805 });
32806
32807 // shorthand
32808 var CP = Roo.ContentPanel;
32809
32810 layout.beginUpdate();
32811 layout.add("north", new CP("north", "North"));
32812 layout.add("south", new CP("south", {title: "South", closable: true}));
32813 layout.add("west", new CP("west", {title: "West"}));
32814 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32815 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32816 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32817 layout.getRegion("center").showPanel("center1");
32818 layout.endUpdate();
32819 </code></pre>
32820
32821 <b>The container the layout is rendered into can be either the body element or any other element.
32822 If it is not the body element, the container needs to either be an absolute positioned element,
32823 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32824 the container size if it is not the body element.</b>
32825
32826 * @constructor
32827 * Create a new BorderLayout
32828 * @param {String/HTMLElement/Element} container The container this layout is bound to
32829 * @param {Object} config Configuration options
32830  */
32831 Roo.BorderLayout = function(container, config){
32832     config = config || {};
32833     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32834     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32835     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32836         var target = this.factory.validRegions[i];
32837         if(config[target]){
32838             this.addRegion(target, config[target]);
32839         }
32840     }
32841 };
32842
32843 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32844     /**
32845      * Creates and adds a new region if it doesn't already exist.
32846      * @param {String} target The target region key (north, south, east, west or center).
32847      * @param {Object} config The regions config object
32848      * @return {BorderLayoutRegion} The new region
32849      */
32850     addRegion : function(target, config){
32851         if(!this.regions[target]){
32852             var r = this.factory.create(target, this, config);
32853             this.bindRegion(target, r);
32854         }
32855         return this.regions[target];
32856     },
32857
32858     // private (kinda)
32859     bindRegion : function(name, r){
32860         this.regions[name] = r;
32861         r.on("visibilitychange", this.layout, this);
32862         r.on("paneladded", this.layout, this);
32863         r.on("panelremoved", this.layout, this);
32864         r.on("invalidated", this.layout, this);
32865         r.on("resized", this.onRegionResized, this);
32866         r.on("collapsed", this.onRegionCollapsed, this);
32867         r.on("expanded", this.onRegionExpanded, this);
32868     },
32869
32870     /**
32871      * Performs a layout update.
32872      */
32873     layout : function(){
32874         if(this.updating) return;
32875         var size = this.getViewSize();
32876         var w = size.width;
32877         var h = size.height;
32878         var centerW = w;
32879         var centerH = h;
32880         var centerY = 0;
32881         var centerX = 0;
32882         //var x = 0, y = 0;
32883
32884         var rs = this.regions;
32885         var north = rs["north"];
32886         var south = rs["south"]; 
32887         var west = rs["west"];
32888         var east = rs["east"];
32889         var center = rs["center"];
32890         //if(this.hideOnLayout){ // not supported anymore
32891             //c.el.setStyle("display", "none");
32892         //}
32893         if(north && north.isVisible()){
32894             var b = north.getBox();
32895             var m = north.getMargins();
32896             b.width = w - (m.left+m.right);
32897             b.x = m.left;
32898             b.y = m.top;
32899             centerY = b.height + b.y + m.bottom;
32900             centerH -= centerY;
32901             north.updateBox(this.safeBox(b));
32902         }
32903         if(south && south.isVisible()){
32904             var b = south.getBox();
32905             var m = south.getMargins();
32906             b.width = w - (m.left+m.right);
32907             b.x = m.left;
32908             var totalHeight = (b.height + m.top + m.bottom);
32909             b.y = h - totalHeight + m.top;
32910             centerH -= totalHeight;
32911             south.updateBox(this.safeBox(b));
32912         }
32913         if(west && west.isVisible()){
32914             var b = west.getBox();
32915             var m = west.getMargins();
32916             b.height = centerH - (m.top+m.bottom);
32917             b.x = m.left;
32918             b.y = centerY + m.top;
32919             var totalWidth = (b.width + m.left + m.right);
32920             centerX += totalWidth;
32921             centerW -= totalWidth;
32922             west.updateBox(this.safeBox(b));
32923         }
32924         if(east && east.isVisible()){
32925             var b = east.getBox();
32926             var m = east.getMargins();
32927             b.height = centerH - (m.top+m.bottom);
32928             var totalWidth = (b.width + m.left + m.right);
32929             b.x = w - totalWidth + m.left;
32930             b.y = centerY + m.top;
32931             centerW -= totalWidth;
32932             east.updateBox(this.safeBox(b));
32933         }
32934         if(center){
32935             var m = center.getMargins();
32936             var centerBox = {
32937                 x: centerX + m.left,
32938                 y: centerY + m.top,
32939                 width: centerW - (m.left+m.right),
32940                 height: centerH - (m.top+m.bottom)
32941             };
32942             //if(this.hideOnLayout){
32943                 //center.el.setStyle("display", "block");
32944             //}
32945             center.updateBox(this.safeBox(centerBox));
32946         }
32947         this.el.repaint();
32948         this.fireEvent("layout", this);
32949     },
32950
32951     // private
32952     safeBox : function(box){
32953         box.width = Math.max(0, box.width);
32954         box.height = Math.max(0, box.height);
32955         return box;
32956     },
32957
32958     /**
32959      * Adds a ContentPanel (or subclass) to this layout.
32960      * @param {String} target The target region key (north, south, east, west or center).
32961      * @param {Roo.ContentPanel} panel The panel to add
32962      * @return {Roo.ContentPanel} The added panel
32963      */
32964     add : function(target, panel){
32965          
32966         target = target.toLowerCase();
32967         return this.regions[target].add(panel);
32968     },
32969
32970     /**
32971      * Remove a ContentPanel (or subclass) to this layout.
32972      * @param {String} target The target region key (north, south, east, west or center).
32973      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32974      * @return {Roo.ContentPanel} The removed panel
32975      */
32976     remove : function(target, panel){
32977         target = target.toLowerCase();
32978         return this.regions[target].remove(panel);
32979     },
32980
32981     /**
32982      * Searches all regions for a panel with the specified id
32983      * @param {String} panelId
32984      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32985      */
32986     findPanel : function(panelId){
32987         var rs = this.regions;
32988         for(var target in rs){
32989             if(typeof rs[target] != "function"){
32990                 var p = rs[target].getPanel(panelId);
32991                 if(p){
32992                     return p;
32993                 }
32994             }
32995         }
32996         return null;
32997     },
32998
32999     /**
33000      * Searches all regions for a panel with the specified id and activates (shows) it.
33001      * @param {String/ContentPanel} panelId The panels id or the panel itself
33002      * @return {Roo.ContentPanel} The shown panel or null
33003      */
33004     showPanel : function(panelId) {
33005       var rs = this.regions;
33006       for(var target in rs){
33007          var r = rs[target];
33008          if(typeof r != "function"){
33009             if(r.hasPanel(panelId)){
33010                return r.showPanel(panelId);
33011             }
33012          }
33013       }
33014       return null;
33015    },
33016
33017    /**
33018      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33019      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33020      */
33021     restoreState : function(provider){
33022         if(!provider){
33023             provider = Roo.state.Manager;
33024         }
33025         var sm = new Roo.LayoutStateManager();
33026         sm.init(this, provider);
33027     },
33028
33029     /**
33030      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33031      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33032      * a valid ContentPanel config object.  Example:
33033      * <pre><code>
33034 // Create the main layout
33035 var layout = new Roo.BorderLayout('main-ct', {
33036     west: {
33037         split:true,
33038         minSize: 175,
33039         titlebar: true
33040     },
33041     center: {
33042         title:'Components'
33043     }
33044 }, 'main-ct');
33045
33046 // Create and add multiple ContentPanels at once via configs
33047 layout.batchAdd({
33048    west: {
33049        id: 'source-files',
33050        autoCreate:true,
33051        title:'Ext Source Files',
33052        autoScroll:true,
33053        fitToFrame:true
33054    },
33055    center : {
33056        el: cview,
33057        autoScroll:true,
33058        fitToFrame:true,
33059        toolbar: tb,
33060        resizeEl:'cbody'
33061    }
33062 });
33063 </code></pre>
33064      * @param {Object} regions An object containing ContentPanel configs by region name
33065      */
33066     batchAdd : function(regions){
33067         this.beginUpdate();
33068         for(var rname in regions){
33069             var lr = this.regions[rname];
33070             if(lr){
33071                 this.addTypedPanels(lr, regions[rname]);
33072             }
33073         }
33074         this.endUpdate();
33075     },
33076
33077     // private
33078     addTypedPanels : function(lr, ps){
33079         if(typeof ps == 'string'){
33080             lr.add(new Roo.ContentPanel(ps));
33081         }
33082         else if(ps instanceof Array){
33083             for(var i =0, len = ps.length; i < len; i++){
33084                 this.addTypedPanels(lr, ps[i]);
33085             }
33086         }
33087         else if(!ps.events){ // raw config?
33088             var el = ps.el;
33089             delete ps.el; // prevent conflict
33090             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33091         }
33092         else {  // panel object assumed!
33093             lr.add(ps);
33094         }
33095     },
33096     /**
33097      * Adds a xtype elements to the layout.
33098      * <pre><code>
33099
33100 layout.addxtype({
33101        xtype : 'ContentPanel',
33102        region: 'west',
33103        items: [ .... ]
33104    }
33105 );
33106
33107 layout.addxtype({
33108         xtype : 'NestedLayoutPanel',
33109         region: 'west',
33110         layout: {
33111            center: { },
33112            west: { }   
33113         },
33114         items : [ ... list of content panels or nested layout panels.. ]
33115    }
33116 );
33117 </code></pre>
33118      * @param {Object} cfg Xtype definition of item to add.
33119      */
33120     addxtype : function(cfg)
33121     {
33122         // basically accepts a pannel...
33123         // can accept a layout region..!?!?
33124         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33125         
33126         if (!cfg.xtype.match(/Panel$/)) {
33127             return false;
33128         }
33129         var ret = false;
33130         
33131         if (typeof(cfg.region) == 'undefined') {
33132             Roo.log("Failed to add Panel, region was not set");
33133             Roo.log(cfg);
33134             return false;
33135         }
33136         var region = cfg.region;
33137         delete cfg.region;
33138         
33139           
33140         var xitems = [];
33141         if (cfg.items) {
33142             xitems = cfg.items;
33143             delete cfg.items;
33144         }
33145         var nb = false;
33146         
33147         switch(cfg.xtype) 
33148         {
33149             case 'ContentPanel':  // ContentPanel (el, cfg)
33150             case 'ScrollPanel':  // ContentPanel (el, cfg)
33151             case 'ViewPanel': 
33152                 if(cfg.autoCreate) {
33153                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33154                 } else {
33155                     var el = this.el.createChild();
33156                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33157                 }
33158                 
33159                 this.add(region, ret);
33160                 break;
33161             
33162             
33163             case 'TreePanel': // our new panel!
33164                 cfg.el = this.el.createChild();
33165                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33166                 this.add(region, ret);
33167                 break;
33168             
33169             case 'NestedLayoutPanel': 
33170                 // create a new Layout (which is  a Border Layout...
33171                 var el = this.el.createChild();
33172                 var clayout = cfg.layout;
33173                 delete cfg.layout;
33174                 clayout.items   = clayout.items  || [];
33175                 // replace this exitems with the clayout ones..
33176                 xitems = clayout.items;
33177                  
33178                 
33179                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33180                     cfg.background = false;
33181                 }
33182                 var layout = new Roo.BorderLayout(el, clayout);
33183                 
33184                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33185                 //console.log('adding nested layout panel '  + cfg.toSource());
33186                 this.add(region, ret);
33187                 nb = {}; /// find first...
33188                 break;
33189                 
33190             case 'GridPanel': 
33191             
33192                 // needs grid and region
33193                 
33194                 //var el = this.getRegion(region).el.createChild();
33195                 var el = this.el.createChild();
33196                 // create the grid first...
33197                 
33198                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33199                 delete cfg.grid;
33200                 if (region == 'center' && this.active ) {
33201                     cfg.background = false;
33202                 }
33203                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33204                 
33205                 this.add(region, ret);
33206                 if (cfg.background) {
33207                     ret.on('activate', function(gp) {
33208                         if (!gp.grid.rendered) {
33209                             gp.grid.render();
33210                         }
33211                     });
33212                 } else {
33213                     grid.render();
33214                 }
33215                 break;
33216            
33217            
33218            
33219                 
33220                 
33221                 
33222             default:
33223                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33224                     
33225                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33226                     this.add(region, ret);
33227                 } else {
33228                 
33229                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33230                     return null;
33231                 }
33232                 
33233              // GridPanel (grid, cfg)
33234             
33235         }
33236         this.beginUpdate();
33237         // add children..
33238         var region = '';
33239         var abn = {};
33240         Roo.each(xitems, function(i)  {
33241             region = nb && i.region ? i.region : false;
33242             
33243             var add = ret.addxtype(i);
33244            
33245             if (region) {
33246                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33247                 if (!i.background) {
33248                     abn[region] = nb[region] ;
33249                 }
33250             }
33251             
33252         });
33253         this.endUpdate();
33254
33255         // make the last non-background panel active..
33256         //if (nb) { Roo.log(abn); }
33257         if (nb) {
33258             
33259             for(var r in abn) {
33260                 region = this.getRegion(r);
33261                 if (region) {
33262                     // tried using nb[r], but it does not work..
33263                      
33264                     region.showPanel(abn[r]);
33265                    
33266                 }
33267             }
33268         }
33269         return ret;
33270         
33271     }
33272 });
33273
33274 /**
33275  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33276  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33277  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33278  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33279  * <pre><code>
33280 // shorthand
33281 var CP = Roo.ContentPanel;
33282
33283 var layout = Roo.BorderLayout.create({
33284     north: {
33285         initialSize: 25,
33286         titlebar: false,
33287         panels: [new CP("north", "North")]
33288     },
33289     west: {
33290         split:true,
33291         initialSize: 200,
33292         minSize: 175,
33293         maxSize: 400,
33294         titlebar: true,
33295         collapsible: true,
33296         panels: [new CP("west", {title: "West"})]
33297     },
33298     east: {
33299         split:true,
33300         initialSize: 202,
33301         minSize: 175,
33302         maxSize: 400,
33303         titlebar: true,
33304         collapsible: true,
33305         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33306     },
33307     south: {
33308         split:true,
33309         initialSize: 100,
33310         minSize: 100,
33311         maxSize: 200,
33312         titlebar: true,
33313         collapsible: true,
33314         panels: [new CP("south", {title: "South", closable: true})]
33315     },
33316     center: {
33317         titlebar: true,
33318         autoScroll:true,
33319         resizeTabs: true,
33320         minTabWidth: 50,
33321         preferredTabWidth: 150,
33322         panels: [
33323             new CP("center1", {title: "Close Me", closable: true}),
33324             new CP("center2", {title: "Center Panel", closable: false})
33325         ]
33326     }
33327 }, document.body);
33328
33329 layout.getRegion("center").showPanel("center1");
33330 </code></pre>
33331  * @param config
33332  * @param targetEl
33333  */
33334 Roo.BorderLayout.create = function(config, targetEl){
33335     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33336     layout.beginUpdate();
33337     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33338     for(var j = 0, jlen = regions.length; j < jlen; j++){
33339         var lr = regions[j];
33340         if(layout.regions[lr] && config[lr].panels){
33341             var r = layout.regions[lr];
33342             var ps = config[lr].panels;
33343             layout.addTypedPanels(r, ps);
33344         }
33345     }
33346     layout.endUpdate();
33347     return layout;
33348 };
33349
33350 // private
33351 Roo.BorderLayout.RegionFactory = {
33352     // private
33353     validRegions : ["north","south","east","west","center"],
33354
33355     // private
33356     create : function(target, mgr, config){
33357         target = target.toLowerCase();
33358         if(config.lightweight || config.basic){
33359             return new Roo.BasicLayoutRegion(mgr, config, target);
33360         }
33361         switch(target){
33362             case "north":
33363                 return new Roo.NorthLayoutRegion(mgr, config);
33364             case "south":
33365                 return new Roo.SouthLayoutRegion(mgr, config);
33366             case "east":
33367                 return new Roo.EastLayoutRegion(mgr, config);
33368             case "west":
33369                 return new Roo.WestLayoutRegion(mgr, config);
33370             case "center":
33371                 return new Roo.CenterLayoutRegion(mgr, config);
33372         }
33373         throw 'Layout region "'+target+'" not supported.';
33374     }
33375 };/*
33376  * Based on:
33377  * Ext JS Library 1.1.1
33378  * Copyright(c) 2006-2007, Ext JS, LLC.
33379  *
33380  * Originally Released Under LGPL - original licence link has changed is not relivant.
33381  *
33382  * Fork - LGPL
33383  * <script type="text/javascript">
33384  */
33385  
33386 /**
33387  * @class Roo.BasicLayoutRegion
33388  * @extends Roo.util.Observable
33389  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33390  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33391  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33392  */
33393 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33394     this.mgr = mgr;
33395     this.position  = pos;
33396     this.events = {
33397         /**
33398          * @scope Roo.BasicLayoutRegion
33399          */
33400         
33401         /**
33402          * @event beforeremove
33403          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33404          * @param {Roo.LayoutRegion} this
33405          * @param {Roo.ContentPanel} panel The panel
33406          * @param {Object} e The cancel event object
33407          */
33408         "beforeremove" : true,
33409         /**
33410          * @event invalidated
33411          * Fires when the layout for this region is changed.
33412          * @param {Roo.LayoutRegion} this
33413          */
33414         "invalidated" : true,
33415         /**
33416          * @event visibilitychange
33417          * Fires when this region is shown or hidden 
33418          * @param {Roo.LayoutRegion} this
33419          * @param {Boolean} visibility true or false
33420          */
33421         "visibilitychange" : true,
33422         /**
33423          * @event paneladded
33424          * Fires when a panel is added. 
33425          * @param {Roo.LayoutRegion} this
33426          * @param {Roo.ContentPanel} panel The panel
33427          */
33428         "paneladded" : true,
33429         /**
33430          * @event panelremoved
33431          * Fires when a panel is removed. 
33432          * @param {Roo.LayoutRegion} this
33433          * @param {Roo.ContentPanel} panel The panel
33434          */
33435         "panelremoved" : true,
33436         /**
33437          * @event collapsed
33438          * Fires when this region is collapsed.
33439          * @param {Roo.LayoutRegion} this
33440          */
33441         "collapsed" : true,
33442         /**
33443          * @event expanded
33444          * Fires when this region is expanded.
33445          * @param {Roo.LayoutRegion} this
33446          */
33447         "expanded" : true,
33448         /**
33449          * @event slideshow
33450          * Fires when this region is slid into view.
33451          * @param {Roo.LayoutRegion} this
33452          */
33453         "slideshow" : true,
33454         /**
33455          * @event slidehide
33456          * Fires when this region slides out of view. 
33457          * @param {Roo.LayoutRegion} this
33458          */
33459         "slidehide" : true,
33460         /**
33461          * @event panelactivated
33462          * Fires when a panel is activated. 
33463          * @param {Roo.LayoutRegion} this
33464          * @param {Roo.ContentPanel} panel The activated panel
33465          */
33466         "panelactivated" : true,
33467         /**
33468          * @event resized
33469          * Fires when the user resizes this region. 
33470          * @param {Roo.LayoutRegion} this
33471          * @param {Number} newSize The new size (width for east/west, height for north/south)
33472          */
33473         "resized" : true
33474     };
33475     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33476     this.panels = new Roo.util.MixedCollection();
33477     this.panels.getKey = this.getPanelId.createDelegate(this);
33478     this.box = null;
33479     this.activePanel = null;
33480     // ensure listeners are added...
33481     
33482     if (config.listeners || config.events) {
33483         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33484             listeners : config.listeners || {},
33485             events : config.events || {}
33486         });
33487     }
33488     
33489     if(skipConfig !== true){
33490         this.applyConfig(config);
33491     }
33492 };
33493
33494 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33495     getPanelId : function(p){
33496         return p.getId();
33497     },
33498     
33499     applyConfig : function(config){
33500         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33501         this.config = config;
33502         
33503     },
33504     
33505     /**
33506      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33507      * the width, for horizontal (north, south) the height.
33508      * @param {Number} newSize The new width or height
33509      */
33510     resizeTo : function(newSize){
33511         var el = this.el ? this.el :
33512                  (this.activePanel ? this.activePanel.getEl() : null);
33513         if(el){
33514             switch(this.position){
33515                 case "east":
33516                 case "west":
33517                     el.setWidth(newSize);
33518                     this.fireEvent("resized", this, newSize);
33519                 break;
33520                 case "north":
33521                 case "south":
33522                     el.setHeight(newSize);
33523                     this.fireEvent("resized", this, newSize);
33524                 break;                
33525             }
33526         }
33527     },
33528     
33529     getBox : function(){
33530         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33531     },
33532     
33533     getMargins : function(){
33534         return this.margins;
33535     },
33536     
33537     updateBox : function(box){
33538         this.box = box;
33539         var el = this.activePanel.getEl();
33540         el.dom.style.left = box.x + "px";
33541         el.dom.style.top = box.y + "px";
33542         this.activePanel.setSize(box.width, box.height);
33543     },
33544     
33545     /**
33546      * Returns the container element for this region.
33547      * @return {Roo.Element}
33548      */
33549     getEl : function(){
33550         return this.activePanel;
33551     },
33552     
33553     /**
33554      * Returns true if this region is currently visible.
33555      * @return {Boolean}
33556      */
33557     isVisible : function(){
33558         return this.activePanel ? true : false;
33559     },
33560     
33561     setActivePanel : function(panel){
33562         panel = this.getPanel(panel);
33563         if(this.activePanel && this.activePanel != panel){
33564             this.activePanel.setActiveState(false);
33565             this.activePanel.getEl().setLeftTop(-10000,-10000);
33566         }
33567         this.activePanel = panel;
33568         panel.setActiveState(true);
33569         if(this.box){
33570             panel.setSize(this.box.width, this.box.height);
33571         }
33572         this.fireEvent("panelactivated", this, panel);
33573         this.fireEvent("invalidated");
33574     },
33575     
33576     /**
33577      * Show the specified panel.
33578      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33579      * @return {Roo.ContentPanel} The shown panel or null
33580      */
33581     showPanel : function(panel){
33582         if(panel = this.getPanel(panel)){
33583             this.setActivePanel(panel);
33584         }
33585         return panel;
33586     },
33587     
33588     /**
33589      * Get the active panel for this region.
33590      * @return {Roo.ContentPanel} The active panel or null
33591      */
33592     getActivePanel : function(){
33593         return this.activePanel;
33594     },
33595     
33596     /**
33597      * Add the passed ContentPanel(s)
33598      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33599      * @return {Roo.ContentPanel} The panel added (if only one was added)
33600      */
33601     add : function(panel){
33602         if(arguments.length > 1){
33603             for(var i = 0, len = arguments.length; i < len; i++) {
33604                 this.add(arguments[i]);
33605             }
33606             return null;
33607         }
33608         if(this.hasPanel(panel)){
33609             this.showPanel(panel);
33610             return panel;
33611         }
33612         var el = panel.getEl();
33613         if(el.dom.parentNode != this.mgr.el.dom){
33614             this.mgr.el.dom.appendChild(el.dom);
33615         }
33616         if(panel.setRegion){
33617             panel.setRegion(this);
33618         }
33619         this.panels.add(panel);
33620         el.setStyle("position", "absolute");
33621         if(!panel.background){
33622             this.setActivePanel(panel);
33623             if(this.config.initialSize && this.panels.getCount()==1){
33624                 this.resizeTo(this.config.initialSize);
33625             }
33626         }
33627         this.fireEvent("paneladded", this, panel);
33628         return panel;
33629     },
33630     
33631     /**
33632      * Returns true if the panel is in this region.
33633      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33634      * @return {Boolean}
33635      */
33636     hasPanel : function(panel){
33637         if(typeof panel == "object"){ // must be panel obj
33638             panel = panel.getId();
33639         }
33640         return this.getPanel(panel) ? true : false;
33641     },
33642     
33643     /**
33644      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33645      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33646      * @param {Boolean} preservePanel Overrides the config preservePanel option
33647      * @return {Roo.ContentPanel} The panel that was removed
33648      */
33649     remove : function(panel, preservePanel){
33650         panel = this.getPanel(panel);
33651         if(!panel){
33652             return null;
33653         }
33654         var e = {};
33655         this.fireEvent("beforeremove", this, panel, e);
33656         if(e.cancel === true){
33657             return null;
33658         }
33659         var panelId = panel.getId();
33660         this.panels.removeKey(panelId);
33661         return panel;
33662     },
33663     
33664     /**
33665      * Returns the panel specified or null if it's not in this region.
33666      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33667      * @return {Roo.ContentPanel}
33668      */
33669     getPanel : function(id){
33670         if(typeof id == "object"){ // must be panel obj
33671             return id;
33672         }
33673         return this.panels.get(id);
33674     },
33675     
33676     /**
33677      * Returns this regions position (north/south/east/west/center).
33678      * @return {String} 
33679      */
33680     getPosition: function(){
33681         return this.position;    
33682     }
33683 });/*
33684  * Based on:
33685  * Ext JS Library 1.1.1
33686  * Copyright(c) 2006-2007, Ext JS, LLC.
33687  *
33688  * Originally Released Under LGPL - original licence link has changed is not relivant.
33689  *
33690  * Fork - LGPL
33691  * <script type="text/javascript">
33692  */
33693  
33694 /**
33695  * @class Roo.LayoutRegion
33696  * @extends Roo.BasicLayoutRegion
33697  * This class represents a region in a layout manager.
33698  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33699  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33700  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33701  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33702  * @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})
33703  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33704  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33705  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33706  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33707  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33708  * @cfg {String}    title           The title for the region (overrides panel titles)
33709  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33710  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33711  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33712  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33713  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33714  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33715  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33716  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33717  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33718  * @cfg {Boolean}   showPin         True to show a pin button
33719  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33720  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33721  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33722  * @cfg {Number}    width           For East/West panels
33723  * @cfg {Number}    height          For North/South panels
33724  * @cfg {Boolean}   split           To show the splitter
33725  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33726  */
33727 Roo.LayoutRegion = function(mgr, config, pos){
33728     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33729     var dh = Roo.DomHelper;
33730     /** This region's container element 
33731     * @type Roo.Element */
33732     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33733     /** This region's title element 
33734     * @type Roo.Element */
33735
33736     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33737         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33738         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33739     ]}, true);
33740     this.titleEl.enableDisplayMode();
33741     /** This region's title text element 
33742     * @type HTMLElement */
33743     this.titleTextEl = this.titleEl.dom.firstChild;
33744     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33745     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33746     this.closeBtn.enableDisplayMode();
33747     this.closeBtn.on("click", this.closeClicked, this);
33748     this.closeBtn.hide();
33749
33750     this.createBody(config);
33751     this.visible = true;
33752     this.collapsed = false;
33753
33754     if(config.hideWhenEmpty){
33755         this.hide();
33756         this.on("paneladded", this.validateVisibility, this);
33757         this.on("panelremoved", this.validateVisibility, this);
33758     }
33759     this.applyConfig(config);
33760 };
33761
33762 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33763
33764     createBody : function(){
33765         /** This region's body element 
33766         * @type Roo.Element */
33767         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33768     },
33769
33770     applyConfig : function(c){
33771         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33772             var dh = Roo.DomHelper;
33773             if(c.titlebar !== false){
33774                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33775                 this.collapseBtn.on("click", this.collapse, this);
33776                 this.collapseBtn.enableDisplayMode();
33777
33778                 if(c.showPin === true || this.showPin){
33779                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33780                     this.stickBtn.enableDisplayMode();
33781                     this.stickBtn.on("click", this.expand, this);
33782                     this.stickBtn.hide();
33783                 }
33784             }
33785             /** This region's collapsed element
33786             * @type Roo.Element */
33787             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33788                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33789             ]}, true);
33790             if(c.floatable !== false){
33791                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33792                this.collapsedEl.on("click", this.collapseClick, this);
33793             }
33794
33795             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33796                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33797                    id: "message", unselectable: "on", style:{"float":"left"}});
33798                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33799              }
33800             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33801             this.expandBtn.on("click", this.expand, this);
33802         }
33803         if(this.collapseBtn){
33804             this.collapseBtn.setVisible(c.collapsible == true);
33805         }
33806         this.cmargins = c.cmargins || this.cmargins ||
33807                          (this.position == "west" || this.position == "east" ?
33808                              {top: 0, left: 2, right:2, bottom: 0} :
33809                              {top: 2, left: 0, right:0, bottom: 2});
33810         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33811         this.bottomTabs = c.tabPosition != "top";
33812         this.autoScroll = c.autoScroll || false;
33813         if(this.autoScroll){
33814             this.bodyEl.setStyle("overflow", "auto");
33815         }else{
33816             this.bodyEl.setStyle("overflow", "hidden");
33817         }
33818         //if(c.titlebar !== false){
33819             if((!c.titlebar && !c.title) || c.titlebar === false){
33820                 this.titleEl.hide();
33821             }else{
33822                 this.titleEl.show();
33823                 if(c.title){
33824                     this.titleTextEl.innerHTML = c.title;
33825                 }
33826             }
33827         //}
33828         this.duration = c.duration || .30;
33829         this.slideDuration = c.slideDuration || .45;
33830         this.config = c;
33831         if(c.collapsed){
33832             this.collapse(true);
33833         }
33834         if(c.hidden){
33835             this.hide();
33836         }
33837     },
33838     /**
33839      * Returns true if this region is currently visible.
33840      * @return {Boolean}
33841      */
33842     isVisible : function(){
33843         return this.visible;
33844     },
33845
33846     /**
33847      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33848      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33849      */
33850     setCollapsedTitle : function(title){
33851         title = title || "&#160;";
33852         if(this.collapsedTitleTextEl){
33853             this.collapsedTitleTextEl.innerHTML = title;
33854         }
33855     },
33856
33857     getBox : function(){
33858         var b;
33859         if(!this.collapsed){
33860             b = this.el.getBox(false, true);
33861         }else{
33862             b = this.collapsedEl.getBox(false, true);
33863         }
33864         return b;
33865     },
33866
33867     getMargins : function(){
33868         return this.collapsed ? this.cmargins : this.margins;
33869     },
33870
33871     highlight : function(){
33872         this.el.addClass("x-layout-panel-dragover");
33873     },
33874
33875     unhighlight : function(){
33876         this.el.removeClass("x-layout-panel-dragover");
33877     },
33878
33879     updateBox : function(box){
33880         this.box = box;
33881         if(!this.collapsed){
33882             this.el.dom.style.left = box.x + "px";
33883             this.el.dom.style.top = box.y + "px";
33884             this.updateBody(box.width, box.height);
33885         }else{
33886             this.collapsedEl.dom.style.left = box.x + "px";
33887             this.collapsedEl.dom.style.top = box.y + "px";
33888             this.collapsedEl.setSize(box.width, box.height);
33889         }
33890         if(this.tabs){
33891             this.tabs.autoSizeTabs();
33892         }
33893     },
33894
33895     updateBody : function(w, h){
33896         if(w !== null){
33897             this.el.setWidth(w);
33898             w -= this.el.getBorderWidth("rl");
33899             if(this.config.adjustments){
33900                 w += this.config.adjustments[0];
33901             }
33902         }
33903         if(h !== null){
33904             this.el.setHeight(h);
33905             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33906             h -= this.el.getBorderWidth("tb");
33907             if(this.config.adjustments){
33908                 h += this.config.adjustments[1];
33909             }
33910             this.bodyEl.setHeight(h);
33911             if(this.tabs){
33912                 h = this.tabs.syncHeight(h);
33913             }
33914         }
33915         if(this.panelSize){
33916             w = w !== null ? w : this.panelSize.width;
33917             h = h !== null ? h : this.panelSize.height;
33918         }
33919         if(this.activePanel){
33920             var el = this.activePanel.getEl();
33921             w = w !== null ? w : el.getWidth();
33922             h = h !== null ? h : el.getHeight();
33923             this.panelSize = {width: w, height: h};
33924             this.activePanel.setSize(w, h);
33925         }
33926         if(Roo.isIE && this.tabs){
33927             this.tabs.el.repaint();
33928         }
33929     },
33930
33931     /**
33932      * Returns the container element for this region.
33933      * @return {Roo.Element}
33934      */
33935     getEl : function(){
33936         return this.el;
33937     },
33938
33939     /**
33940      * Hides this region.
33941      */
33942     hide : function(){
33943         if(!this.collapsed){
33944             this.el.dom.style.left = "-2000px";
33945             this.el.hide();
33946         }else{
33947             this.collapsedEl.dom.style.left = "-2000px";
33948             this.collapsedEl.hide();
33949         }
33950         this.visible = false;
33951         this.fireEvent("visibilitychange", this, false);
33952     },
33953
33954     /**
33955      * Shows this region if it was previously hidden.
33956      */
33957     show : function(){
33958         if(!this.collapsed){
33959             this.el.show();
33960         }else{
33961             this.collapsedEl.show();
33962         }
33963         this.visible = true;
33964         this.fireEvent("visibilitychange", this, true);
33965     },
33966
33967     closeClicked : function(){
33968         if(this.activePanel){
33969             this.remove(this.activePanel);
33970         }
33971     },
33972
33973     collapseClick : function(e){
33974         if(this.isSlid){
33975            e.stopPropagation();
33976            this.slideIn();
33977         }else{
33978            e.stopPropagation();
33979            this.slideOut();
33980         }
33981     },
33982
33983     /**
33984      * Collapses this region.
33985      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33986      */
33987     collapse : function(skipAnim){
33988         if(this.collapsed) return;
33989         this.collapsed = true;
33990         if(this.split){
33991             this.split.el.hide();
33992         }
33993         if(this.config.animate && skipAnim !== true){
33994             this.fireEvent("invalidated", this);
33995             this.animateCollapse();
33996         }else{
33997             this.el.setLocation(-20000,-20000);
33998             this.el.hide();
33999             this.collapsedEl.show();
34000             this.fireEvent("collapsed", this);
34001             this.fireEvent("invalidated", this);
34002         }
34003     },
34004
34005     animateCollapse : function(){
34006         // overridden
34007     },
34008
34009     /**
34010      * Expands this region if it was previously collapsed.
34011      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34012      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34013      */
34014     expand : function(e, skipAnim){
34015         if(e) e.stopPropagation();
34016         if(!this.collapsed || this.el.hasActiveFx()) return;
34017         if(this.isSlid){
34018             this.afterSlideIn();
34019             skipAnim = true;
34020         }
34021         this.collapsed = false;
34022         if(this.config.animate && skipAnim !== true){
34023             this.animateExpand();
34024         }else{
34025             this.el.show();
34026             if(this.split){
34027                 this.split.el.show();
34028             }
34029             this.collapsedEl.setLocation(-2000,-2000);
34030             this.collapsedEl.hide();
34031             this.fireEvent("invalidated", this);
34032             this.fireEvent("expanded", this);
34033         }
34034     },
34035
34036     animateExpand : function(){
34037         // overridden
34038     },
34039
34040     initTabs : function()
34041     {
34042         this.bodyEl.setStyle("overflow", "hidden");
34043         var ts = new Roo.TabPanel(
34044                 this.bodyEl.dom,
34045                 {
34046                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34047                     disableTooltips: this.config.disableTabTips,
34048                     toolbar : this.config.toolbar
34049                 }
34050         );
34051         if(this.config.hideTabs){
34052             ts.stripWrap.setDisplayed(false);
34053         }
34054         this.tabs = ts;
34055         ts.resizeTabs = this.config.resizeTabs === true;
34056         ts.minTabWidth = this.config.minTabWidth || 40;
34057         ts.maxTabWidth = this.config.maxTabWidth || 250;
34058         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34059         ts.monitorResize = false;
34060         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34061         ts.bodyEl.addClass('x-layout-tabs-body');
34062         this.panels.each(this.initPanelAsTab, this);
34063     },
34064
34065     initPanelAsTab : function(panel){
34066         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34067                     this.config.closeOnTab && panel.isClosable());
34068         if(panel.tabTip !== undefined){
34069             ti.setTooltip(panel.tabTip);
34070         }
34071         ti.on("activate", function(){
34072               this.setActivePanel(panel);
34073         }, this);
34074         if(this.config.closeOnTab){
34075             ti.on("beforeclose", function(t, e){
34076                 e.cancel = true;
34077                 this.remove(panel);
34078             }, this);
34079         }
34080         return ti;
34081     },
34082
34083     updatePanelTitle : function(panel, title){
34084         if(this.activePanel == panel){
34085             this.updateTitle(title);
34086         }
34087         if(this.tabs){
34088             var ti = this.tabs.getTab(panel.getEl().id);
34089             ti.setText(title);
34090             if(panel.tabTip !== undefined){
34091                 ti.setTooltip(panel.tabTip);
34092             }
34093         }
34094     },
34095
34096     updateTitle : function(title){
34097         if(this.titleTextEl && !this.config.title){
34098             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34099         }
34100     },
34101
34102     setActivePanel : function(panel){
34103         panel = this.getPanel(panel);
34104         if(this.activePanel && this.activePanel != panel){
34105             this.activePanel.setActiveState(false);
34106         }
34107         this.activePanel = panel;
34108         panel.setActiveState(true);
34109         if(this.panelSize){
34110             panel.setSize(this.panelSize.width, this.panelSize.height);
34111         }
34112         if(this.closeBtn){
34113             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34114         }
34115         this.updateTitle(panel.getTitle());
34116         if(this.tabs){
34117             this.fireEvent("invalidated", this);
34118         }
34119         this.fireEvent("panelactivated", this, panel);
34120     },
34121
34122     /**
34123      * Shows the specified panel.
34124      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34125      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34126      */
34127     showPanel : function(panel)
34128     {
34129         panel = this.getPanel(panel);
34130         if(panel){
34131             if(this.tabs){
34132                 var tab = this.tabs.getTab(panel.getEl().id);
34133                 if(tab.isHidden()){
34134                     this.tabs.unhideTab(tab.id);
34135                 }
34136                 tab.activate();
34137             }else{
34138                 this.setActivePanel(panel);
34139             }
34140         }
34141         return panel;
34142     },
34143
34144     /**
34145      * Get the active panel for this region.
34146      * @return {Roo.ContentPanel} The active panel or null
34147      */
34148     getActivePanel : function(){
34149         return this.activePanel;
34150     },
34151
34152     validateVisibility : function(){
34153         if(this.panels.getCount() < 1){
34154             this.updateTitle("&#160;");
34155             this.closeBtn.hide();
34156             this.hide();
34157         }else{
34158             if(!this.isVisible()){
34159                 this.show();
34160             }
34161         }
34162     },
34163
34164     /**
34165      * Adds the passed ContentPanel(s) to this region.
34166      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34167      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34168      */
34169     add : function(panel){
34170         if(arguments.length > 1){
34171             for(var i = 0, len = arguments.length; i < len; i++) {
34172                 this.add(arguments[i]);
34173             }
34174             return null;
34175         }
34176         if(this.hasPanel(panel)){
34177             this.showPanel(panel);
34178             return panel;
34179         }
34180         panel.setRegion(this);
34181         this.panels.add(panel);
34182         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34183             this.bodyEl.dom.appendChild(panel.getEl().dom);
34184             if(panel.background !== true){
34185                 this.setActivePanel(panel);
34186             }
34187             this.fireEvent("paneladded", this, panel);
34188             return panel;
34189         }
34190         if(!this.tabs){
34191             this.initTabs();
34192         }else{
34193             this.initPanelAsTab(panel);
34194         }
34195         if(panel.background !== true){
34196             this.tabs.activate(panel.getEl().id);
34197         }
34198         this.fireEvent("paneladded", this, panel);
34199         return panel;
34200     },
34201
34202     /**
34203      * Hides the tab for the specified panel.
34204      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34205      */
34206     hidePanel : function(panel){
34207         if(this.tabs && (panel = this.getPanel(panel))){
34208             this.tabs.hideTab(panel.getEl().id);
34209         }
34210     },
34211
34212     /**
34213      * Unhides the tab for a previously hidden panel.
34214      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34215      */
34216     unhidePanel : function(panel){
34217         if(this.tabs && (panel = this.getPanel(panel))){
34218             this.tabs.unhideTab(panel.getEl().id);
34219         }
34220     },
34221
34222     clearPanels : function(){
34223         while(this.panels.getCount() > 0){
34224              this.remove(this.panels.first());
34225         }
34226     },
34227
34228     /**
34229      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34230      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34231      * @param {Boolean} preservePanel Overrides the config preservePanel option
34232      * @return {Roo.ContentPanel} The panel that was removed
34233      */
34234     remove : function(panel, preservePanel){
34235         panel = this.getPanel(panel);
34236         if(!panel){
34237             return null;
34238         }
34239         var e = {};
34240         this.fireEvent("beforeremove", this, panel, e);
34241         if(e.cancel === true){
34242             return null;
34243         }
34244         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34245         var panelId = panel.getId();
34246         this.panels.removeKey(panelId);
34247         if(preservePanel){
34248             document.body.appendChild(panel.getEl().dom);
34249         }
34250         if(this.tabs){
34251             this.tabs.removeTab(panel.getEl().id);
34252         }else if (!preservePanel){
34253             this.bodyEl.dom.removeChild(panel.getEl().dom);
34254         }
34255         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34256             var p = this.panels.first();
34257             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34258             tempEl.appendChild(p.getEl().dom);
34259             this.bodyEl.update("");
34260             this.bodyEl.dom.appendChild(p.getEl().dom);
34261             tempEl = null;
34262             this.updateTitle(p.getTitle());
34263             this.tabs = null;
34264             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34265             this.setActivePanel(p);
34266         }
34267         panel.setRegion(null);
34268         if(this.activePanel == panel){
34269             this.activePanel = null;
34270         }
34271         if(this.config.autoDestroy !== false && preservePanel !== true){
34272             try{panel.destroy();}catch(e){}
34273         }
34274         this.fireEvent("panelremoved", this, panel);
34275         return panel;
34276     },
34277
34278     /**
34279      * Returns the TabPanel component used by this region
34280      * @return {Roo.TabPanel}
34281      */
34282     getTabs : function(){
34283         return this.tabs;
34284     },
34285
34286     createTool : function(parentEl, className){
34287         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34288             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34289         btn.addClassOnOver("x-layout-tools-button-over");
34290         return btn;
34291     }
34292 });/*
34293  * Based on:
34294  * Ext JS Library 1.1.1
34295  * Copyright(c) 2006-2007, Ext JS, LLC.
34296  *
34297  * Originally Released Under LGPL - original licence link has changed is not relivant.
34298  *
34299  * Fork - LGPL
34300  * <script type="text/javascript">
34301  */
34302  
34303
34304
34305 /**
34306  * @class Roo.SplitLayoutRegion
34307  * @extends Roo.LayoutRegion
34308  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34309  */
34310 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34311     this.cursor = cursor;
34312     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34313 };
34314
34315 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34316     splitTip : "Drag to resize.",
34317     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34318     useSplitTips : false,
34319
34320     applyConfig : function(config){
34321         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34322         if(config.split){
34323             if(!this.split){
34324                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34325                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34326                 /** The SplitBar for this region 
34327                 * @type Roo.SplitBar */
34328                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34329                 this.split.on("moved", this.onSplitMove, this);
34330                 this.split.useShim = config.useShim === true;
34331                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34332                 if(this.useSplitTips){
34333                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34334                 }
34335                 if(config.collapsible){
34336                     this.split.el.on("dblclick", this.collapse,  this);
34337                 }
34338             }
34339             if(typeof config.minSize != "undefined"){
34340                 this.split.minSize = config.minSize;
34341             }
34342             if(typeof config.maxSize != "undefined"){
34343                 this.split.maxSize = config.maxSize;
34344             }
34345             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34346                 this.hideSplitter();
34347             }
34348         }
34349     },
34350
34351     getHMaxSize : function(){
34352          var cmax = this.config.maxSize || 10000;
34353          var center = this.mgr.getRegion("center");
34354          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34355     },
34356
34357     getVMaxSize : function(){
34358          var cmax = this.config.maxSize || 10000;
34359          var center = this.mgr.getRegion("center");
34360          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34361     },
34362
34363     onSplitMove : function(split, newSize){
34364         this.fireEvent("resized", this, newSize);
34365     },
34366     
34367     /** 
34368      * Returns the {@link Roo.SplitBar} for this region.
34369      * @return {Roo.SplitBar}
34370      */
34371     getSplitBar : function(){
34372         return this.split;
34373     },
34374     
34375     hide : function(){
34376         this.hideSplitter();
34377         Roo.SplitLayoutRegion.superclass.hide.call(this);
34378     },
34379
34380     hideSplitter : function(){
34381         if(this.split){
34382             this.split.el.setLocation(-2000,-2000);
34383             this.split.el.hide();
34384         }
34385     },
34386
34387     show : function(){
34388         if(this.split){
34389             this.split.el.show();
34390         }
34391         Roo.SplitLayoutRegion.superclass.show.call(this);
34392     },
34393     
34394     beforeSlide: function(){
34395         if(Roo.isGecko){// firefox overflow auto bug workaround
34396             this.bodyEl.clip();
34397             if(this.tabs) this.tabs.bodyEl.clip();
34398             if(this.activePanel){
34399                 this.activePanel.getEl().clip();
34400                 
34401                 if(this.activePanel.beforeSlide){
34402                     this.activePanel.beforeSlide();
34403                 }
34404             }
34405         }
34406     },
34407     
34408     afterSlide : function(){
34409         if(Roo.isGecko){// firefox overflow auto bug workaround
34410             this.bodyEl.unclip();
34411             if(this.tabs) this.tabs.bodyEl.unclip();
34412             if(this.activePanel){
34413                 this.activePanel.getEl().unclip();
34414                 if(this.activePanel.afterSlide){
34415                     this.activePanel.afterSlide();
34416                 }
34417             }
34418         }
34419     },
34420
34421     initAutoHide : function(){
34422         if(this.autoHide !== false){
34423             if(!this.autoHideHd){
34424                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34425                 this.autoHideHd = {
34426                     "mouseout": function(e){
34427                         if(!e.within(this.el, true)){
34428                             st.delay(500);
34429                         }
34430                     },
34431                     "mouseover" : function(e){
34432                         st.cancel();
34433                     },
34434                     scope : this
34435                 };
34436             }
34437             this.el.on(this.autoHideHd);
34438         }
34439     },
34440
34441     clearAutoHide : function(){
34442         if(this.autoHide !== false){
34443             this.el.un("mouseout", this.autoHideHd.mouseout);
34444             this.el.un("mouseover", this.autoHideHd.mouseover);
34445         }
34446     },
34447
34448     clearMonitor : function(){
34449         Roo.get(document).un("click", this.slideInIf, this);
34450     },
34451
34452     // these names are backwards but not changed for compat
34453     slideOut : function(){
34454         if(this.isSlid || this.el.hasActiveFx()){
34455             return;
34456         }
34457         this.isSlid = true;
34458         if(this.collapseBtn){
34459             this.collapseBtn.hide();
34460         }
34461         this.closeBtnState = this.closeBtn.getStyle('display');
34462         this.closeBtn.hide();
34463         if(this.stickBtn){
34464             this.stickBtn.show();
34465         }
34466         this.el.show();
34467         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34468         this.beforeSlide();
34469         this.el.setStyle("z-index", 10001);
34470         this.el.slideIn(this.getSlideAnchor(), {
34471             callback: function(){
34472                 this.afterSlide();
34473                 this.initAutoHide();
34474                 Roo.get(document).on("click", this.slideInIf, this);
34475                 this.fireEvent("slideshow", this);
34476             },
34477             scope: this,
34478             block: true
34479         });
34480     },
34481
34482     afterSlideIn : function(){
34483         this.clearAutoHide();
34484         this.isSlid = false;
34485         this.clearMonitor();
34486         this.el.setStyle("z-index", "");
34487         if(this.collapseBtn){
34488             this.collapseBtn.show();
34489         }
34490         this.closeBtn.setStyle('display', this.closeBtnState);
34491         if(this.stickBtn){
34492             this.stickBtn.hide();
34493         }
34494         this.fireEvent("slidehide", this);
34495     },
34496
34497     slideIn : function(cb){
34498         if(!this.isSlid || this.el.hasActiveFx()){
34499             Roo.callback(cb);
34500             return;
34501         }
34502         this.isSlid = false;
34503         this.beforeSlide();
34504         this.el.slideOut(this.getSlideAnchor(), {
34505             callback: function(){
34506                 this.el.setLeftTop(-10000, -10000);
34507                 this.afterSlide();
34508                 this.afterSlideIn();
34509                 Roo.callback(cb);
34510             },
34511             scope: this,
34512             block: true
34513         });
34514     },
34515     
34516     slideInIf : function(e){
34517         if(!e.within(this.el)){
34518             this.slideIn();
34519         }
34520     },
34521
34522     animateCollapse : function(){
34523         this.beforeSlide();
34524         this.el.setStyle("z-index", 20000);
34525         var anchor = this.getSlideAnchor();
34526         this.el.slideOut(anchor, {
34527             callback : function(){
34528                 this.el.setStyle("z-index", "");
34529                 this.collapsedEl.slideIn(anchor, {duration:.3});
34530                 this.afterSlide();
34531                 this.el.setLocation(-10000,-10000);
34532                 this.el.hide();
34533                 this.fireEvent("collapsed", this);
34534             },
34535             scope: this,
34536             block: true
34537         });
34538     },
34539
34540     animateExpand : function(){
34541         this.beforeSlide();
34542         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34543         this.el.setStyle("z-index", 20000);
34544         this.collapsedEl.hide({
34545             duration:.1
34546         });
34547         this.el.slideIn(this.getSlideAnchor(), {
34548             callback : function(){
34549                 this.el.setStyle("z-index", "");
34550                 this.afterSlide();
34551                 if(this.split){
34552                     this.split.el.show();
34553                 }
34554                 this.fireEvent("invalidated", this);
34555                 this.fireEvent("expanded", this);
34556             },
34557             scope: this,
34558             block: true
34559         });
34560     },
34561
34562     anchors : {
34563         "west" : "left",
34564         "east" : "right",
34565         "north" : "top",
34566         "south" : "bottom"
34567     },
34568
34569     sanchors : {
34570         "west" : "l",
34571         "east" : "r",
34572         "north" : "t",
34573         "south" : "b"
34574     },
34575
34576     canchors : {
34577         "west" : "tl-tr",
34578         "east" : "tr-tl",
34579         "north" : "tl-bl",
34580         "south" : "bl-tl"
34581     },
34582
34583     getAnchor : function(){
34584         return this.anchors[this.position];
34585     },
34586
34587     getCollapseAnchor : function(){
34588         return this.canchors[this.position];
34589     },
34590
34591     getSlideAnchor : function(){
34592         return this.sanchors[this.position];
34593     },
34594
34595     getAlignAdj : function(){
34596         var cm = this.cmargins;
34597         switch(this.position){
34598             case "west":
34599                 return [0, 0];
34600             break;
34601             case "east":
34602                 return [0, 0];
34603             break;
34604             case "north":
34605                 return [0, 0];
34606             break;
34607             case "south":
34608                 return [0, 0];
34609             break;
34610         }
34611     },
34612
34613     getExpandAdj : function(){
34614         var c = this.collapsedEl, cm = this.cmargins;
34615         switch(this.position){
34616             case "west":
34617                 return [-(cm.right+c.getWidth()+cm.left), 0];
34618             break;
34619             case "east":
34620                 return [cm.right+c.getWidth()+cm.left, 0];
34621             break;
34622             case "north":
34623                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34624             break;
34625             case "south":
34626                 return [0, cm.top+cm.bottom+c.getHeight()];
34627             break;
34628         }
34629     }
34630 });/*
34631  * Based on:
34632  * Ext JS Library 1.1.1
34633  * Copyright(c) 2006-2007, Ext JS, LLC.
34634  *
34635  * Originally Released Under LGPL - original licence link has changed is not relivant.
34636  *
34637  * Fork - LGPL
34638  * <script type="text/javascript">
34639  */
34640 /*
34641  * These classes are private internal classes
34642  */
34643 Roo.CenterLayoutRegion = function(mgr, config){
34644     Roo.LayoutRegion.call(this, mgr, config, "center");
34645     this.visible = true;
34646     this.minWidth = config.minWidth || 20;
34647     this.minHeight = config.minHeight || 20;
34648 };
34649
34650 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34651     hide : function(){
34652         // center panel can't be hidden
34653     },
34654     
34655     show : function(){
34656         // center panel can't be hidden
34657     },
34658     
34659     getMinWidth: function(){
34660         return this.minWidth;
34661     },
34662     
34663     getMinHeight: function(){
34664         return this.minHeight;
34665     }
34666 });
34667
34668
34669 Roo.NorthLayoutRegion = function(mgr, config){
34670     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34671     if(this.split){
34672         this.split.placement = Roo.SplitBar.TOP;
34673         this.split.orientation = Roo.SplitBar.VERTICAL;
34674         this.split.el.addClass("x-layout-split-v");
34675     }
34676     var size = config.initialSize || config.height;
34677     if(typeof size != "undefined"){
34678         this.el.setHeight(size);
34679     }
34680 };
34681 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34682     orientation: Roo.SplitBar.VERTICAL,
34683     getBox : function(){
34684         if(this.collapsed){
34685             return this.collapsedEl.getBox();
34686         }
34687         var box = this.el.getBox();
34688         if(this.split){
34689             box.height += this.split.el.getHeight();
34690         }
34691         return box;
34692     },
34693     
34694     updateBox : function(box){
34695         if(this.split && !this.collapsed){
34696             box.height -= this.split.el.getHeight();
34697             this.split.el.setLeft(box.x);
34698             this.split.el.setTop(box.y+box.height);
34699             this.split.el.setWidth(box.width);
34700         }
34701         if(this.collapsed){
34702             this.updateBody(box.width, null);
34703         }
34704         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34705     }
34706 });
34707
34708 Roo.SouthLayoutRegion = function(mgr, config){
34709     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34710     if(this.split){
34711         this.split.placement = Roo.SplitBar.BOTTOM;
34712         this.split.orientation = Roo.SplitBar.VERTICAL;
34713         this.split.el.addClass("x-layout-split-v");
34714     }
34715     var size = config.initialSize || config.height;
34716     if(typeof size != "undefined"){
34717         this.el.setHeight(size);
34718     }
34719 };
34720 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34721     orientation: Roo.SplitBar.VERTICAL,
34722     getBox : function(){
34723         if(this.collapsed){
34724             return this.collapsedEl.getBox();
34725         }
34726         var box = this.el.getBox();
34727         if(this.split){
34728             var sh = this.split.el.getHeight();
34729             box.height += sh;
34730             box.y -= sh;
34731         }
34732         return box;
34733     },
34734     
34735     updateBox : function(box){
34736         if(this.split && !this.collapsed){
34737             var sh = this.split.el.getHeight();
34738             box.height -= sh;
34739             box.y += sh;
34740             this.split.el.setLeft(box.x);
34741             this.split.el.setTop(box.y-sh);
34742             this.split.el.setWidth(box.width);
34743         }
34744         if(this.collapsed){
34745             this.updateBody(box.width, null);
34746         }
34747         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34748     }
34749 });
34750
34751 Roo.EastLayoutRegion = function(mgr, config){
34752     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34753     if(this.split){
34754         this.split.placement = Roo.SplitBar.RIGHT;
34755         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34756         this.split.el.addClass("x-layout-split-h");
34757     }
34758     var size = config.initialSize || config.width;
34759     if(typeof size != "undefined"){
34760         this.el.setWidth(size);
34761     }
34762 };
34763 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34764     orientation: Roo.SplitBar.HORIZONTAL,
34765     getBox : function(){
34766         if(this.collapsed){
34767             return this.collapsedEl.getBox();
34768         }
34769         var box = this.el.getBox();
34770         if(this.split){
34771             var sw = this.split.el.getWidth();
34772             box.width += sw;
34773             box.x -= sw;
34774         }
34775         return box;
34776     },
34777
34778     updateBox : function(box){
34779         if(this.split && !this.collapsed){
34780             var sw = this.split.el.getWidth();
34781             box.width -= sw;
34782             this.split.el.setLeft(box.x);
34783             this.split.el.setTop(box.y);
34784             this.split.el.setHeight(box.height);
34785             box.x += sw;
34786         }
34787         if(this.collapsed){
34788             this.updateBody(null, box.height);
34789         }
34790         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34791     }
34792 });
34793
34794 Roo.WestLayoutRegion = function(mgr, config){
34795     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34796     if(this.split){
34797         this.split.placement = Roo.SplitBar.LEFT;
34798         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34799         this.split.el.addClass("x-layout-split-h");
34800     }
34801     var size = config.initialSize || config.width;
34802     if(typeof size != "undefined"){
34803         this.el.setWidth(size);
34804     }
34805 };
34806 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34807     orientation: Roo.SplitBar.HORIZONTAL,
34808     getBox : function(){
34809         if(this.collapsed){
34810             return this.collapsedEl.getBox();
34811         }
34812         var box = this.el.getBox();
34813         if(this.split){
34814             box.width += this.split.el.getWidth();
34815         }
34816         return box;
34817     },
34818     
34819     updateBox : function(box){
34820         if(this.split && !this.collapsed){
34821             var sw = this.split.el.getWidth();
34822             box.width -= sw;
34823             this.split.el.setLeft(box.x+box.width);
34824             this.split.el.setTop(box.y);
34825             this.split.el.setHeight(box.height);
34826         }
34827         if(this.collapsed){
34828             this.updateBody(null, box.height);
34829         }
34830         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34831     }
34832 });
34833 /*
34834  * Based on:
34835  * Ext JS Library 1.1.1
34836  * Copyright(c) 2006-2007, Ext JS, LLC.
34837  *
34838  * Originally Released Under LGPL - original licence link has changed is not relivant.
34839  *
34840  * Fork - LGPL
34841  * <script type="text/javascript">
34842  */
34843  
34844  
34845 /*
34846  * Private internal class for reading and applying state
34847  */
34848 Roo.LayoutStateManager = function(layout){
34849      // default empty state
34850      this.state = {
34851         north: {},
34852         south: {},
34853         east: {},
34854         west: {}       
34855     };
34856 };
34857
34858 Roo.LayoutStateManager.prototype = {
34859     init : function(layout, provider){
34860         this.provider = provider;
34861         var state = provider.get(layout.id+"-layout-state");
34862         if(state){
34863             var wasUpdating = layout.isUpdating();
34864             if(!wasUpdating){
34865                 layout.beginUpdate();
34866             }
34867             for(var key in state){
34868                 if(typeof state[key] != "function"){
34869                     var rstate = state[key];
34870                     var r = layout.getRegion(key);
34871                     if(r && rstate){
34872                         if(rstate.size){
34873                             r.resizeTo(rstate.size);
34874                         }
34875                         if(rstate.collapsed == true){
34876                             r.collapse(true);
34877                         }else{
34878                             r.expand(null, true);
34879                         }
34880                     }
34881                 }
34882             }
34883             if(!wasUpdating){
34884                 layout.endUpdate();
34885             }
34886             this.state = state; 
34887         }
34888         this.layout = layout;
34889         layout.on("regionresized", this.onRegionResized, this);
34890         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34891         layout.on("regionexpanded", this.onRegionExpanded, this);
34892     },
34893     
34894     storeState : function(){
34895         this.provider.set(this.layout.id+"-layout-state", this.state);
34896     },
34897     
34898     onRegionResized : function(region, newSize){
34899         this.state[region.getPosition()].size = newSize;
34900         this.storeState();
34901     },
34902     
34903     onRegionCollapsed : function(region){
34904         this.state[region.getPosition()].collapsed = true;
34905         this.storeState();
34906     },
34907     
34908     onRegionExpanded : function(region){
34909         this.state[region.getPosition()].collapsed = false;
34910         this.storeState();
34911     }
34912 };/*
34913  * Based on:
34914  * Ext JS Library 1.1.1
34915  * Copyright(c) 2006-2007, Ext JS, LLC.
34916  *
34917  * Originally Released Under LGPL - original licence link has changed is not relivant.
34918  *
34919  * Fork - LGPL
34920  * <script type="text/javascript">
34921  */
34922 /**
34923  * @class Roo.ContentPanel
34924  * @extends Roo.util.Observable
34925  * A basic ContentPanel element.
34926  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34927  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34928  * @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
34929  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34930  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34931  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34932  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34933  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34934  * @cfg {String} title          The title for this panel
34935  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34936  * @cfg {String} url            Calls {@link #setUrl} with this value
34937  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34938  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34939  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34940  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34941
34942  * @constructor
34943  * Create a new ContentPanel.
34944  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34945  * @param {String/Object} config A string to set only the title or a config object
34946  * @param {String} content (optional) Set the HTML content for this panel
34947  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34948  */
34949 Roo.ContentPanel = function(el, config, content){
34950     
34951      
34952     /*
34953     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34954         config = el;
34955         el = Roo.id();
34956     }
34957     if (config && config.parentLayout) { 
34958         el = config.parentLayout.el.createChild(); 
34959     }
34960     */
34961     if(el.autoCreate){ // xtype is available if this is called from factory
34962         config = el;
34963         el = Roo.id();
34964     }
34965     this.el = Roo.get(el);
34966     if(!this.el && config && config.autoCreate){
34967         if(typeof config.autoCreate == "object"){
34968             if(!config.autoCreate.id){
34969                 config.autoCreate.id = config.id||el;
34970             }
34971             this.el = Roo.DomHelper.append(document.body,
34972                         config.autoCreate, true);
34973         }else{
34974             this.el = Roo.DomHelper.append(document.body,
34975                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34976         }
34977     }
34978     this.closable = false;
34979     this.loaded = false;
34980     this.active = false;
34981     if(typeof config == "string"){
34982         this.title = config;
34983     }else{
34984         Roo.apply(this, config);
34985     }
34986     
34987     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34988         this.wrapEl = this.el.wrap();
34989         this.toolbar.container = this.el.insertSibling(false, 'before');
34990         this.toolbar = new Roo.Toolbar(this.toolbar);
34991     }
34992     
34993     // xtype created footer. - not sure if will work as we normally have to render first..
34994     if (this.footer && !this.footer.el && this.footer.xtype) {
34995         if (!this.wrapEl) {
34996             this.wrapEl = this.el.wrap();
34997         }
34998     
34999         this.footer.container = this.wrapEl.createChild();
35000          
35001         this.footer = Roo.factory(this.footer, Roo);
35002         
35003     }
35004     
35005     if(this.resizeEl){
35006         this.resizeEl = Roo.get(this.resizeEl, true);
35007     }else{
35008         this.resizeEl = this.el;
35009     }
35010     // handle view.xtype
35011     
35012  
35013     
35014     
35015     this.addEvents({
35016         /**
35017          * @event activate
35018          * Fires when this panel is activated. 
35019          * @param {Roo.ContentPanel} this
35020          */
35021         "activate" : true,
35022         /**
35023          * @event deactivate
35024          * Fires when this panel is activated. 
35025          * @param {Roo.ContentPanel} this
35026          */
35027         "deactivate" : true,
35028
35029         /**
35030          * @event resize
35031          * Fires when this panel is resized if fitToFrame is true.
35032          * @param {Roo.ContentPanel} this
35033          * @param {Number} width The width after any component adjustments
35034          * @param {Number} height The height after any component adjustments
35035          */
35036         "resize" : true,
35037         
35038          /**
35039          * @event render
35040          * Fires when this tab is created
35041          * @param {Roo.ContentPanel} this
35042          */
35043         "render" : true
35044         
35045         
35046         
35047     });
35048     
35049
35050     
35051     
35052     if(this.autoScroll){
35053         this.resizeEl.setStyle("overflow", "auto");
35054     } else {
35055         // fix randome scrolling
35056         this.el.on('scroll', function() {
35057             Roo.log('fix random scolling');
35058             this.scrollTo('top',0); 
35059         });
35060     }
35061     content = content || this.content;
35062     if(content){
35063         this.setContent(content);
35064     }
35065     if(config && config.url){
35066         this.setUrl(this.url, this.params, this.loadOnce);
35067     }
35068     
35069     
35070     
35071     Roo.ContentPanel.superclass.constructor.call(this);
35072     
35073     if (this.view && typeof(this.view.xtype) != 'undefined') {
35074         this.view.el = this.el.appendChild(document.createElement("div"));
35075         this.view = Roo.factory(this.view); 
35076         this.view.render  &&  this.view.render(false, '');  
35077     }
35078     
35079     
35080     this.fireEvent('render', this);
35081 };
35082
35083 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35084     tabTip:'',
35085     setRegion : function(region){
35086         this.region = region;
35087         if(region){
35088            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35089         }else{
35090            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35091         } 
35092     },
35093     
35094     /**
35095      * Returns the toolbar for this Panel if one was configured. 
35096      * @return {Roo.Toolbar} 
35097      */
35098     getToolbar : function(){
35099         return this.toolbar;
35100     },
35101     
35102     setActiveState : function(active){
35103         this.active = active;
35104         if(!active){
35105             this.fireEvent("deactivate", this);
35106         }else{
35107             this.fireEvent("activate", this);
35108         }
35109     },
35110     /**
35111      * Updates this panel's element
35112      * @param {String} content The new content
35113      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35114     */
35115     setContent : function(content, loadScripts){
35116         this.el.update(content, loadScripts);
35117     },
35118
35119     ignoreResize : function(w, h){
35120         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35121             return true;
35122         }else{
35123             this.lastSize = {width: w, height: h};
35124             return false;
35125         }
35126     },
35127     /**
35128      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35129      * @return {Roo.UpdateManager} The UpdateManager
35130      */
35131     getUpdateManager : function(){
35132         return this.el.getUpdateManager();
35133     },
35134      /**
35135      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35136      * @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:
35137 <pre><code>
35138 panel.load({
35139     url: "your-url.php",
35140     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35141     callback: yourFunction,
35142     scope: yourObject, //(optional scope)
35143     discardUrl: false,
35144     nocache: false,
35145     text: "Loading...",
35146     timeout: 30,
35147     scripts: false
35148 });
35149 </code></pre>
35150      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35151      * 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.
35152      * @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}
35153      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35154      * @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.
35155      * @return {Roo.ContentPanel} this
35156      */
35157     load : function(){
35158         var um = this.el.getUpdateManager();
35159         um.update.apply(um, arguments);
35160         return this;
35161     },
35162
35163
35164     /**
35165      * 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.
35166      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35167      * @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)
35168      * @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)
35169      * @return {Roo.UpdateManager} The UpdateManager
35170      */
35171     setUrl : function(url, params, loadOnce){
35172         if(this.refreshDelegate){
35173             this.removeListener("activate", this.refreshDelegate);
35174         }
35175         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35176         this.on("activate", this.refreshDelegate);
35177         return this.el.getUpdateManager();
35178     },
35179     
35180     _handleRefresh : function(url, params, loadOnce){
35181         if(!loadOnce || !this.loaded){
35182             var updater = this.el.getUpdateManager();
35183             updater.update(url, params, this._setLoaded.createDelegate(this));
35184         }
35185     },
35186     
35187     _setLoaded : function(){
35188         this.loaded = true;
35189     }, 
35190     
35191     /**
35192      * Returns this panel's id
35193      * @return {String} 
35194      */
35195     getId : function(){
35196         return this.el.id;
35197     },
35198     
35199     /** 
35200      * Returns this panel's element - used by regiosn to add.
35201      * @return {Roo.Element} 
35202      */
35203     getEl : function(){
35204         return this.wrapEl || this.el;
35205     },
35206     
35207     adjustForComponents : function(width, height)
35208     {
35209         //Roo.log('adjustForComponents ');
35210         if(this.resizeEl != this.el){
35211             width -= this.el.getFrameWidth('lr');
35212             height -= this.el.getFrameWidth('tb');
35213         }
35214         if(this.toolbar){
35215             var te = this.toolbar.getEl();
35216             height -= te.getHeight();
35217             te.setWidth(width);
35218         }
35219         if(this.footer){
35220             var te = this.footer.getEl();
35221             Roo.log("footer:" + te.getHeight());
35222             
35223             height -= te.getHeight();
35224             te.setWidth(width);
35225         }
35226         
35227         
35228         if(this.adjustments){
35229             width += this.adjustments[0];
35230             height += this.adjustments[1];
35231         }
35232         return {"width": width, "height": height};
35233     },
35234     
35235     setSize : function(width, height){
35236         if(this.fitToFrame && !this.ignoreResize(width, height)){
35237             if(this.fitContainer && this.resizeEl != this.el){
35238                 this.el.setSize(width, height);
35239             }
35240             var size = this.adjustForComponents(width, height);
35241             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35242             this.fireEvent('resize', this, size.width, size.height);
35243         }
35244     },
35245     
35246     /**
35247      * Returns this panel's title
35248      * @return {String} 
35249      */
35250     getTitle : function(){
35251         return this.title;
35252     },
35253     
35254     /**
35255      * Set this panel's title
35256      * @param {String} title
35257      */
35258     setTitle : function(title){
35259         this.title = title;
35260         if(this.region){
35261             this.region.updatePanelTitle(this, title);
35262         }
35263     },
35264     
35265     /**
35266      * Returns true is this panel was configured to be closable
35267      * @return {Boolean} 
35268      */
35269     isClosable : function(){
35270         return this.closable;
35271     },
35272     
35273     beforeSlide : function(){
35274         this.el.clip();
35275         this.resizeEl.clip();
35276     },
35277     
35278     afterSlide : function(){
35279         this.el.unclip();
35280         this.resizeEl.unclip();
35281     },
35282     
35283     /**
35284      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35285      *   Will fail silently if the {@link #setUrl} method has not been called.
35286      *   This does not activate the panel, just updates its content.
35287      */
35288     refresh : function(){
35289         if(this.refreshDelegate){
35290            this.loaded = false;
35291            this.refreshDelegate();
35292         }
35293     },
35294     
35295     /**
35296      * Destroys this panel
35297      */
35298     destroy : function(){
35299         this.el.removeAllListeners();
35300         var tempEl = document.createElement("span");
35301         tempEl.appendChild(this.el.dom);
35302         tempEl.innerHTML = "";
35303         this.el.remove();
35304         this.el = null;
35305     },
35306     
35307     /**
35308      * form - if the content panel contains a form - this is a reference to it.
35309      * @type {Roo.form.Form}
35310      */
35311     form : false,
35312     /**
35313      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35314      *    This contains a reference to it.
35315      * @type {Roo.View}
35316      */
35317     view : false,
35318     
35319       /**
35320      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35321      * <pre><code>
35322
35323 layout.addxtype({
35324        xtype : 'Form',
35325        items: [ .... ]
35326    }
35327 );
35328
35329 </code></pre>
35330      * @param {Object} cfg Xtype definition of item to add.
35331      */
35332     
35333     addxtype : function(cfg) {
35334         // add form..
35335         if (cfg.xtype.match(/^Form$/)) {
35336             
35337             var el;
35338             //if (this.footer) {
35339             //    el = this.footer.container.insertSibling(false, 'before');
35340             //} else {
35341                 el = this.el.createChild();
35342             //}
35343
35344             this.form = new  Roo.form.Form(cfg);
35345             
35346             
35347             if ( this.form.allItems.length) this.form.render(el.dom);
35348             return this.form;
35349         }
35350         // should only have one of theses..
35351         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35352             // views.. should not be just added - used named prop 'view''
35353             
35354             cfg.el = this.el.appendChild(document.createElement("div"));
35355             // factory?
35356             
35357             var ret = new Roo.factory(cfg);
35358              
35359              ret.render && ret.render(false, ''); // render blank..
35360             this.view = ret;
35361             return ret;
35362         }
35363         return false;
35364     }
35365 });
35366
35367 /**
35368  * @class Roo.GridPanel
35369  * @extends Roo.ContentPanel
35370  * @constructor
35371  * Create a new GridPanel.
35372  * @param {Roo.grid.Grid} grid The grid for this panel
35373  * @param {String/Object} config A string to set only the panel's title, or a config object
35374  */
35375 Roo.GridPanel = function(grid, config){
35376     
35377   
35378     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35379         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35380         
35381     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35382     
35383     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35384     
35385     if(this.toolbar){
35386         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35387     }
35388     // xtype created footer. - not sure if will work as we normally have to render first..
35389     if (this.footer && !this.footer.el && this.footer.xtype) {
35390         
35391         this.footer.container = this.grid.getView().getFooterPanel(true);
35392         this.footer.dataSource = this.grid.dataSource;
35393         this.footer = Roo.factory(this.footer, Roo);
35394         
35395     }
35396     
35397     grid.monitorWindowResize = false; // turn off autosizing
35398     grid.autoHeight = false;
35399     grid.autoWidth = false;
35400     this.grid = grid;
35401     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35402 };
35403
35404 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35405     getId : function(){
35406         return this.grid.id;
35407     },
35408     
35409     /**
35410      * Returns the grid for this panel
35411      * @return {Roo.grid.Grid} 
35412      */
35413     getGrid : function(){
35414         return this.grid;    
35415     },
35416     
35417     setSize : function(width, height){
35418         if(!this.ignoreResize(width, height)){
35419             var grid = this.grid;
35420             var size = this.adjustForComponents(width, height);
35421             grid.getGridEl().setSize(size.width, size.height);
35422             grid.autoSize();
35423         }
35424     },
35425     
35426     beforeSlide : function(){
35427         this.grid.getView().scroller.clip();
35428     },
35429     
35430     afterSlide : function(){
35431         this.grid.getView().scroller.unclip();
35432     },
35433     
35434     destroy : function(){
35435         this.grid.destroy();
35436         delete this.grid;
35437         Roo.GridPanel.superclass.destroy.call(this); 
35438     }
35439 });
35440
35441
35442 /**
35443  * @class Roo.NestedLayoutPanel
35444  * @extends Roo.ContentPanel
35445  * @constructor
35446  * Create a new NestedLayoutPanel.
35447  * 
35448  * 
35449  * @param {Roo.BorderLayout} layout The layout for this panel
35450  * @param {String/Object} config A string to set only the title or a config object
35451  */
35452 Roo.NestedLayoutPanel = function(layout, config)
35453 {
35454     // construct with only one argument..
35455     /* FIXME - implement nicer consturctors
35456     if (layout.layout) {
35457         config = layout;
35458         layout = config.layout;
35459         delete config.layout;
35460     }
35461     if (layout.xtype && !layout.getEl) {
35462         // then layout needs constructing..
35463         layout = Roo.factory(layout, Roo);
35464     }
35465     */
35466     
35467     
35468     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35469     
35470     layout.monitorWindowResize = false; // turn off autosizing
35471     this.layout = layout;
35472     this.layout.getEl().addClass("x-layout-nested-layout");
35473     
35474     
35475     
35476     
35477 };
35478
35479 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35480
35481     setSize : function(width, height){
35482         if(!this.ignoreResize(width, height)){
35483             var size = this.adjustForComponents(width, height);
35484             var el = this.layout.getEl();
35485             el.setSize(size.width, size.height);
35486             var touch = el.dom.offsetWidth;
35487             this.layout.layout();
35488             // ie requires a double layout on the first pass
35489             if(Roo.isIE && !this.initialized){
35490                 this.initialized = true;
35491                 this.layout.layout();
35492             }
35493         }
35494     },
35495     
35496     // activate all subpanels if not currently active..
35497     
35498     setActiveState : function(active){
35499         this.active = active;
35500         if(!active){
35501             this.fireEvent("deactivate", this);
35502             return;
35503         }
35504         
35505         this.fireEvent("activate", this);
35506         // not sure if this should happen before or after..
35507         if (!this.layout) {
35508             return; // should not happen..
35509         }
35510         var reg = false;
35511         for (var r in this.layout.regions) {
35512             reg = this.layout.getRegion(r);
35513             if (reg.getActivePanel()) {
35514                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35515                 reg.setActivePanel(reg.getActivePanel());
35516                 continue;
35517             }
35518             if (!reg.panels.length) {
35519                 continue;
35520             }
35521             reg.showPanel(reg.getPanel(0));
35522         }
35523         
35524         
35525         
35526         
35527     },
35528     
35529     /**
35530      * Returns the nested BorderLayout for this panel
35531      * @return {Roo.BorderLayout} 
35532      */
35533     getLayout : function(){
35534         return this.layout;
35535     },
35536     
35537      /**
35538      * Adds a xtype elements to the layout of the nested panel
35539      * <pre><code>
35540
35541 panel.addxtype({
35542        xtype : 'ContentPanel',
35543        region: 'west',
35544        items: [ .... ]
35545    }
35546 );
35547
35548 panel.addxtype({
35549         xtype : 'NestedLayoutPanel',
35550         region: 'west',
35551         layout: {
35552            center: { },
35553            west: { }   
35554         },
35555         items : [ ... list of content panels or nested layout panels.. ]
35556    }
35557 );
35558 </code></pre>
35559      * @param {Object} cfg Xtype definition of item to add.
35560      */
35561     addxtype : function(cfg) {
35562         return this.layout.addxtype(cfg);
35563     
35564     }
35565 });
35566
35567 Roo.ScrollPanel = function(el, config, content){
35568     config = config || {};
35569     config.fitToFrame = true;
35570     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35571     
35572     this.el.dom.style.overflow = "hidden";
35573     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35574     this.el.removeClass("x-layout-inactive-content");
35575     this.el.on("mousewheel", this.onWheel, this);
35576
35577     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35578     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35579     up.unselectable(); down.unselectable();
35580     up.on("click", this.scrollUp, this);
35581     down.on("click", this.scrollDown, this);
35582     up.addClassOnOver("x-scroller-btn-over");
35583     down.addClassOnOver("x-scroller-btn-over");
35584     up.addClassOnClick("x-scroller-btn-click");
35585     down.addClassOnClick("x-scroller-btn-click");
35586     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35587
35588     this.resizeEl = this.el;
35589     this.el = wrap; this.up = up; this.down = down;
35590 };
35591
35592 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35593     increment : 100,
35594     wheelIncrement : 5,
35595     scrollUp : function(){
35596         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35597     },
35598
35599     scrollDown : function(){
35600         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35601     },
35602
35603     afterScroll : function(){
35604         var el = this.resizeEl;
35605         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35606         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35607         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35608     },
35609
35610     setSize : function(){
35611         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35612         this.afterScroll();
35613     },
35614
35615     onWheel : function(e){
35616         var d = e.getWheelDelta();
35617         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35618         this.afterScroll();
35619         e.stopEvent();
35620     },
35621
35622     setContent : function(content, loadScripts){
35623         this.resizeEl.update(content, loadScripts);
35624     }
35625
35626 });
35627
35628
35629
35630
35631
35632
35633
35634
35635
35636 /**
35637  * @class Roo.TreePanel
35638  * @extends Roo.ContentPanel
35639  * @constructor
35640  * Create a new TreePanel. - defaults to fit/scoll contents.
35641  * @param {String/Object} config A string to set only the panel's title, or a config object
35642  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35643  */
35644 Roo.TreePanel = function(config){
35645     var el = config.el;
35646     var tree = config.tree;
35647     delete config.tree; 
35648     delete config.el; // hopefull!
35649     
35650     // wrapper for IE7 strict & safari scroll issue
35651     
35652     var treeEl = el.createChild();
35653     config.resizeEl = treeEl;
35654     
35655     
35656     
35657     Roo.TreePanel.superclass.constructor.call(this, el, config);
35658  
35659  
35660     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35661     //console.log(tree);
35662     this.on('activate', function()
35663     {
35664         if (this.tree.rendered) {
35665             return;
35666         }
35667         //console.log('render tree');
35668         this.tree.render();
35669     });
35670     // this should not be needed.. - it's actually the 'el' that resizes?
35671     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35672     
35673     //this.on('resize',  function (cp, w, h) {
35674     //        this.tree.innerCt.setWidth(w);
35675     //        this.tree.innerCt.setHeight(h);
35676     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35677     //});
35678
35679         
35680     
35681 };
35682
35683 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35684     fitToFrame : true,
35685     autoScroll : true
35686 });
35687
35688
35689
35690
35691
35692
35693
35694
35695
35696
35697
35698 /*
35699  * Based on:
35700  * Ext JS Library 1.1.1
35701  * Copyright(c) 2006-2007, Ext JS, LLC.
35702  *
35703  * Originally Released Under LGPL - original licence link has changed is not relivant.
35704  *
35705  * Fork - LGPL
35706  * <script type="text/javascript">
35707  */
35708  
35709
35710 /**
35711  * @class Roo.ReaderLayout
35712  * @extends Roo.BorderLayout
35713  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35714  * center region containing two nested regions (a top one for a list view and one for item preview below),
35715  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35716  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35717  * expedites the setup of the overall layout and regions for this common application style.
35718  * Example:
35719  <pre><code>
35720 var reader = new Roo.ReaderLayout();
35721 var CP = Roo.ContentPanel;  // shortcut for adding
35722
35723 reader.beginUpdate();
35724 reader.add("north", new CP("north", "North"));
35725 reader.add("west", new CP("west", {title: "West"}));
35726 reader.add("east", new CP("east", {title: "East"}));
35727
35728 reader.regions.listView.add(new CP("listView", "List"));
35729 reader.regions.preview.add(new CP("preview", "Preview"));
35730 reader.endUpdate();
35731 </code></pre>
35732 * @constructor
35733 * Create a new ReaderLayout
35734 * @param {Object} config Configuration options
35735 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35736 * document.body if omitted)
35737 */
35738 Roo.ReaderLayout = function(config, renderTo){
35739     var c = config || {size:{}};
35740     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35741         north: c.north !== false ? Roo.apply({
35742             split:false,
35743             initialSize: 32,
35744             titlebar: false
35745         }, c.north) : false,
35746         west: c.west !== false ? Roo.apply({
35747             split:true,
35748             initialSize: 200,
35749             minSize: 175,
35750             maxSize: 400,
35751             titlebar: true,
35752             collapsible: true,
35753             animate: true,
35754             margins:{left:5,right:0,bottom:5,top:5},
35755             cmargins:{left:5,right:5,bottom:5,top:5}
35756         }, c.west) : false,
35757         east: c.east !== false ? Roo.apply({
35758             split:true,
35759             initialSize: 200,
35760             minSize: 175,
35761             maxSize: 400,
35762             titlebar: true,
35763             collapsible: true,
35764             animate: true,
35765             margins:{left:0,right:5,bottom:5,top:5},
35766             cmargins:{left:5,right:5,bottom:5,top:5}
35767         }, c.east) : false,
35768         center: Roo.apply({
35769             tabPosition: 'top',
35770             autoScroll:false,
35771             closeOnTab: true,
35772             titlebar:false,
35773             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35774         }, c.center)
35775     });
35776
35777     this.el.addClass('x-reader');
35778
35779     this.beginUpdate();
35780
35781     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35782         south: c.preview !== false ? Roo.apply({
35783             split:true,
35784             initialSize: 200,
35785             minSize: 100,
35786             autoScroll:true,
35787             collapsible:true,
35788             titlebar: true,
35789             cmargins:{top:5,left:0, right:0, bottom:0}
35790         }, c.preview) : false,
35791         center: Roo.apply({
35792             autoScroll:false,
35793             titlebar:false,
35794             minHeight:200
35795         }, c.listView)
35796     });
35797     this.add('center', new Roo.NestedLayoutPanel(inner,
35798             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35799
35800     this.endUpdate();
35801
35802     this.regions.preview = inner.getRegion('south');
35803     this.regions.listView = inner.getRegion('center');
35804 };
35805
35806 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35807  * Based on:
35808  * Ext JS Library 1.1.1
35809  * Copyright(c) 2006-2007, Ext JS, LLC.
35810  *
35811  * Originally Released Under LGPL - original licence link has changed is not relivant.
35812  *
35813  * Fork - LGPL
35814  * <script type="text/javascript">
35815  */
35816  
35817 /**
35818  * @class Roo.grid.Grid
35819  * @extends Roo.util.Observable
35820  * This class represents the primary interface of a component based grid control.
35821  * <br><br>Usage:<pre><code>
35822  var grid = new Roo.grid.Grid("my-container-id", {
35823      ds: myDataStore,
35824      cm: myColModel,
35825      selModel: mySelectionModel,
35826      autoSizeColumns: true,
35827      monitorWindowResize: false,
35828      trackMouseOver: true
35829  });
35830  // set any options
35831  grid.render();
35832  * </code></pre>
35833  * <b>Common Problems:</b><br/>
35834  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35835  * element will correct this<br/>
35836  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35837  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35838  * are unpredictable.<br/>
35839  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35840  * grid to calculate dimensions/offsets.<br/>
35841   * @constructor
35842  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35843  * The container MUST have some type of size defined for the grid to fill. The container will be
35844  * automatically set to position relative if it isn't already.
35845  * @param {Object} config A config object that sets properties on this grid.
35846  */
35847 Roo.grid.Grid = function(container, config){
35848         // initialize the container
35849         this.container = Roo.get(container);
35850         this.container.update("");
35851         this.container.setStyle("overflow", "hidden");
35852     this.container.addClass('x-grid-container');
35853
35854     this.id = this.container.id;
35855
35856     Roo.apply(this, config);
35857     // check and correct shorthanded configs
35858     if(this.ds){
35859         this.dataSource = this.ds;
35860         delete this.ds;
35861     }
35862     if(this.cm){
35863         this.colModel = this.cm;
35864         delete this.cm;
35865     }
35866     if(this.sm){
35867         this.selModel = this.sm;
35868         delete this.sm;
35869     }
35870
35871     if (this.selModel) {
35872         this.selModel = Roo.factory(this.selModel, Roo.grid);
35873         this.sm = this.selModel;
35874         this.sm.xmodule = this.xmodule || false;
35875     }
35876     if (typeof(this.colModel.config) == 'undefined') {
35877         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35878         this.cm = this.colModel;
35879         this.cm.xmodule = this.xmodule || false;
35880     }
35881     if (this.dataSource) {
35882         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35883         this.ds = this.dataSource;
35884         this.ds.xmodule = this.xmodule || false;
35885          
35886     }
35887     
35888     
35889     
35890     if(this.width){
35891         this.container.setWidth(this.width);
35892     }
35893
35894     if(this.height){
35895         this.container.setHeight(this.height);
35896     }
35897     /** @private */
35898         this.addEvents({
35899         // raw events
35900         /**
35901          * @event click
35902          * The raw click event for the entire grid.
35903          * @param {Roo.EventObject} e
35904          */
35905         "click" : true,
35906         /**
35907          * @event dblclick
35908          * The raw dblclick event for the entire grid.
35909          * @param {Roo.EventObject} e
35910          */
35911         "dblclick" : true,
35912         /**
35913          * @event contextmenu
35914          * The raw contextmenu event for the entire grid.
35915          * @param {Roo.EventObject} e
35916          */
35917         "contextmenu" : true,
35918         /**
35919          * @event mousedown
35920          * The raw mousedown event for the entire grid.
35921          * @param {Roo.EventObject} e
35922          */
35923         "mousedown" : true,
35924         /**
35925          * @event mouseup
35926          * The raw mouseup event for the entire grid.
35927          * @param {Roo.EventObject} e
35928          */
35929         "mouseup" : true,
35930         /**
35931          * @event mouseover
35932          * The raw mouseover event for the entire grid.
35933          * @param {Roo.EventObject} e
35934          */
35935         "mouseover" : true,
35936         /**
35937          * @event mouseout
35938          * The raw mouseout event for the entire grid.
35939          * @param {Roo.EventObject} e
35940          */
35941         "mouseout" : true,
35942         /**
35943          * @event keypress
35944          * The raw keypress event for the entire grid.
35945          * @param {Roo.EventObject} e
35946          */
35947         "keypress" : true,
35948         /**
35949          * @event keydown
35950          * The raw keydown event for the entire grid.
35951          * @param {Roo.EventObject} e
35952          */
35953         "keydown" : true,
35954
35955         // custom events
35956
35957         /**
35958          * @event cellclick
35959          * Fires when a cell is clicked
35960          * @param {Grid} this
35961          * @param {Number} rowIndex
35962          * @param {Number} columnIndex
35963          * @param {Roo.EventObject} e
35964          */
35965         "cellclick" : true,
35966         /**
35967          * @event celldblclick
35968          * Fires when a cell is double clicked
35969          * @param {Grid} this
35970          * @param {Number} rowIndex
35971          * @param {Number} columnIndex
35972          * @param {Roo.EventObject} e
35973          */
35974         "celldblclick" : true,
35975         /**
35976          * @event rowclick
35977          * Fires when a row is clicked
35978          * @param {Grid} this
35979          * @param {Number} rowIndex
35980          * @param {Roo.EventObject} e
35981          */
35982         "rowclick" : true,
35983         /**
35984          * @event rowdblclick
35985          * Fires when a row is double clicked
35986          * @param {Grid} this
35987          * @param {Number} rowIndex
35988          * @param {Roo.EventObject} e
35989          */
35990         "rowdblclick" : true,
35991         /**
35992          * @event headerclick
35993          * Fires when a header is clicked
35994          * @param {Grid} this
35995          * @param {Number} columnIndex
35996          * @param {Roo.EventObject} e
35997          */
35998         "headerclick" : true,
35999         /**
36000          * @event headerdblclick
36001          * Fires when a header cell is double clicked
36002          * @param {Grid} this
36003          * @param {Number} columnIndex
36004          * @param {Roo.EventObject} e
36005          */
36006         "headerdblclick" : true,
36007         /**
36008          * @event rowcontextmenu
36009          * Fires when a row is right clicked
36010          * @param {Grid} this
36011          * @param {Number} rowIndex
36012          * @param {Roo.EventObject} e
36013          */
36014         "rowcontextmenu" : true,
36015         /**
36016          * @event cellcontextmenu
36017          * Fires when a cell is right clicked
36018          * @param {Grid} this
36019          * @param {Number} rowIndex
36020          * @param {Number} cellIndex
36021          * @param {Roo.EventObject} e
36022          */
36023          "cellcontextmenu" : true,
36024         /**
36025          * @event headercontextmenu
36026          * Fires when a header is right clicked
36027          * @param {Grid} this
36028          * @param {Number} columnIndex
36029          * @param {Roo.EventObject} e
36030          */
36031         "headercontextmenu" : true,
36032         /**
36033          * @event bodyscroll
36034          * Fires when the body element is scrolled
36035          * @param {Number} scrollLeft
36036          * @param {Number} scrollTop
36037          */
36038         "bodyscroll" : true,
36039         /**
36040          * @event columnresize
36041          * Fires when the user resizes a column
36042          * @param {Number} columnIndex
36043          * @param {Number} newSize
36044          */
36045         "columnresize" : true,
36046         /**
36047          * @event columnmove
36048          * Fires when the user moves a column
36049          * @param {Number} oldIndex
36050          * @param {Number} newIndex
36051          */
36052         "columnmove" : true,
36053         /**
36054          * @event startdrag
36055          * Fires when row(s) start being dragged
36056          * @param {Grid} this
36057          * @param {Roo.GridDD} dd The drag drop object
36058          * @param {event} e The raw browser event
36059          */
36060         "startdrag" : true,
36061         /**
36062          * @event enddrag
36063          * Fires when a drag operation is complete
36064          * @param {Grid} this
36065          * @param {Roo.GridDD} dd The drag drop object
36066          * @param {event} e The raw browser event
36067          */
36068         "enddrag" : true,
36069         /**
36070          * @event dragdrop
36071          * Fires when dragged row(s) are dropped on a valid DD target
36072          * @param {Grid} this
36073          * @param {Roo.GridDD} dd The drag drop object
36074          * @param {String} targetId The target drag drop object
36075          * @param {event} e The raw browser event
36076          */
36077         "dragdrop" : true,
36078         /**
36079          * @event dragover
36080          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36081          * @param {Grid} this
36082          * @param {Roo.GridDD} dd The drag drop object
36083          * @param {String} targetId The target drag drop object
36084          * @param {event} e The raw browser event
36085          */
36086         "dragover" : true,
36087         /**
36088          * @event dragenter
36089          *  Fires when the dragged row(s) first cross another DD target while being dragged
36090          * @param {Grid} this
36091          * @param {Roo.GridDD} dd The drag drop object
36092          * @param {String} targetId The target drag drop object
36093          * @param {event} e The raw browser event
36094          */
36095         "dragenter" : true,
36096         /**
36097          * @event dragout
36098          * Fires when the dragged row(s) leave another DD target while being dragged
36099          * @param {Grid} this
36100          * @param {Roo.GridDD} dd The drag drop object
36101          * @param {String} targetId The target drag drop object
36102          * @param {event} e The raw browser event
36103          */
36104         "dragout" : true,
36105         /**
36106          * @event rowclass
36107          * Fires when a row is rendered, so you can change add a style to it.
36108          * @param {GridView} gridview   The grid view
36109          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36110          */
36111         'rowclass' : true,
36112
36113         /**
36114          * @event render
36115          * Fires when the grid is rendered
36116          * @param {Grid} grid
36117          */
36118         'render' : true
36119     });
36120
36121     Roo.grid.Grid.superclass.constructor.call(this);
36122 };
36123 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36124     
36125     /**
36126      * @cfg {String} ddGroup - drag drop group.
36127      */
36128
36129     /**
36130      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36131      */
36132     minColumnWidth : 25,
36133
36134     /**
36135      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36136      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36137      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36138      */
36139     autoSizeColumns : false,
36140
36141     /**
36142      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36143      */
36144     autoSizeHeaders : true,
36145
36146     /**
36147      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36148      */
36149     monitorWindowResize : true,
36150
36151     /**
36152      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36153      * rows measured to get a columns size. Default is 0 (all rows).
36154      */
36155     maxRowsToMeasure : 0,
36156
36157     /**
36158      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36159      */
36160     trackMouseOver : true,
36161
36162     /**
36163     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36164     */
36165     
36166     /**
36167     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36168     */
36169     enableDragDrop : false,
36170     
36171     /**
36172     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36173     */
36174     enableColumnMove : true,
36175     
36176     /**
36177     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36178     */
36179     enableColumnHide : true,
36180     
36181     /**
36182     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36183     */
36184     enableRowHeightSync : false,
36185     
36186     /**
36187     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36188     */
36189     stripeRows : true,
36190     
36191     /**
36192     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36193     */
36194     autoHeight : false,
36195
36196     /**
36197      * @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.
36198      */
36199     autoExpandColumn : false,
36200
36201     /**
36202     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36203     * Default is 50.
36204     */
36205     autoExpandMin : 50,
36206
36207     /**
36208     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36209     */
36210     autoExpandMax : 1000,
36211
36212     /**
36213     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36214     */
36215     view : null,
36216
36217     /**
36218     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36219     */
36220     loadMask : false,
36221     /**
36222     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36223     */
36224     dropTarget: false,
36225     
36226    
36227     
36228     // private
36229     rendered : false,
36230
36231     /**
36232     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36233     * of a fixed width. Default is false.
36234     */
36235     /**
36236     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36237     */
36238     /**
36239      * Called once after all setup has been completed and the grid is ready to be rendered.
36240      * @return {Roo.grid.Grid} this
36241      */
36242     render : function()
36243     {
36244         var c = this.container;
36245         // try to detect autoHeight/width mode
36246         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36247             this.autoHeight = true;
36248         }
36249         var view = this.getView();
36250         view.init(this);
36251
36252         c.on("click", this.onClick, this);
36253         c.on("dblclick", this.onDblClick, this);
36254         c.on("contextmenu", this.onContextMenu, this);
36255         c.on("keydown", this.onKeyDown, this);
36256         if (Roo.isTouch) {
36257             c.on("touchstart", this.onTouchStart, this);
36258         }
36259
36260         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36261
36262         this.getSelectionModel().init(this);
36263
36264         view.render();
36265
36266         if(this.loadMask){
36267             this.loadMask = new Roo.LoadMask(this.container,
36268                     Roo.apply({store:this.dataSource}, this.loadMask));
36269         }
36270         
36271         
36272         if (this.toolbar && this.toolbar.xtype) {
36273             this.toolbar.container = this.getView().getHeaderPanel(true);
36274             this.toolbar = new Roo.Toolbar(this.toolbar);
36275         }
36276         if (this.footer && this.footer.xtype) {
36277             this.footer.dataSource = this.getDataSource();
36278             this.footer.container = this.getView().getFooterPanel(true);
36279             this.footer = Roo.factory(this.footer, Roo);
36280         }
36281         if (this.dropTarget && this.dropTarget.xtype) {
36282             delete this.dropTarget.xtype;
36283             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36284         }
36285         
36286         
36287         this.rendered = true;
36288         this.fireEvent('render', this);
36289         return this;
36290     },
36291
36292         /**
36293          * Reconfigures the grid to use a different Store and Column Model.
36294          * The View will be bound to the new objects and refreshed.
36295          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36296          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36297          */
36298     reconfigure : function(dataSource, colModel){
36299         if(this.loadMask){
36300             this.loadMask.destroy();
36301             this.loadMask = new Roo.LoadMask(this.container,
36302                     Roo.apply({store:dataSource}, this.loadMask));
36303         }
36304         this.view.bind(dataSource, colModel);
36305         this.dataSource = dataSource;
36306         this.colModel = colModel;
36307         this.view.refresh(true);
36308     },
36309
36310     // private
36311     onKeyDown : function(e){
36312         this.fireEvent("keydown", e);
36313     },
36314
36315     /**
36316      * Destroy this grid.
36317      * @param {Boolean} removeEl True to remove the element
36318      */
36319     destroy : function(removeEl, keepListeners){
36320         if(this.loadMask){
36321             this.loadMask.destroy();
36322         }
36323         var c = this.container;
36324         c.removeAllListeners();
36325         this.view.destroy();
36326         this.colModel.purgeListeners();
36327         if(!keepListeners){
36328             this.purgeListeners();
36329         }
36330         c.update("");
36331         if(removeEl === true){
36332             c.remove();
36333         }
36334     },
36335
36336     // private
36337     processEvent : function(name, e){
36338         // does this fire select???
36339         //Roo.log('grid:processEvent '  + name);
36340         
36341         if (name != 'touchstart' ) {
36342             this.fireEvent(name, e);    
36343         }
36344         
36345         var t = e.getTarget();
36346         var v = this.view;
36347         var header = v.findHeaderIndex(t);
36348         if(header !== false){
36349             var ename = name == 'touchstart' ? 'click' : name;
36350              
36351             this.fireEvent("header" + ename, this, header, e);
36352         }else{
36353             var row = v.findRowIndex(t);
36354             var cell = v.findCellIndex(t);
36355             if (name == 'touchstart') {
36356                 // first touch is always a click.
36357                 // hopefull this happens after selection is updated.?
36358                 name = false;
36359                 
36360                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36361                     var cs = this.selModel.getSelectedCell();
36362                     if (row == cs[0] && cell == cs[1]){
36363                         name = 'dblclick';
36364                     }
36365                 }
36366                 if (typeof(this.selModel.getSelections) != 'undefined') {
36367                     var cs = this.selModel.getSelections();
36368                     var ds = this.dataSource;
36369                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36370                         name = 'dblclick';
36371                     }
36372                 }
36373                 if (!name) {
36374                     return;
36375                 }
36376             }
36377             
36378             
36379             if(row !== false){
36380                 this.fireEvent("row" + name, this, row, e);
36381                 if(cell !== false){
36382                     this.fireEvent("cell" + name, this, row, cell, e);
36383                 }
36384             }
36385         }
36386     },
36387
36388     // private
36389     onClick : function(e){
36390         this.processEvent("click", e);
36391     },
36392    // private
36393     onTouchStart : function(e){
36394         this.processEvent("touchstart", e);
36395     },
36396
36397     // private
36398     onContextMenu : function(e, t){
36399         this.processEvent("contextmenu", e);
36400     },
36401
36402     // private
36403     onDblClick : function(e){
36404         this.processEvent("dblclick", e);
36405     },
36406
36407     // private
36408     walkCells : function(row, col, step, fn, scope){
36409         var cm = this.colModel, clen = cm.getColumnCount();
36410         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36411         if(step < 0){
36412             if(col < 0){
36413                 row--;
36414                 first = false;
36415             }
36416             while(row >= 0){
36417                 if(!first){
36418                     col = clen-1;
36419                 }
36420                 first = false;
36421                 while(col >= 0){
36422                     if(fn.call(scope || this, row, col, cm) === true){
36423                         return [row, col];
36424                     }
36425                     col--;
36426                 }
36427                 row--;
36428             }
36429         } else {
36430             if(col >= clen){
36431                 row++;
36432                 first = false;
36433             }
36434             while(row < rlen){
36435                 if(!first){
36436                     col = 0;
36437                 }
36438                 first = false;
36439                 while(col < clen){
36440                     if(fn.call(scope || this, row, col, cm) === true){
36441                         return [row, col];
36442                     }
36443                     col++;
36444                 }
36445                 row++;
36446             }
36447         }
36448         return null;
36449     },
36450
36451     // private
36452     getSelections : function(){
36453         return this.selModel.getSelections();
36454     },
36455
36456     /**
36457      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36458      * but if manual update is required this method will initiate it.
36459      */
36460     autoSize : function(){
36461         if(this.rendered){
36462             this.view.layout();
36463             if(this.view.adjustForScroll){
36464                 this.view.adjustForScroll();
36465             }
36466         }
36467     },
36468
36469     /**
36470      * Returns the grid's underlying element.
36471      * @return {Element} The element
36472      */
36473     getGridEl : function(){
36474         return this.container;
36475     },
36476
36477     // private for compatibility, overridden by editor grid
36478     stopEditing : function(){},
36479
36480     /**
36481      * Returns the grid's SelectionModel.
36482      * @return {SelectionModel}
36483      */
36484     getSelectionModel : function(){
36485         if(!this.selModel){
36486             this.selModel = new Roo.grid.RowSelectionModel();
36487         }
36488         return this.selModel;
36489     },
36490
36491     /**
36492      * Returns the grid's DataSource.
36493      * @return {DataSource}
36494      */
36495     getDataSource : function(){
36496         return this.dataSource;
36497     },
36498
36499     /**
36500      * Returns the grid's ColumnModel.
36501      * @return {ColumnModel}
36502      */
36503     getColumnModel : function(){
36504         return this.colModel;
36505     },
36506
36507     /**
36508      * Returns the grid's GridView object.
36509      * @return {GridView}
36510      */
36511     getView : function(){
36512         if(!this.view){
36513             this.view = new Roo.grid.GridView(this.viewConfig);
36514         }
36515         return this.view;
36516     },
36517     /**
36518      * Called to get grid's drag proxy text, by default returns this.ddText.
36519      * @return {String}
36520      */
36521     getDragDropText : function(){
36522         var count = this.selModel.getCount();
36523         return String.format(this.ddText, count, count == 1 ? '' : 's');
36524     }
36525 });
36526 /**
36527  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36528  * %0 is replaced with the number of selected rows.
36529  * @type String
36530  */
36531 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36532  * Based on:
36533  * Ext JS Library 1.1.1
36534  * Copyright(c) 2006-2007, Ext JS, LLC.
36535  *
36536  * Originally Released Under LGPL - original licence link has changed is not relivant.
36537  *
36538  * Fork - LGPL
36539  * <script type="text/javascript">
36540  */
36541  
36542 Roo.grid.AbstractGridView = function(){
36543         this.grid = null;
36544         
36545         this.events = {
36546             "beforerowremoved" : true,
36547             "beforerowsinserted" : true,
36548             "beforerefresh" : true,
36549             "rowremoved" : true,
36550             "rowsinserted" : true,
36551             "rowupdated" : true,
36552             "refresh" : true
36553         };
36554     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36555 };
36556
36557 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36558     rowClass : "x-grid-row",
36559     cellClass : "x-grid-cell",
36560     tdClass : "x-grid-td",
36561     hdClass : "x-grid-hd",
36562     splitClass : "x-grid-hd-split",
36563     
36564     init: function(grid){
36565         this.grid = grid;
36566                 var cid = this.grid.getGridEl().id;
36567         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36568         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36569         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36570         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36571         },
36572         
36573     getColumnRenderers : function(){
36574         var renderers = [];
36575         var cm = this.grid.colModel;
36576         var colCount = cm.getColumnCount();
36577         for(var i = 0; i < colCount; i++){
36578             renderers[i] = cm.getRenderer(i);
36579         }
36580         return renderers;
36581     },
36582     
36583     getColumnIds : function(){
36584         var ids = [];
36585         var cm = this.grid.colModel;
36586         var colCount = cm.getColumnCount();
36587         for(var i = 0; i < colCount; i++){
36588             ids[i] = cm.getColumnId(i);
36589         }
36590         return ids;
36591     },
36592     
36593     getDataIndexes : function(){
36594         if(!this.indexMap){
36595             this.indexMap = this.buildIndexMap();
36596         }
36597         return this.indexMap.colToData;
36598     },
36599     
36600     getColumnIndexByDataIndex : function(dataIndex){
36601         if(!this.indexMap){
36602             this.indexMap = this.buildIndexMap();
36603         }
36604         return this.indexMap.dataToCol[dataIndex];
36605     },
36606     
36607     /**
36608      * Set a css style for a column dynamically. 
36609      * @param {Number} colIndex The index of the column
36610      * @param {String} name The css property name
36611      * @param {String} value The css value
36612      */
36613     setCSSStyle : function(colIndex, name, value){
36614         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36615         Roo.util.CSS.updateRule(selector, name, value);
36616     },
36617     
36618     generateRules : function(cm){
36619         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36620         Roo.util.CSS.removeStyleSheet(rulesId);
36621         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36622             var cid = cm.getColumnId(i);
36623             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36624                          this.tdSelector, cid, " {\n}\n",
36625                          this.hdSelector, cid, " {\n}\n",
36626                          this.splitSelector, cid, " {\n}\n");
36627         }
36628         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36629     }
36630 });/*
36631  * Based on:
36632  * Ext JS Library 1.1.1
36633  * Copyright(c) 2006-2007, Ext JS, LLC.
36634  *
36635  * Originally Released Under LGPL - original licence link has changed is not relivant.
36636  *
36637  * Fork - LGPL
36638  * <script type="text/javascript">
36639  */
36640
36641 // private
36642 // This is a support class used internally by the Grid components
36643 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36644     this.grid = grid;
36645     this.view = grid.getView();
36646     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36647     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36648     if(hd2){
36649         this.setHandleElId(Roo.id(hd));
36650         this.setOuterHandleElId(Roo.id(hd2));
36651     }
36652     this.scroll = false;
36653 };
36654 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36655     maxDragWidth: 120,
36656     getDragData : function(e){
36657         var t = Roo.lib.Event.getTarget(e);
36658         var h = this.view.findHeaderCell(t);
36659         if(h){
36660             return {ddel: h.firstChild, header:h};
36661         }
36662         return false;
36663     },
36664
36665     onInitDrag : function(e){
36666         this.view.headersDisabled = true;
36667         var clone = this.dragData.ddel.cloneNode(true);
36668         clone.id = Roo.id();
36669         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36670         this.proxy.update(clone);
36671         return true;
36672     },
36673
36674     afterValidDrop : function(){
36675         var v = this.view;
36676         setTimeout(function(){
36677             v.headersDisabled = false;
36678         }, 50);
36679     },
36680
36681     afterInvalidDrop : function(){
36682         var v = this.view;
36683         setTimeout(function(){
36684             v.headersDisabled = false;
36685         }, 50);
36686     }
36687 });
36688 /*
36689  * Based on:
36690  * Ext JS Library 1.1.1
36691  * Copyright(c) 2006-2007, Ext JS, LLC.
36692  *
36693  * Originally Released Under LGPL - original licence link has changed is not relivant.
36694  *
36695  * Fork - LGPL
36696  * <script type="text/javascript">
36697  */
36698 // private
36699 // This is a support class used internally by the Grid components
36700 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36701     this.grid = grid;
36702     this.view = grid.getView();
36703     // split the proxies so they don't interfere with mouse events
36704     this.proxyTop = Roo.DomHelper.append(document.body, {
36705         cls:"col-move-top", html:"&#160;"
36706     }, true);
36707     this.proxyBottom = Roo.DomHelper.append(document.body, {
36708         cls:"col-move-bottom", html:"&#160;"
36709     }, true);
36710     this.proxyTop.hide = this.proxyBottom.hide = function(){
36711         this.setLeftTop(-100,-100);
36712         this.setStyle("visibility", "hidden");
36713     };
36714     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36715     // temporarily disabled
36716     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36717     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36718 };
36719 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36720     proxyOffsets : [-4, -9],
36721     fly: Roo.Element.fly,
36722
36723     getTargetFromEvent : function(e){
36724         var t = Roo.lib.Event.getTarget(e);
36725         var cindex = this.view.findCellIndex(t);
36726         if(cindex !== false){
36727             return this.view.getHeaderCell(cindex);
36728         }
36729         return null;
36730     },
36731
36732     nextVisible : function(h){
36733         var v = this.view, cm = this.grid.colModel;
36734         h = h.nextSibling;
36735         while(h){
36736             if(!cm.isHidden(v.getCellIndex(h))){
36737                 return h;
36738             }
36739             h = h.nextSibling;
36740         }
36741         return null;
36742     },
36743
36744     prevVisible : function(h){
36745         var v = this.view, cm = this.grid.colModel;
36746         h = h.prevSibling;
36747         while(h){
36748             if(!cm.isHidden(v.getCellIndex(h))){
36749                 return h;
36750             }
36751             h = h.prevSibling;
36752         }
36753         return null;
36754     },
36755
36756     positionIndicator : function(h, n, e){
36757         var x = Roo.lib.Event.getPageX(e);
36758         var r = Roo.lib.Dom.getRegion(n.firstChild);
36759         var px, pt, py = r.top + this.proxyOffsets[1];
36760         if((r.right - x) <= (r.right-r.left)/2){
36761             px = r.right+this.view.borderWidth;
36762             pt = "after";
36763         }else{
36764             px = r.left;
36765             pt = "before";
36766         }
36767         var oldIndex = this.view.getCellIndex(h);
36768         var newIndex = this.view.getCellIndex(n);
36769
36770         if(this.grid.colModel.isFixed(newIndex)){
36771             return false;
36772         }
36773
36774         var locked = this.grid.colModel.isLocked(newIndex);
36775
36776         if(pt == "after"){
36777             newIndex++;
36778         }
36779         if(oldIndex < newIndex){
36780             newIndex--;
36781         }
36782         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36783             return false;
36784         }
36785         px +=  this.proxyOffsets[0];
36786         this.proxyTop.setLeftTop(px, py);
36787         this.proxyTop.show();
36788         if(!this.bottomOffset){
36789             this.bottomOffset = this.view.mainHd.getHeight();
36790         }
36791         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36792         this.proxyBottom.show();
36793         return pt;
36794     },
36795
36796     onNodeEnter : function(n, dd, e, data){
36797         if(data.header != n){
36798             this.positionIndicator(data.header, n, e);
36799         }
36800     },
36801
36802     onNodeOver : function(n, dd, e, data){
36803         var result = false;
36804         if(data.header != n){
36805             result = this.positionIndicator(data.header, n, e);
36806         }
36807         if(!result){
36808             this.proxyTop.hide();
36809             this.proxyBottom.hide();
36810         }
36811         return result ? this.dropAllowed : this.dropNotAllowed;
36812     },
36813
36814     onNodeOut : function(n, dd, e, data){
36815         this.proxyTop.hide();
36816         this.proxyBottom.hide();
36817     },
36818
36819     onNodeDrop : function(n, dd, e, data){
36820         var h = data.header;
36821         if(h != n){
36822             var cm = this.grid.colModel;
36823             var x = Roo.lib.Event.getPageX(e);
36824             var r = Roo.lib.Dom.getRegion(n.firstChild);
36825             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36826             var oldIndex = this.view.getCellIndex(h);
36827             var newIndex = this.view.getCellIndex(n);
36828             var locked = cm.isLocked(newIndex);
36829             if(pt == "after"){
36830                 newIndex++;
36831             }
36832             if(oldIndex < newIndex){
36833                 newIndex--;
36834             }
36835             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36836                 return false;
36837             }
36838             cm.setLocked(oldIndex, locked, true);
36839             cm.moveColumn(oldIndex, newIndex);
36840             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36841             return true;
36842         }
36843         return false;
36844     }
36845 });
36846 /*
36847  * Based on:
36848  * Ext JS Library 1.1.1
36849  * Copyright(c) 2006-2007, Ext JS, LLC.
36850  *
36851  * Originally Released Under LGPL - original licence link has changed is not relivant.
36852  *
36853  * Fork - LGPL
36854  * <script type="text/javascript">
36855  */
36856   
36857 /**
36858  * @class Roo.grid.GridView
36859  * @extends Roo.util.Observable
36860  *
36861  * @constructor
36862  * @param {Object} config
36863  */
36864 Roo.grid.GridView = function(config){
36865     Roo.grid.GridView.superclass.constructor.call(this);
36866     this.el = null;
36867
36868     Roo.apply(this, config);
36869 };
36870
36871 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36872
36873     unselectable :  'unselectable="on"',
36874     unselectableCls :  'x-unselectable',
36875     
36876     
36877     rowClass : "x-grid-row",
36878
36879     cellClass : "x-grid-col",
36880
36881     tdClass : "x-grid-td",
36882
36883     hdClass : "x-grid-hd",
36884
36885     splitClass : "x-grid-split",
36886
36887     sortClasses : ["sort-asc", "sort-desc"],
36888
36889     enableMoveAnim : false,
36890
36891     hlColor: "C3DAF9",
36892
36893     dh : Roo.DomHelper,
36894
36895     fly : Roo.Element.fly,
36896
36897     css : Roo.util.CSS,
36898
36899     borderWidth: 1,
36900
36901     splitOffset: 3,
36902
36903     scrollIncrement : 22,
36904
36905     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36906
36907     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36908
36909     bind : function(ds, cm){
36910         if(this.ds){
36911             this.ds.un("load", this.onLoad, this);
36912             this.ds.un("datachanged", this.onDataChange, this);
36913             this.ds.un("add", this.onAdd, this);
36914             this.ds.un("remove", this.onRemove, this);
36915             this.ds.un("update", this.onUpdate, this);
36916             this.ds.un("clear", this.onClear, this);
36917         }
36918         if(ds){
36919             ds.on("load", this.onLoad, this);
36920             ds.on("datachanged", this.onDataChange, this);
36921             ds.on("add", this.onAdd, this);
36922             ds.on("remove", this.onRemove, this);
36923             ds.on("update", this.onUpdate, this);
36924             ds.on("clear", this.onClear, this);
36925         }
36926         this.ds = ds;
36927
36928         if(this.cm){
36929             this.cm.un("widthchange", this.onColWidthChange, this);
36930             this.cm.un("headerchange", this.onHeaderChange, this);
36931             this.cm.un("hiddenchange", this.onHiddenChange, this);
36932             this.cm.un("columnmoved", this.onColumnMove, this);
36933             this.cm.un("columnlockchange", this.onColumnLock, this);
36934         }
36935         if(cm){
36936             this.generateRules(cm);
36937             cm.on("widthchange", this.onColWidthChange, this);
36938             cm.on("headerchange", this.onHeaderChange, this);
36939             cm.on("hiddenchange", this.onHiddenChange, this);
36940             cm.on("columnmoved", this.onColumnMove, this);
36941             cm.on("columnlockchange", this.onColumnLock, this);
36942         }
36943         this.cm = cm;
36944     },
36945
36946     init: function(grid){
36947         Roo.grid.GridView.superclass.init.call(this, grid);
36948
36949         this.bind(grid.dataSource, grid.colModel);
36950
36951         grid.on("headerclick", this.handleHeaderClick, this);
36952
36953         if(grid.trackMouseOver){
36954             grid.on("mouseover", this.onRowOver, this);
36955             grid.on("mouseout", this.onRowOut, this);
36956         }
36957         grid.cancelTextSelection = function(){};
36958         this.gridId = grid.id;
36959
36960         var tpls = this.templates || {};
36961
36962         if(!tpls.master){
36963             tpls.master = new Roo.Template(
36964                '<div class="x-grid" hidefocus="true">',
36965                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36966                   '<div class="x-grid-topbar"></div>',
36967                   '<div class="x-grid-scroller"><div></div></div>',
36968                   '<div class="x-grid-locked">',
36969                       '<div class="x-grid-header">{lockedHeader}</div>',
36970                       '<div class="x-grid-body">{lockedBody}</div>',
36971                   "</div>",
36972                   '<div class="x-grid-viewport">',
36973                       '<div class="x-grid-header">{header}</div>',
36974                       '<div class="x-grid-body">{body}</div>',
36975                   "</div>",
36976                   '<div class="x-grid-bottombar"></div>',
36977                  
36978                   '<div class="x-grid-resize-proxy">&#160;</div>',
36979                "</div>"
36980             );
36981             tpls.master.disableformats = true;
36982         }
36983
36984         if(!tpls.header){
36985             tpls.header = new Roo.Template(
36986                '<table border="0" cellspacing="0" cellpadding="0">',
36987                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36988                "</table>{splits}"
36989             );
36990             tpls.header.disableformats = true;
36991         }
36992         tpls.header.compile();
36993
36994         if(!tpls.hcell){
36995             tpls.hcell = new Roo.Template(
36996                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36997                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36998                 "</div></td>"
36999              );
37000              tpls.hcell.disableFormats = true;
37001         }
37002         tpls.hcell.compile();
37003
37004         if(!tpls.hsplit){
37005             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37006                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37007             tpls.hsplit.disableFormats = true;
37008         }
37009         tpls.hsplit.compile();
37010
37011         if(!tpls.body){
37012             tpls.body = new Roo.Template(
37013                '<table border="0" cellspacing="0" cellpadding="0">',
37014                "<tbody>{rows}</tbody>",
37015                "</table>"
37016             );
37017             tpls.body.disableFormats = true;
37018         }
37019         tpls.body.compile();
37020
37021         if(!tpls.row){
37022             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37023             tpls.row.disableFormats = true;
37024         }
37025         tpls.row.compile();
37026
37027         if(!tpls.cell){
37028             tpls.cell = new Roo.Template(
37029                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37030                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37031                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37032                 "</td>"
37033             );
37034             tpls.cell.disableFormats = true;
37035         }
37036         tpls.cell.compile();
37037
37038         this.templates = tpls;
37039     },
37040
37041     // remap these for backwards compat
37042     onColWidthChange : function(){
37043         this.updateColumns.apply(this, arguments);
37044     },
37045     onHeaderChange : function(){
37046         this.updateHeaders.apply(this, arguments);
37047     }, 
37048     onHiddenChange : function(){
37049         this.handleHiddenChange.apply(this, arguments);
37050     },
37051     onColumnMove : function(){
37052         this.handleColumnMove.apply(this, arguments);
37053     },
37054     onColumnLock : function(){
37055         this.handleLockChange.apply(this, arguments);
37056     },
37057
37058     onDataChange : function(){
37059         this.refresh();
37060         this.updateHeaderSortState();
37061     },
37062
37063     onClear : function(){
37064         this.refresh();
37065     },
37066
37067     onUpdate : function(ds, record){
37068         this.refreshRow(record);
37069     },
37070
37071     refreshRow : function(record){
37072         var ds = this.ds, index;
37073         if(typeof record == 'number'){
37074             index = record;
37075             record = ds.getAt(index);
37076         }else{
37077             index = ds.indexOf(record);
37078         }
37079         this.insertRows(ds, index, index, true);
37080         this.onRemove(ds, record, index+1, true);
37081         this.syncRowHeights(index, index);
37082         this.layout();
37083         this.fireEvent("rowupdated", this, index, record);
37084     },
37085
37086     onAdd : function(ds, records, index){
37087         this.insertRows(ds, index, index + (records.length-1));
37088     },
37089
37090     onRemove : function(ds, record, index, isUpdate){
37091         if(isUpdate !== true){
37092             this.fireEvent("beforerowremoved", this, index, record);
37093         }
37094         var bt = this.getBodyTable(), lt = this.getLockedTable();
37095         if(bt.rows[index]){
37096             bt.firstChild.removeChild(bt.rows[index]);
37097         }
37098         if(lt.rows[index]){
37099             lt.firstChild.removeChild(lt.rows[index]);
37100         }
37101         if(isUpdate !== true){
37102             this.stripeRows(index);
37103             this.syncRowHeights(index, index);
37104             this.layout();
37105             this.fireEvent("rowremoved", this, index, record);
37106         }
37107     },
37108
37109     onLoad : function(){
37110         this.scrollToTop();
37111     },
37112
37113     /**
37114      * Scrolls the grid to the top
37115      */
37116     scrollToTop : function(){
37117         if(this.scroller){
37118             this.scroller.dom.scrollTop = 0;
37119             this.syncScroll();
37120         }
37121     },
37122
37123     /**
37124      * Gets a panel in the header of the grid that can be used for toolbars etc.
37125      * After modifying the contents of this panel a call to grid.autoSize() may be
37126      * required to register any changes in size.
37127      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37128      * @return Roo.Element
37129      */
37130     getHeaderPanel : function(doShow){
37131         if(doShow){
37132             this.headerPanel.show();
37133         }
37134         return this.headerPanel;
37135     },
37136
37137     /**
37138      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37139      * After modifying the contents of this panel a call to grid.autoSize() may be
37140      * required to register any changes in size.
37141      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37142      * @return Roo.Element
37143      */
37144     getFooterPanel : function(doShow){
37145         if(doShow){
37146             this.footerPanel.show();
37147         }
37148         return this.footerPanel;
37149     },
37150
37151     initElements : function(){
37152         var E = Roo.Element;
37153         var el = this.grid.getGridEl().dom.firstChild;
37154         var cs = el.childNodes;
37155
37156         this.el = new E(el);
37157         
37158          this.focusEl = new E(el.firstChild);
37159         this.focusEl.swallowEvent("click", true);
37160         
37161         this.headerPanel = new E(cs[1]);
37162         this.headerPanel.enableDisplayMode("block");
37163
37164         this.scroller = new E(cs[2]);
37165         this.scrollSizer = new E(this.scroller.dom.firstChild);
37166
37167         this.lockedWrap = new E(cs[3]);
37168         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37169         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37170
37171         this.mainWrap = new E(cs[4]);
37172         this.mainHd = new E(this.mainWrap.dom.firstChild);
37173         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37174
37175         this.footerPanel = new E(cs[5]);
37176         this.footerPanel.enableDisplayMode("block");
37177
37178         this.resizeProxy = new E(cs[6]);
37179
37180         this.headerSelector = String.format(
37181            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37182            this.lockedHd.id, this.mainHd.id
37183         );
37184
37185         this.splitterSelector = String.format(
37186            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37187            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37188         );
37189     },
37190     idToCssName : function(s)
37191     {
37192         return s.replace(/[^a-z0-9]+/ig, '-');
37193     },
37194
37195     getHeaderCell : function(index){
37196         return Roo.DomQuery.select(this.headerSelector)[index];
37197     },
37198
37199     getHeaderCellMeasure : function(index){
37200         return this.getHeaderCell(index).firstChild;
37201     },
37202
37203     getHeaderCellText : function(index){
37204         return this.getHeaderCell(index).firstChild.firstChild;
37205     },
37206
37207     getLockedTable : function(){
37208         return this.lockedBody.dom.firstChild;
37209     },
37210
37211     getBodyTable : function(){
37212         return this.mainBody.dom.firstChild;
37213     },
37214
37215     getLockedRow : function(index){
37216         return this.getLockedTable().rows[index];
37217     },
37218
37219     getRow : function(index){
37220         return this.getBodyTable().rows[index];
37221     },
37222
37223     getRowComposite : function(index){
37224         if(!this.rowEl){
37225             this.rowEl = new Roo.CompositeElementLite();
37226         }
37227         var els = [], lrow, mrow;
37228         if(lrow = this.getLockedRow(index)){
37229             els.push(lrow);
37230         }
37231         if(mrow = this.getRow(index)){
37232             els.push(mrow);
37233         }
37234         this.rowEl.elements = els;
37235         return this.rowEl;
37236     },
37237     /**
37238      * Gets the 'td' of the cell
37239      * 
37240      * @param {Integer} rowIndex row to select
37241      * @param {Integer} colIndex column to select
37242      * 
37243      * @return {Object} 
37244      */
37245     getCell : function(rowIndex, colIndex){
37246         var locked = this.cm.getLockedCount();
37247         var source;
37248         if(colIndex < locked){
37249             source = this.lockedBody.dom.firstChild;
37250         }else{
37251             source = this.mainBody.dom.firstChild;
37252             colIndex -= locked;
37253         }
37254         return source.rows[rowIndex].childNodes[colIndex];
37255     },
37256
37257     getCellText : function(rowIndex, colIndex){
37258         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37259     },
37260
37261     getCellBox : function(cell){
37262         var b = this.fly(cell).getBox();
37263         if(Roo.isOpera){ // opera fails to report the Y
37264             b.y = cell.offsetTop + this.mainBody.getY();
37265         }
37266         return b;
37267     },
37268
37269     getCellIndex : function(cell){
37270         var id = String(cell.className).match(this.cellRE);
37271         if(id){
37272             return parseInt(id[1], 10);
37273         }
37274         return 0;
37275     },
37276
37277     findHeaderIndex : function(n){
37278         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37279         return r ? this.getCellIndex(r) : false;
37280     },
37281
37282     findHeaderCell : function(n){
37283         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37284         return r ? r : false;
37285     },
37286
37287     findRowIndex : function(n){
37288         if(!n){
37289             return false;
37290         }
37291         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37292         return r ? r.rowIndex : false;
37293     },
37294
37295     findCellIndex : function(node){
37296         var stop = this.el.dom;
37297         while(node && node != stop){
37298             if(this.findRE.test(node.className)){
37299                 return this.getCellIndex(node);
37300             }
37301             node = node.parentNode;
37302         }
37303         return false;
37304     },
37305
37306     getColumnId : function(index){
37307         return this.cm.getColumnId(index);
37308     },
37309
37310     getSplitters : function()
37311     {
37312         if(this.splitterSelector){
37313            return Roo.DomQuery.select(this.splitterSelector);
37314         }else{
37315             return null;
37316       }
37317     },
37318
37319     getSplitter : function(index){
37320         return this.getSplitters()[index];
37321     },
37322
37323     onRowOver : function(e, t){
37324         var row;
37325         if((row = this.findRowIndex(t)) !== false){
37326             this.getRowComposite(row).addClass("x-grid-row-over");
37327         }
37328     },
37329
37330     onRowOut : function(e, t){
37331         var row;
37332         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37333             this.getRowComposite(row).removeClass("x-grid-row-over");
37334         }
37335     },
37336
37337     renderHeaders : function(){
37338         var cm = this.cm;
37339         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37340         var cb = [], lb = [], sb = [], lsb = [], p = {};
37341         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37342             p.cellId = "x-grid-hd-0-" + i;
37343             p.splitId = "x-grid-csplit-0-" + i;
37344             p.id = cm.getColumnId(i);
37345             p.title = cm.getColumnTooltip(i) || "";
37346             p.value = cm.getColumnHeader(i) || "";
37347             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37348             if(!cm.isLocked(i)){
37349                 cb[cb.length] = ct.apply(p);
37350                 sb[sb.length] = st.apply(p);
37351             }else{
37352                 lb[lb.length] = ct.apply(p);
37353                 lsb[lsb.length] = st.apply(p);
37354             }
37355         }
37356         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37357                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37358     },
37359
37360     updateHeaders : function(){
37361         var html = this.renderHeaders();
37362         this.lockedHd.update(html[0]);
37363         this.mainHd.update(html[1]);
37364     },
37365
37366     /**
37367      * Focuses the specified row.
37368      * @param {Number} row The row index
37369      */
37370     focusRow : function(row)
37371     {
37372         //Roo.log('GridView.focusRow');
37373         var x = this.scroller.dom.scrollLeft;
37374         this.focusCell(row, 0, false);
37375         this.scroller.dom.scrollLeft = x;
37376     },
37377
37378     /**
37379      * Focuses the specified cell.
37380      * @param {Number} row The row index
37381      * @param {Number} col The column index
37382      * @param {Boolean} hscroll false to disable horizontal scrolling
37383      */
37384     focusCell : function(row, col, hscroll)
37385     {
37386         //Roo.log('GridView.focusCell');
37387         var el = this.ensureVisible(row, col, hscroll);
37388         this.focusEl.alignTo(el, "tl-tl");
37389         if(Roo.isGecko){
37390             this.focusEl.focus();
37391         }else{
37392             this.focusEl.focus.defer(1, this.focusEl);
37393         }
37394     },
37395
37396     /**
37397      * Scrolls the specified cell into view
37398      * @param {Number} row The row index
37399      * @param {Number} col The column index
37400      * @param {Boolean} hscroll false to disable horizontal scrolling
37401      */
37402     ensureVisible : function(row, col, hscroll)
37403     {
37404         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37405         //return null; //disable for testing.
37406         if(typeof row != "number"){
37407             row = row.rowIndex;
37408         }
37409         if(row < 0 && row >= this.ds.getCount()){
37410             return  null;
37411         }
37412         col = (col !== undefined ? col : 0);
37413         var cm = this.grid.colModel;
37414         while(cm.isHidden(col)){
37415             col++;
37416         }
37417
37418         var el = this.getCell(row, col);
37419         if(!el){
37420             return null;
37421         }
37422         var c = this.scroller.dom;
37423
37424         var ctop = parseInt(el.offsetTop, 10);
37425         var cleft = parseInt(el.offsetLeft, 10);
37426         var cbot = ctop + el.offsetHeight;
37427         var cright = cleft + el.offsetWidth;
37428         
37429         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37430         var stop = parseInt(c.scrollTop, 10);
37431         var sleft = parseInt(c.scrollLeft, 10);
37432         var sbot = stop + ch;
37433         var sright = sleft + c.clientWidth;
37434         /*
37435         Roo.log('GridView.ensureVisible:' +
37436                 ' ctop:' + ctop +
37437                 ' c.clientHeight:' + c.clientHeight +
37438                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37439                 ' stop:' + stop +
37440                 ' cbot:' + cbot +
37441                 ' sbot:' + sbot +
37442                 ' ch:' + ch  
37443                 );
37444         */
37445         if(ctop < stop){
37446              c.scrollTop = ctop;
37447             //Roo.log("set scrolltop to ctop DISABLE?");
37448         }else if(cbot > sbot){
37449             //Roo.log("set scrolltop to cbot-ch");
37450             c.scrollTop = cbot-ch;
37451         }
37452         
37453         if(hscroll !== false){
37454             if(cleft < sleft){
37455                 c.scrollLeft = cleft;
37456             }else if(cright > sright){
37457                 c.scrollLeft = cright-c.clientWidth;
37458             }
37459         }
37460          
37461         return el;
37462     },
37463
37464     updateColumns : function(){
37465         this.grid.stopEditing();
37466         var cm = this.grid.colModel, colIds = this.getColumnIds();
37467         //var totalWidth = cm.getTotalWidth();
37468         var pos = 0;
37469         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37470             //if(cm.isHidden(i)) continue;
37471             var w = cm.getColumnWidth(i);
37472             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37473             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37474         }
37475         this.updateSplitters();
37476     },
37477
37478     generateRules : function(cm){
37479         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37480         Roo.util.CSS.removeStyleSheet(rulesId);
37481         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37482             var cid = cm.getColumnId(i);
37483             var align = '';
37484             if(cm.config[i].align){
37485                 align = 'text-align:'+cm.config[i].align+';';
37486             }
37487             var hidden = '';
37488             if(cm.isHidden(i)){
37489                 hidden = 'display:none;';
37490             }
37491             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37492             ruleBuf.push(
37493                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37494                     this.hdSelector, cid, " {\n", align, width, "}\n",
37495                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37496                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37497         }
37498         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37499     },
37500
37501     updateSplitters : function(){
37502         var cm = this.cm, s = this.getSplitters();
37503         if(s){ // splitters not created yet
37504             var pos = 0, locked = true;
37505             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37506                 if(cm.isHidden(i)) continue;
37507                 var w = cm.getColumnWidth(i); // make sure it's a number
37508                 if(!cm.isLocked(i) && locked){
37509                     pos = 0;
37510                     locked = false;
37511                 }
37512                 pos += w;
37513                 s[i].style.left = (pos-this.splitOffset) + "px";
37514             }
37515         }
37516     },
37517
37518     handleHiddenChange : function(colModel, colIndex, hidden){
37519         if(hidden){
37520             this.hideColumn(colIndex);
37521         }else{
37522             this.unhideColumn(colIndex);
37523         }
37524     },
37525
37526     hideColumn : function(colIndex){
37527         var cid = this.getColumnId(colIndex);
37528         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37529         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37530         if(Roo.isSafari){
37531             this.updateHeaders();
37532         }
37533         this.updateSplitters();
37534         this.layout();
37535     },
37536
37537     unhideColumn : function(colIndex){
37538         var cid = this.getColumnId(colIndex);
37539         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37540         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37541
37542         if(Roo.isSafari){
37543             this.updateHeaders();
37544         }
37545         this.updateSplitters();
37546         this.layout();
37547     },
37548
37549     insertRows : function(dm, firstRow, lastRow, isUpdate){
37550         if(firstRow == 0 && lastRow == dm.getCount()-1){
37551             this.refresh();
37552         }else{
37553             if(!isUpdate){
37554                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37555             }
37556             var s = this.getScrollState();
37557             var markup = this.renderRows(firstRow, lastRow);
37558             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37559             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37560             this.restoreScroll(s);
37561             if(!isUpdate){
37562                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37563                 this.syncRowHeights(firstRow, lastRow);
37564                 this.stripeRows(firstRow);
37565                 this.layout();
37566             }
37567         }
37568     },
37569
37570     bufferRows : function(markup, target, index){
37571         var before = null, trows = target.rows, tbody = target.tBodies[0];
37572         if(index < trows.length){
37573             before = trows[index];
37574         }
37575         var b = document.createElement("div");
37576         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37577         var rows = b.firstChild.rows;
37578         for(var i = 0, len = rows.length; i < len; i++){
37579             if(before){
37580                 tbody.insertBefore(rows[0], before);
37581             }else{
37582                 tbody.appendChild(rows[0]);
37583             }
37584         }
37585         b.innerHTML = "";
37586         b = null;
37587     },
37588
37589     deleteRows : function(dm, firstRow, lastRow){
37590         if(dm.getRowCount()<1){
37591             this.fireEvent("beforerefresh", this);
37592             this.mainBody.update("");
37593             this.lockedBody.update("");
37594             this.fireEvent("refresh", this);
37595         }else{
37596             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37597             var bt = this.getBodyTable();
37598             var tbody = bt.firstChild;
37599             var rows = bt.rows;
37600             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37601                 tbody.removeChild(rows[firstRow]);
37602             }
37603             this.stripeRows(firstRow);
37604             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37605         }
37606     },
37607
37608     updateRows : function(dataSource, firstRow, lastRow){
37609         var s = this.getScrollState();
37610         this.refresh();
37611         this.restoreScroll(s);
37612     },
37613
37614     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37615         if(!noRefresh){
37616            this.refresh();
37617         }
37618         this.updateHeaderSortState();
37619     },
37620
37621     getScrollState : function(){
37622         
37623         var sb = this.scroller.dom;
37624         return {left: sb.scrollLeft, top: sb.scrollTop};
37625     },
37626
37627     stripeRows : function(startRow){
37628         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37629             return;
37630         }
37631         startRow = startRow || 0;
37632         var rows = this.getBodyTable().rows;
37633         var lrows = this.getLockedTable().rows;
37634         var cls = ' x-grid-row-alt ';
37635         for(var i = startRow, len = rows.length; i < len; i++){
37636             var row = rows[i], lrow = lrows[i];
37637             var isAlt = ((i+1) % 2 == 0);
37638             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37639             if(isAlt == hasAlt){
37640                 continue;
37641             }
37642             if(isAlt){
37643                 row.className += " x-grid-row-alt";
37644             }else{
37645                 row.className = row.className.replace("x-grid-row-alt", "");
37646             }
37647             if(lrow){
37648                 lrow.className = row.className;
37649             }
37650         }
37651     },
37652
37653     restoreScroll : function(state){
37654         //Roo.log('GridView.restoreScroll');
37655         var sb = this.scroller.dom;
37656         sb.scrollLeft = state.left;
37657         sb.scrollTop = state.top;
37658         this.syncScroll();
37659     },
37660
37661     syncScroll : function(){
37662         //Roo.log('GridView.syncScroll');
37663         var sb = this.scroller.dom;
37664         var sh = this.mainHd.dom;
37665         var bs = this.mainBody.dom;
37666         var lv = this.lockedBody.dom;
37667         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37668         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37669     },
37670
37671     handleScroll : function(e){
37672         this.syncScroll();
37673         var sb = this.scroller.dom;
37674         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37675         e.stopEvent();
37676     },
37677
37678     handleWheel : function(e){
37679         var d = e.getWheelDelta();
37680         this.scroller.dom.scrollTop -= d*22;
37681         // set this here to prevent jumpy scrolling on large tables
37682         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37683         e.stopEvent();
37684     },
37685
37686     renderRows : function(startRow, endRow){
37687         // pull in all the crap needed to render rows
37688         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37689         var colCount = cm.getColumnCount();
37690
37691         if(ds.getCount() < 1){
37692             return ["", ""];
37693         }
37694
37695         // build a map for all the columns
37696         var cs = [];
37697         for(var i = 0; i < colCount; i++){
37698             var name = cm.getDataIndex(i);
37699             cs[i] = {
37700                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37701                 renderer : cm.getRenderer(i),
37702                 id : cm.getColumnId(i),
37703                 locked : cm.isLocked(i)
37704             };
37705         }
37706
37707         startRow = startRow || 0;
37708         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37709
37710         // records to render
37711         var rs = ds.getRange(startRow, endRow);
37712
37713         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37714     },
37715
37716     // As much as I hate to duplicate code, this was branched because FireFox really hates
37717     // [].join("") on strings. The performance difference was substantial enough to
37718     // branch this function
37719     doRender : Roo.isGecko ?
37720             function(cs, rs, ds, startRow, colCount, stripe){
37721                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37722                 // buffers
37723                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37724                 
37725                 var hasListener = this.grid.hasListener('rowclass');
37726                 var rowcfg = {};
37727                 for(var j = 0, len = rs.length; j < len; j++){
37728                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37729                     for(var i = 0; i < colCount; i++){
37730                         c = cs[i];
37731                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37732                         p.id = c.id;
37733                         p.css = p.attr = "";
37734                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37735                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37736                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37737                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37738                         }
37739                         var markup = ct.apply(p);
37740                         if(!c.locked){
37741                             cb+= markup;
37742                         }else{
37743                             lcb+= markup;
37744                         }
37745                     }
37746                     var alt = [];
37747                     if(stripe && ((rowIndex+1) % 2 == 0)){
37748                         alt.push("x-grid-row-alt")
37749                     }
37750                     if(r.dirty){
37751                         alt.push(  " x-grid-dirty-row");
37752                     }
37753                     rp.cells = lcb;
37754                     if(this.getRowClass){
37755                         alt.push(this.getRowClass(r, rowIndex));
37756                     }
37757                     if (hasListener) {
37758                         rowcfg = {
37759                              
37760                             record: r,
37761                             rowIndex : rowIndex,
37762                             rowClass : ''
37763                         }
37764                         this.grid.fireEvent('rowclass', this, rowcfg);
37765                         alt.push(rowcfg.rowClass);
37766                     }
37767                     rp.alt = alt.join(" ");
37768                     lbuf+= rt.apply(rp);
37769                     rp.cells = cb;
37770                     buf+=  rt.apply(rp);
37771                 }
37772                 return [lbuf, buf];
37773             } :
37774             function(cs, rs, ds, startRow, colCount, stripe){
37775                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37776                 // buffers
37777                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37778                 var hasListener = this.grid.hasListener('rowclass');
37779  
37780                 var rowcfg = {};
37781                 for(var j = 0, len = rs.length; j < len; j++){
37782                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37783                     for(var i = 0; i < colCount; i++){
37784                         c = cs[i];
37785                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37786                         p.id = c.id;
37787                         p.css = p.attr = "";
37788                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37789                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37790                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37791                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37792                         }
37793                         
37794                         var markup = ct.apply(p);
37795                         if(!c.locked){
37796                             cb[cb.length] = markup;
37797                         }else{
37798                             lcb[lcb.length] = markup;
37799                         }
37800                     }
37801                     var alt = [];
37802                     if(stripe && ((rowIndex+1) % 2 == 0)){
37803                         alt.push( "x-grid-row-alt");
37804                     }
37805                     if(r.dirty){
37806                         alt.push(" x-grid-dirty-row");
37807                     }
37808                     rp.cells = lcb;
37809                     if(this.getRowClass){
37810                         alt.push( this.getRowClass(r, rowIndex));
37811                     }
37812                     if (hasListener) {
37813                         rowcfg = {
37814                              
37815                             record: r,
37816                             rowIndex : rowIndex,
37817                             rowClass : ''
37818                         }
37819                         this.grid.fireEvent('rowclass', this, rowcfg);
37820                         alt.push(rowcfg.rowClass);
37821                     }
37822                     rp.alt = alt.join(" ");
37823                     rp.cells = lcb.join("");
37824                     lbuf[lbuf.length] = rt.apply(rp);
37825                     rp.cells = cb.join("");
37826                     buf[buf.length] =  rt.apply(rp);
37827                 }
37828                 return [lbuf.join(""), buf.join("")];
37829             },
37830
37831     renderBody : function(){
37832         var markup = this.renderRows();
37833         var bt = this.templates.body;
37834         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37835     },
37836
37837     /**
37838      * Refreshes the grid
37839      * @param {Boolean} headersToo
37840      */
37841     refresh : function(headersToo){
37842         this.fireEvent("beforerefresh", this);
37843         this.grid.stopEditing();
37844         var result = this.renderBody();
37845         this.lockedBody.update(result[0]);
37846         this.mainBody.update(result[1]);
37847         if(headersToo === true){
37848             this.updateHeaders();
37849             this.updateColumns();
37850             this.updateSplitters();
37851             this.updateHeaderSortState();
37852         }
37853         this.syncRowHeights();
37854         this.layout();
37855         this.fireEvent("refresh", this);
37856     },
37857
37858     handleColumnMove : function(cm, oldIndex, newIndex){
37859         this.indexMap = null;
37860         var s = this.getScrollState();
37861         this.refresh(true);
37862         this.restoreScroll(s);
37863         this.afterMove(newIndex);
37864     },
37865
37866     afterMove : function(colIndex){
37867         if(this.enableMoveAnim && Roo.enableFx){
37868             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37869         }
37870         // if multisort - fix sortOrder, and reload..
37871         if (this.grid.dataSource.multiSort) {
37872             // the we can call sort again..
37873             var dm = this.grid.dataSource;
37874             var cm = this.grid.colModel;
37875             var so = [];
37876             for(var i = 0; i < cm.config.length; i++ ) {
37877                 
37878                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37879                     continue; // dont' bother, it's not in sort list or being set.
37880                 }
37881                 
37882                 so.push(cm.config[i].dataIndex);
37883             };
37884             dm.sortOrder = so;
37885             dm.load(dm.lastOptions);
37886             
37887             
37888         }
37889         
37890     },
37891
37892     updateCell : function(dm, rowIndex, dataIndex){
37893         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37894         if(typeof colIndex == "undefined"){ // not present in grid
37895             return;
37896         }
37897         var cm = this.grid.colModel;
37898         var cell = this.getCell(rowIndex, colIndex);
37899         var cellText = this.getCellText(rowIndex, colIndex);
37900
37901         var p = {
37902             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37903             id : cm.getColumnId(colIndex),
37904             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37905         };
37906         var renderer = cm.getRenderer(colIndex);
37907         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37908         if(typeof val == "undefined" || val === "") val = "&#160;";
37909         cellText.innerHTML = val;
37910         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37911         this.syncRowHeights(rowIndex, rowIndex);
37912     },
37913
37914     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37915         var maxWidth = 0;
37916         if(this.grid.autoSizeHeaders){
37917             var h = this.getHeaderCellMeasure(colIndex);
37918             maxWidth = Math.max(maxWidth, h.scrollWidth);
37919         }
37920         var tb, index;
37921         if(this.cm.isLocked(colIndex)){
37922             tb = this.getLockedTable();
37923             index = colIndex;
37924         }else{
37925             tb = this.getBodyTable();
37926             index = colIndex - this.cm.getLockedCount();
37927         }
37928         if(tb && tb.rows){
37929             var rows = tb.rows;
37930             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37931             for(var i = 0; i < stopIndex; i++){
37932                 var cell = rows[i].childNodes[index].firstChild;
37933                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37934             }
37935         }
37936         return maxWidth + /*margin for error in IE*/ 5;
37937     },
37938     /**
37939      * Autofit a column to its content.
37940      * @param {Number} colIndex
37941      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37942      */
37943      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37944          if(this.cm.isHidden(colIndex)){
37945              return; // can't calc a hidden column
37946          }
37947         if(forceMinSize){
37948             var cid = this.cm.getColumnId(colIndex);
37949             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37950            if(this.grid.autoSizeHeaders){
37951                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37952            }
37953         }
37954         var newWidth = this.calcColumnWidth(colIndex);
37955         this.cm.setColumnWidth(colIndex,
37956             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37957         if(!suppressEvent){
37958             this.grid.fireEvent("columnresize", colIndex, newWidth);
37959         }
37960     },
37961
37962     /**
37963      * Autofits all columns to their content and then expands to fit any extra space in the grid
37964      */
37965      autoSizeColumns : function(){
37966         var cm = this.grid.colModel;
37967         var colCount = cm.getColumnCount();
37968         for(var i = 0; i < colCount; i++){
37969             this.autoSizeColumn(i, true, true);
37970         }
37971         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37972             this.fitColumns();
37973         }else{
37974             this.updateColumns();
37975             this.layout();
37976         }
37977     },
37978
37979     /**
37980      * Autofits all columns to the grid's width proportionate with their current size
37981      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37982      */
37983     fitColumns : function(reserveScrollSpace){
37984         var cm = this.grid.colModel;
37985         var colCount = cm.getColumnCount();
37986         var cols = [];
37987         var width = 0;
37988         var i, w;
37989         for (i = 0; i < colCount; i++){
37990             if(!cm.isHidden(i) && !cm.isFixed(i)){
37991                 w = cm.getColumnWidth(i);
37992                 cols.push(i);
37993                 cols.push(w);
37994                 width += w;
37995             }
37996         }
37997         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37998         if(reserveScrollSpace){
37999             avail -= 17;
38000         }
38001         var frac = (avail - cm.getTotalWidth())/width;
38002         while (cols.length){
38003             w = cols.pop();
38004             i = cols.pop();
38005             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38006         }
38007         this.updateColumns();
38008         this.layout();
38009     },
38010
38011     onRowSelect : function(rowIndex){
38012         var row = this.getRowComposite(rowIndex);
38013         row.addClass("x-grid-row-selected");
38014     },
38015
38016     onRowDeselect : function(rowIndex){
38017         var row = this.getRowComposite(rowIndex);
38018         row.removeClass("x-grid-row-selected");
38019     },
38020
38021     onCellSelect : function(row, col){
38022         var cell = this.getCell(row, col);
38023         if(cell){
38024             Roo.fly(cell).addClass("x-grid-cell-selected");
38025         }
38026     },
38027
38028     onCellDeselect : function(row, col){
38029         var cell = this.getCell(row, col);
38030         if(cell){
38031             Roo.fly(cell).removeClass("x-grid-cell-selected");
38032         }
38033     },
38034
38035     updateHeaderSortState : function(){
38036         
38037         // sort state can be single { field: xxx, direction : yyy}
38038         // or   { xxx=>ASC , yyy : DESC ..... }
38039         
38040         var mstate = {};
38041         if (!this.ds.multiSort) { 
38042             var state = this.ds.getSortState();
38043             if(!state){
38044                 return;
38045             }
38046             mstate[state.field] = state.direction;
38047             // FIXME... - this is not used here.. but might be elsewhere..
38048             this.sortState = state;
38049             
38050         } else {
38051             mstate = this.ds.sortToggle;
38052         }
38053         //remove existing sort classes..
38054         
38055         var sc = this.sortClasses;
38056         var hds = this.el.select(this.headerSelector).removeClass(sc);
38057         
38058         for(var f in mstate) {
38059         
38060             var sortColumn = this.cm.findColumnIndex(f);
38061             
38062             if(sortColumn != -1){
38063                 var sortDir = mstate[f];        
38064                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38065             }
38066         }
38067         
38068          
38069         
38070     },
38071
38072
38073     handleHeaderClick : function(g, index,e){
38074         
38075         Roo.log("header click");
38076         
38077         if (Roo.isTouch) {
38078             // touch events on header are handled by context
38079             this.handleHdCtx(g,index,e);
38080             return;
38081         }
38082         
38083         
38084         if(this.headersDisabled){
38085             return;
38086         }
38087         var dm = g.dataSource, cm = g.colModel;
38088         if(!cm.isSortable(index)){
38089             return;
38090         }
38091         g.stopEditing();
38092         
38093         if (dm.multiSort) {
38094             // update the sortOrder
38095             var so = [];
38096             for(var i = 0; i < cm.config.length; i++ ) {
38097                 
38098                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38099                     continue; // dont' bother, it's not in sort list or being set.
38100                 }
38101                 
38102                 so.push(cm.config[i].dataIndex);
38103             };
38104             dm.sortOrder = so;
38105         }
38106         
38107         
38108         dm.sort(cm.getDataIndex(index));
38109     },
38110
38111
38112     destroy : function(){
38113         if(this.colMenu){
38114             this.colMenu.removeAll();
38115             Roo.menu.MenuMgr.unregister(this.colMenu);
38116             this.colMenu.getEl().remove();
38117             delete this.colMenu;
38118         }
38119         if(this.hmenu){
38120             this.hmenu.removeAll();
38121             Roo.menu.MenuMgr.unregister(this.hmenu);
38122             this.hmenu.getEl().remove();
38123             delete this.hmenu;
38124         }
38125         if(this.grid.enableColumnMove){
38126             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38127             if(dds){
38128                 for(var dd in dds){
38129                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38130                         var elid = dds[dd].dragElId;
38131                         dds[dd].unreg();
38132                         Roo.get(elid).remove();
38133                     } else if(dds[dd].config.isTarget){
38134                         dds[dd].proxyTop.remove();
38135                         dds[dd].proxyBottom.remove();
38136                         dds[dd].unreg();
38137                     }
38138                     if(Roo.dd.DDM.locationCache[dd]){
38139                         delete Roo.dd.DDM.locationCache[dd];
38140                     }
38141                 }
38142                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38143             }
38144         }
38145         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38146         this.bind(null, null);
38147         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38148     },
38149
38150     handleLockChange : function(){
38151         this.refresh(true);
38152     },
38153
38154     onDenyColumnLock : function(){
38155
38156     },
38157
38158     onDenyColumnHide : function(){
38159
38160     },
38161
38162     handleHdMenuClick : function(item){
38163         var index = this.hdCtxIndex;
38164         var cm = this.cm, ds = this.ds;
38165         switch(item.id){
38166             case "asc":
38167                 ds.sort(cm.getDataIndex(index), "ASC");
38168                 break;
38169             case "desc":
38170                 ds.sort(cm.getDataIndex(index), "DESC");
38171                 break;
38172             case "lock":
38173                 var lc = cm.getLockedCount();
38174                 if(cm.getColumnCount(true) <= lc+1){
38175                     this.onDenyColumnLock();
38176                     return;
38177                 }
38178                 if(lc != index){
38179                     cm.setLocked(index, true, true);
38180                     cm.moveColumn(index, lc);
38181                     this.grid.fireEvent("columnmove", index, lc);
38182                 }else{
38183                     cm.setLocked(index, true);
38184                 }
38185             break;
38186             case "unlock":
38187                 var lc = cm.getLockedCount();
38188                 if((lc-1) != index){
38189                     cm.setLocked(index, false, true);
38190                     cm.moveColumn(index, lc-1);
38191                     this.grid.fireEvent("columnmove", index, lc-1);
38192                 }else{
38193                     cm.setLocked(index, false);
38194                 }
38195             break;
38196             case 'wider': // used to expand cols on touch..
38197             case 'narrow':
38198                 var cw = cm.getColumnWidth(index);
38199                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38200                 cw = Math.max(0, cw);
38201                 cw = Math.min(cw,4000);
38202                 cm.setColumnWidth(index, cw);
38203                 break;
38204                 
38205             default:
38206                 index = cm.getIndexById(item.id.substr(4));
38207                 if(index != -1){
38208                     if(item.checked && cm.getColumnCount(true) <= 1){
38209                         this.onDenyColumnHide();
38210                         return false;
38211                     }
38212                     cm.setHidden(index, item.checked);
38213                 }
38214         }
38215         return true;
38216     },
38217
38218     beforeColMenuShow : function(){
38219         var cm = this.cm,  colCount = cm.getColumnCount();
38220         this.colMenu.removeAll();
38221         for(var i = 0; i < colCount; i++){
38222             this.colMenu.add(new Roo.menu.CheckItem({
38223                 id: "col-"+cm.getColumnId(i),
38224                 text: cm.getColumnHeader(i),
38225                 checked: !cm.isHidden(i),
38226                 hideOnClick:false
38227             }));
38228         }
38229     },
38230
38231     handleHdCtx : function(g, index, e){
38232         e.stopEvent();
38233         var hd = this.getHeaderCell(index);
38234         this.hdCtxIndex = index;
38235         var ms = this.hmenu.items, cm = this.cm;
38236         ms.get("asc").setDisabled(!cm.isSortable(index));
38237         ms.get("desc").setDisabled(!cm.isSortable(index));
38238         if(this.grid.enableColLock !== false){
38239             ms.get("lock").setDisabled(cm.isLocked(index));
38240             ms.get("unlock").setDisabled(!cm.isLocked(index));
38241         }
38242         this.hmenu.show(hd, "tl-bl");
38243     },
38244
38245     handleHdOver : function(e){
38246         var hd = this.findHeaderCell(e.getTarget());
38247         if(hd && !this.headersDisabled){
38248             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38249                this.fly(hd).addClass("x-grid-hd-over");
38250             }
38251         }
38252     },
38253
38254     handleHdOut : function(e){
38255         var hd = this.findHeaderCell(e.getTarget());
38256         if(hd){
38257             this.fly(hd).removeClass("x-grid-hd-over");
38258         }
38259     },
38260
38261     handleSplitDblClick : function(e, t){
38262         var i = this.getCellIndex(t);
38263         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38264             this.autoSizeColumn(i, true);
38265             this.layout();
38266         }
38267     },
38268
38269     render : function(){
38270
38271         var cm = this.cm;
38272         var colCount = cm.getColumnCount();
38273
38274         if(this.grid.monitorWindowResize === true){
38275             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38276         }
38277         var header = this.renderHeaders();
38278         var body = this.templates.body.apply({rows:""});
38279         var html = this.templates.master.apply({
38280             lockedBody: body,
38281             body: body,
38282             lockedHeader: header[0],
38283             header: header[1]
38284         });
38285
38286         //this.updateColumns();
38287
38288         this.grid.getGridEl().dom.innerHTML = html;
38289
38290         this.initElements();
38291         
38292         // a kludge to fix the random scolling effect in webkit
38293         this.el.on("scroll", function() {
38294             this.el.dom.scrollTop=0; // hopefully not recursive..
38295         },this);
38296
38297         this.scroller.on("scroll", this.handleScroll, this);
38298         this.lockedBody.on("mousewheel", this.handleWheel, this);
38299         this.mainBody.on("mousewheel", this.handleWheel, this);
38300
38301         this.mainHd.on("mouseover", this.handleHdOver, this);
38302         this.mainHd.on("mouseout", this.handleHdOut, this);
38303         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38304                 {delegate: "."+this.splitClass});
38305
38306         this.lockedHd.on("mouseover", this.handleHdOver, this);
38307         this.lockedHd.on("mouseout", this.handleHdOut, this);
38308         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38309                 {delegate: "."+this.splitClass});
38310
38311         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38312             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38313         }
38314
38315         this.updateSplitters();
38316
38317         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38318             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38319             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38320         }
38321
38322         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38323             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38324             this.hmenu.add(
38325                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38326                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38327             );
38328             if(this.grid.enableColLock !== false){
38329                 this.hmenu.add('-',
38330                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38331                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38332                 );
38333             }
38334             if (Roo.isTouch) {
38335                  this.hmenu.add('-',
38336                     {id:"wider", text: this.columnsWiderText},
38337                     {id:"narrow", text: this.columnsNarrowText }
38338                 );
38339                 
38340                  
38341             }
38342             
38343             if(this.grid.enableColumnHide !== false){
38344
38345                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38346                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38347                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38348
38349                 this.hmenu.add('-',
38350                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38351                 );
38352             }
38353             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38354
38355             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38356         }
38357
38358         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38359             this.dd = new Roo.grid.GridDragZone(this.grid, {
38360                 ddGroup : this.grid.ddGroup || 'GridDD'
38361             });
38362             
38363         }
38364
38365         /*
38366         for(var i = 0; i < colCount; i++){
38367             if(cm.isHidden(i)){
38368                 this.hideColumn(i);
38369             }
38370             if(cm.config[i].align){
38371                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38372                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38373             }
38374         }*/
38375         
38376         this.updateHeaderSortState();
38377
38378         this.beforeInitialResize();
38379         this.layout(true);
38380
38381         // two part rendering gives faster view to the user
38382         this.renderPhase2.defer(1, this);
38383     },
38384
38385     renderPhase2 : function(){
38386         // render the rows now
38387         this.refresh();
38388         if(this.grid.autoSizeColumns){
38389             this.autoSizeColumns();
38390         }
38391     },
38392
38393     beforeInitialResize : function(){
38394
38395     },
38396
38397     onColumnSplitterMoved : function(i, w){
38398         this.userResized = true;
38399         var cm = this.grid.colModel;
38400         cm.setColumnWidth(i, w, true);
38401         var cid = cm.getColumnId(i);
38402         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38403         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38404         this.updateSplitters();
38405         this.layout();
38406         this.grid.fireEvent("columnresize", i, w);
38407     },
38408
38409     syncRowHeights : function(startIndex, endIndex){
38410         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38411             startIndex = startIndex || 0;
38412             var mrows = this.getBodyTable().rows;
38413             var lrows = this.getLockedTable().rows;
38414             var len = mrows.length-1;
38415             endIndex = Math.min(endIndex || len, len);
38416             for(var i = startIndex; i <= endIndex; i++){
38417                 var m = mrows[i], l = lrows[i];
38418                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38419                 m.style.height = l.style.height = h + "px";
38420             }
38421         }
38422     },
38423
38424     layout : function(initialRender, is2ndPass){
38425         var g = this.grid;
38426         var auto = g.autoHeight;
38427         var scrollOffset = 16;
38428         var c = g.getGridEl(), cm = this.cm,
38429                 expandCol = g.autoExpandColumn,
38430                 gv = this;
38431         //c.beginMeasure();
38432
38433         if(!c.dom.offsetWidth){ // display:none?
38434             if(initialRender){
38435                 this.lockedWrap.show();
38436                 this.mainWrap.show();
38437             }
38438             return;
38439         }
38440
38441         var hasLock = this.cm.isLocked(0);
38442
38443         var tbh = this.headerPanel.getHeight();
38444         var bbh = this.footerPanel.getHeight();
38445
38446         if(auto){
38447             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38448             var newHeight = ch + c.getBorderWidth("tb");
38449             if(g.maxHeight){
38450                 newHeight = Math.min(g.maxHeight, newHeight);
38451             }
38452             c.setHeight(newHeight);
38453         }
38454
38455         if(g.autoWidth){
38456             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38457         }
38458
38459         var s = this.scroller;
38460
38461         var csize = c.getSize(true);
38462
38463         this.el.setSize(csize.width, csize.height);
38464
38465         this.headerPanel.setWidth(csize.width);
38466         this.footerPanel.setWidth(csize.width);
38467
38468         var hdHeight = this.mainHd.getHeight();
38469         var vw = csize.width;
38470         var vh = csize.height - (tbh + bbh);
38471
38472         s.setSize(vw, vh);
38473
38474         var bt = this.getBodyTable();
38475         var ltWidth = hasLock ?
38476                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38477
38478         var scrollHeight = bt.offsetHeight;
38479         var scrollWidth = ltWidth + bt.offsetWidth;
38480         var vscroll = false, hscroll = false;
38481
38482         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38483
38484         var lw = this.lockedWrap, mw = this.mainWrap;
38485         var lb = this.lockedBody, mb = this.mainBody;
38486
38487         setTimeout(function(){
38488             var t = s.dom.offsetTop;
38489             var w = s.dom.clientWidth,
38490                 h = s.dom.clientHeight;
38491
38492             lw.setTop(t);
38493             lw.setSize(ltWidth, h);
38494
38495             mw.setLeftTop(ltWidth, t);
38496             mw.setSize(w-ltWidth, h);
38497
38498             lb.setHeight(h-hdHeight);
38499             mb.setHeight(h-hdHeight);
38500
38501             if(is2ndPass !== true && !gv.userResized && expandCol){
38502                 // high speed resize without full column calculation
38503                 
38504                 var ci = cm.getIndexById(expandCol);
38505                 if (ci < 0) {
38506                     ci = cm.findColumnIndex(expandCol);
38507                 }
38508                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38509                 var expandId = cm.getColumnId(ci);
38510                 var  tw = cm.getTotalWidth(false);
38511                 var currentWidth = cm.getColumnWidth(ci);
38512                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38513                 if(currentWidth != cw){
38514                     cm.setColumnWidth(ci, cw, true);
38515                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38516                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38517                     gv.updateSplitters();
38518                     gv.layout(false, true);
38519                 }
38520             }
38521
38522             if(initialRender){
38523                 lw.show();
38524                 mw.show();
38525             }
38526             //c.endMeasure();
38527         }, 10);
38528     },
38529
38530     onWindowResize : function(){
38531         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38532             return;
38533         }
38534         this.layout();
38535     },
38536
38537     appendFooter : function(parentEl){
38538         return null;
38539     },
38540
38541     sortAscText : "Sort Ascending",
38542     sortDescText : "Sort Descending",
38543     lockText : "Lock Column",
38544     unlockText : "Unlock Column",
38545     columnsText : "Columns",
38546  
38547     columnsWiderText : "Wider",
38548     columnsNarrowText : "Thinner"
38549 });
38550
38551
38552 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38553     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38554     this.proxy.el.addClass('x-grid3-col-dd');
38555 };
38556
38557 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38558     handleMouseDown : function(e){
38559
38560     },
38561
38562     callHandleMouseDown : function(e){
38563         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38564     }
38565 });
38566 /*
38567  * Based on:
38568  * Ext JS Library 1.1.1
38569  * Copyright(c) 2006-2007, Ext JS, LLC.
38570  *
38571  * Originally Released Under LGPL - original licence link has changed is not relivant.
38572  *
38573  * Fork - LGPL
38574  * <script type="text/javascript">
38575  */
38576  
38577 // private
38578 // This is a support class used internally by the Grid components
38579 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38580     this.grid = grid;
38581     this.view = grid.getView();
38582     this.proxy = this.view.resizeProxy;
38583     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38584         "gridSplitters" + this.grid.getGridEl().id, {
38585         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38586     });
38587     this.setHandleElId(Roo.id(hd));
38588     this.setOuterHandleElId(Roo.id(hd2));
38589     this.scroll = false;
38590 };
38591 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38592     fly: Roo.Element.fly,
38593
38594     b4StartDrag : function(x, y){
38595         this.view.headersDisabled = true;
38596         this.proxy.setHeight(this.view.mainWrap.getHeight());
38597         var w = this.cm.getColumnWidth(this.cellIndex);
38598         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38599         this.resetConstraints();
38600         this.setXConstraint(minw, 1000);
38601         this.setYConstraint(0, 0);
38602         this.minX = x - minw;
38603         this.maxX = x + 1000;
38604         this.startPos = x;
38605         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38606     },
38607
38608
38609     handleMouseDown : function(e){
38610         ev = Roo.EventObject.setEvent(e);
38611         var t = this.fly(ev.getTarget());
38612         if(t.hasClass("x-grid-split")){
38613             this.cellIndex = this.view.getCellIndex(t.dom);
38614             this.split = t.dom;
38615             this.cm = this.grid.colModel;
38616             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38617                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38618             }
38619         }
38620     },
38621
38622     endDrag : function(e){
38623         this.view.headersDisabled = false;
38624         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38625         var diff = endX - this.startPos;
38626         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38627     },
38628
38629     autoOffset : function(){
38630         this.setDelta(0,0);
38631     }
38632 });/*
38633  * Based on:
38634  * Ext JS Library 1.1.1
38635  * Copyright(c) 2006-2007, Ext JS, LLC.
38636  *
38637  * Originally Released Under LGPL - original licence link has changed is not relivant.
38638  *
38639  * Fork - LGPL
38640  * <script type="text/javascript">
38641  */
38642  
38643 // private
38644 // This is a support class used internally by the Grid components
38645 Roo.grid.GridDragZone = function(grid, config){
38646     this.view = grid.getView();
38647     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38648     if(this.view.lockedBody){
38649         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38650         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38651     }
38652     this.scroll = false;
38653     this.grid = grid;
38654     this.ddel = document.createElement('div');
38655     this.ddel.className = 'x-grid-dd-wrap';
38656 };
38657
38658 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38659     ddGroup : "GridDD",
38660
38661     getDragData : function(e){
38662         var t = Roo.lib.Event.getTarget(e);
38663         var rowIndex = this.view.findRowIndex(t);
38664         var sm = this.grid.selModel;
38665             
38666         //Roo.log(rowIndex);
38667         
38668         if (sm.getSelectedCell) {
38669             // cell selection..
38670             if (!sm.getSelectedCell()) {
38671                 return false;
38672             }
38673             if (rowIndex != sm.getSelectedCell()[0]) {
38674                 return false;
38675             }
38676         
38677         }
38678         
38679         if(rowIndex !== false){
38680             
38681             // if editorgrid.. 
38682             
38683             
38684             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38685                
38686             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38687               //  
38688             //}
38689             if (e.hasModifier()){
38690                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38691             }
38692             
38693             Roo.log("getDragData");
38694             
38695             return {
38696                 grid: this.grid,
38697                 ddel: this.ddel,
38698                 rowIndex: rowIndex,
38699                 selections:sm.getSelections ? sm.getSelections() : (
38700                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38701                 )
38702             };
38703         }
38704         return false;
38705     },
38706
38707     onInitDrag : function(e){
38708         var data = this.dragData;
38709         this.ddel.innerHTML = this.grid.getDragDropText();
38710         this.proxy.update(this.ddel);
38711         // fire start drag?
38712     },
38713
38714     afterRepair : function(){
38715         this.dragging = false;
38716     },
38717
38718     getRepairXY : function(e, data){
38719         return false;
38720     },
38721
38722     onEndDrag : function(data, e){
38723         // fire end drag?
38724     },
38725
38726     onValidDrop : function(dd, e, id){
38727         // fire drag drop?
38728         this.hideProxy();
38729     },
38730
38731     beforeInvalidDrop : function(e, id){
38732
38733     }
38734 });/*
38735  * Based on:
38736  * Ext JS Library 1.1.1
38737  * Copyright(c) 2006-2007, Ext JS, LLC.
38738  *
38739  * Originally Released Under LGPL - original licence link has changed is not relivant.
38740  *
38741  * Fork - LGPL
38742  * <script type="text/javascript">
38743  */
38744  
38745
38746 /**
38747  * @class Roo.grid.ColumnModel
38748  * @extends Roo.util.Observable
38749  * This is the default implementation of a ColumnModel used by the Grid. It defines
38750  * the columns in the grid.
38751  * <br>Usage:<br>
38752  <pre><code>
38753  var colModel = new Roo.grid.ColumnModel([
38754         {header: "Ticker", width: 60, sortable: true, locked: true},
38755         {header: "Company Name", width: 150, sortable: true},
38756         {header: "Market Cap.", width: 100, sortable: true},
38757         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38758         {header: "Employees", width: 100, sortable: true, resizable: false}
38759  ]);
38760  </code></pre>
38761  * <p>
38762  
38763  * The config options listed for this class are options which may appear in each
38764  * individual column definition.
38765  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38766  * @constructor
38767  * @param {Object} config An Array of column config objects. See this class's
38768  * config objects for details.
38769 */
38770 Roo.grid.ColumnModel = function(config){
38771         /**
38772      * The config passed into the constructor
38773      */
38774     this.config = config;
38775     this.lookup = {};
38776
38777     // if no id, create one
38778     // if the column does not have a dataIndex mapping,
38779     // map it to the order it is in the config
38780     for(var i = 0, len = config.length; i < len; i++){
38781         var c = config[i];
38782         if(typeof c.dataIndex == "undefined"){
38783             c.dataIndex = i;
38784         }
38785         if(typeof c.renderer == "string"){
38786             c.renderer = Roo.util.Format[c.renderer];
38787         }
38788         if(typeof c.id == "undefined"){
38789             c.id = Roo.id();
38790         }
38791         if(c.editor && c.editor.xtype){
38792             c.editor  = Roo.factory(c.editor, Roo.grid);
38793         }
38794         if(c.editor && c.editor.isFormField){
38795             c.editor = new Roo.grid.GridEditor(c.editor);
38796         }
38797         this.lookup[c.id] = c;
38798     }
38799
38800     /**
38801      * The width of columns which have no width specified (defaults to 100)
38802      * @type Number
38803      */
38804     this.defaultWidth = 100;
38805
38806     /**
38807      * Default sortable of columns which have no sortable specified (defaults to false)
38808      * @type Boolean
38809      */
38810     this.defaultSortable = false;
38811
38812     this.addEvents({
38813         /**
38814              * @event widthchange
38815              * Fires when the width of a column changes.
38816              * @param {ColumnModel} this
38817              * @param {Number} columnIndex The column index
38818              * @param {Number} newWidth The new width
38819              */
38820             "widthchange": true,
38821         /**
38822              * @event headerchange
38823              * Fires when the text of a header changes.
38824              * @param {ColumnModel} this
38825              * @param {Number} columnIndex The column index
38826              * @param {Number} newText The new header text
38827              */
38828             "headerchange": true,
38829         /**
38830              * @event hiddenchange
38831              * Fires when a column is hidden or "unhidden".
38832              * @param {ColumnModel} this
38833              * @param {Number} columnIndex The column index
38834              * @param {Boolean} hidden true if hidden, false otherwise
38835              */
38836             "hiddenchange": true,
38837             /**
38838          * @event columnmoved
38839          * Fires when a column is moved.
38840          * @param {ColumnModel} this
38841          * @param {Number} oldIndex
38842          * @param {Number} newIndex
38843          */
38844         "columnmoved" : true,
38845         /**
38846          * @event columlockchange
38847          * Fires when a column's locked state is changed
38848          * @param {ColumnModel} this
38849          * @param {Number} colIndex
38850          * @param {Boolean} locked true if locked
38851          */
38852         "columnlockchange" : true
38853     });
38854     Roo.grid.ColumnModel.superclass.constructor.call(this);
38855 };
38856 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38857     /**
38858      * @cfg {String} header The header text to display in the Grid view.
38859      */
38860     /**
38861      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38862      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38863      * specified, the column's index is used as an index into the Record's data Array.
38864      */
38865     /**
38866      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38867      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38868      */
38869     /**
38870      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38871      * Defaults to the value of the {@link #defaultSortable} property.
38872      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38873      */
38874     /**
38875      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38876      */
38877     /**
38878      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38879      */
38880     /**
38881      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38882      */
38883     /**
38884      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38885      */
38886     /**
38887      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38888      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38889      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38890      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38891      */
38892        /**
38893      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38894      */
38895     /**
38896      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38897      */
38898     /**
38899      * @cfg {String} cursor (Optional)
38900      */
38901     /**
38902      * @cfg {String} tooltip (Optional)
38903      */
38904     /**
38905      * Returns the id of the column at the specified index.
38906      * @param {Number} index The column index
38907      * @return {String} the id
38908      */
38909     getColumnId : function(index){
38910         return this.config[index].id;
38911     },
38912
38913     /**
38914      * Returns the column for a specified id.
38915      * @param {String} id The column id
38916      * @return {Object} the column
38917      */
38918     getColumnById : function(id){
38919         return this.lookup[id];
38920     },
38921
38922     
38923     /**
38924      * Returns the column for a specified dataIndex.
38925      * @param {String} dataIndex The column dataIndex
38926      * @return {Object|Boolean} the column or false if not found
38927      */
38928     getColumnByDataIndex: function(dataIndex){
38929         var index = this.findColumnIndex(dataIndex);
38930         return index > -1 ? this.config[index] : false;
38931     },
38932     
38933     /**
38934      * Returns the index for a specified column id.
38935      * @param {String} id The column id
38936      * @return {Number} the index, or -1 if not found
38937      */
38938     getIndexById : function(id){
38939         for(var i = 0, len = this.config.length; i < len; i++){
38940             if(this.config[i].id == id){
38941                 return i;
38942             }
38943         }
38944         return -1;
38945     },
38946     
38947     /**
38948      * Returns the index for a specified column dataIndex.
38949      * @param {String} dataIndex The column dataIndex
38950      * @return {Number} the index, or -1 if not found
38951      */
38952     
38953     findColumnIndex : function(dataIndex){
38954         for(var i = 0, len = this.config.length; i < len; i++){
38955             if(this.config[i].dataIndex == dataIndex){
38956                 return i;
38957             }
38958         }
38959         return -1;
38960     },
38961     
38962     
38963     moveColumn : function(oldIndex, newIndex){
38964         var c = this.config[oldIndex];
38965         this.config.splice(oldIndex, 1);
38966         this.config.splice(newIndex, 0, c);
38967         this.dataMap = null;
38968         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38969     },
38970
38971     isLocked : function(colIndex){
38972         return this.config[colIndex].locked === true;
38973     },
38974
38975     setLocked : function(colIndex, value, suppressEvent){
38976         if(this.isLocked(colIndex) == value){
38977             return;
38978         }
38979         this.config[colIndex].locked = value;
38980         if(!suppressEvent){
38981             this.fireEvent("columnlockchange", this, colIndex, value);
38982         }
38983     },
38984
38985     getTotalLockedWidth : function(){
38986         var totalWidth = 0;
38987         for(var i = 0; i < this.config.length; i++){
38988             if(this.isLocked(i) && !this.isHidden(i)){
38989                 this.totalWidth += this.getColumnWidth(i);
38990             }
38991         }
38992         return totalWidth;
38993     },
38994
38995     getLockedCount : function(){
38996         for(var i = 0, len = this.config.length; i < len; i++){
38997             if(!this.isLocked(i)){
38998                 return i;
38999             }
39000         }
39001     },
39002
39003     /**
39004      * Returns the number of columns.
39005      * @return {Number}
39006      */
39007     getColumnCount : function(visibleOnly){
39008         if(visibleOnly === true){
39009             var c = 0;
39010             for(var i = 0, len = this.config.length; i < len; i++){
39011                 if(!this.isHidden(i)){
39012                     c++;
39013                 }
39014             }
39015             return c;
39016         }
39017         return this.config.length;
39018     },
39019
39020     /**
39021      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39022      * @param {Function} fn
39023      * @param {Object} scope (optional)
39024      * @return {Array} result
39025      */
39026     getColumnsBy : function(fn, scope){
39027         var r = [];
39028         for(var i = 0, len = this.config.length; i < len; i++){
39029             var c = this.config[i];
39030             if(fn.call(scope||this, c, i) === true){
39031                 r[r.length] = c;
39032             }
39033         }
39034         return r;
39035     },
39036
39037     /**
39038      * Returns true if the specified column is sortable.
39039      * @param {Number} col The column index
39040      * @return {Boolean}
39041      */
39042     isSortable : function(col){
39043         if(typeof this.config[col].sortable == "undefined"){
39044             return this.defaultSortable;
39045         }
39046         return this.config[col].sortable;
39047     },
39048
39049     /**
39050      * Returns the rendering (formatting) function defined for the column.
39051      * @param {Number} col The column index.
39052      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39053      */
39054     getRenderer : function(col){
39055         if(!this.config[col].renderer){
39056             return Roo.grid.ColumnModel.defaultRenderer;
39057         }
39058         return this.config[col].renderer;
39059     },
39060
39061     /**
39062      * Sets the rendering (formatting) function for a column.
39063      * @param {Number} col The column index
39064      * @param {Function} fn The function to use to process the cell's raw data
39065      * to return HTML markup for the grid view. The render function is called with
39066      * the following parameters:<ul>
39067      * <li>Data value.</li>
39068      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39069      * <li>css A CSS style string to apply to the table cell.</li>
39070      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39071      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39072      * <li>Row index</li>
39073      * <li>Column index</li>
39074      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39075      */
39076     setRenderer : function(col, fn){
39077         this.config[col].renderer = fn;
39078     },
39079
39080     /**
39081      * Returns the width for the specified column.
39082      * @param {Number} col The column index
39083      * @return {Number}
39084      */
39085     getColumnWidth : function(col){
39086         return this.config[col].width * 1 || this.defaultWidth;
39087     },
39088
39089     /**
39090      * Sets the width for a column.
39091      * @param {Number} col The column index
39092      * @param {Number} width The new width
39093      */
39094     setColumnWidth : function(col, width, suppressEvent){
39095         this.config[col].width = width;
39096         this.totalWidth = null;
39097         if(!suppressEvent){
39098              this.fireEvent("widthchange", this, col, width);
39099         }
39100     },
39101
39102     /**
39103      * Returns the total width of all columns.
39104      * @param {Boolean} includeHidden True to include hidden column widths
39105      * @return {Number}
39106      */
39107     getTotalWidth : function(includeHidden){
39108         if(!this.totalWidth){
39109             this.totalWidth = 0;
39110             for(var i = 0, len = this.config.length; i < len; i++){
39111                 if(includeHidden || !this.isHidden(i)){
39112                     this.totalWidth += this.getColumnWidth(i);
39113                 }
39114             }
39115         }
39116         return this.totalWidth;
39117     },
39118
39119     /**
39120      * Returns the header for the specified column.
39121      * @param {Number} col The column index
39122      * @return {String}
39123      */
39124     getColumnHeader : function(col){
39125         return this.config[col].header;
39126     },
39127
39128     /**
39129      * Sets the header for a column.
39130      * @param {Number} col The column index
39131      * @param {String} header The new header
39132      */
39133     setColumnHeader : function(col, header){
39134         this.config[col].header = header;
39135         this.fireEvent("headerchange", this, col, header);
39136     },
39137
39138     /**
39139      * Returns the tooltip for the specified column.
39140      * @param {Number} col The column index
39141      * @return {String}
39142      */
39143     getColumnTooltip : function(col){
39144             return this.config[col].tooltip;
39145     },
39146     /**
39147      * Sets the tooltip for a column.
39148      * @param {Number} col The column index
39149      * @param {String} tooltip The new tooltip
39150      */
39151     setColumnTooltip : function(col, tooltip){
39152             this.config[col].tooltip = tooltip;
39153     },
39154
39155     /**
39156      * Returns the dataIndex for the specified column.
39157      * @param {Number} col The column index
39158      * @return {Number}
39159      */
39160     getDataIndex : function(col){
39161         return this.config[col].dataIndex;
39162     },
39163
39164     /**
39165      * Sets the dataIndex for a column.
39166      * @param {Number} col The column index
39167      * @param {Number} dataIndex The new dataIndex
39168      */
39169     setDataIndex : function(col, dataIndex){
39170         this.config[col].dataIndex = dataIndex;
39171     },
39172
39173     
39174     
39175     /**
39176      * Returns true if the cell is editable.
39177      * @param {Number} colIndex The column index
39178      * @param {Number} rowIndex The row index
39179      * @return {Boolean}
39180      */
39181     isCellEditable : function(colIndex, rowIndex){
39182         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39183     },
39184
39185     /**
39186      * Returns the editor defined for the cell/column.
39187      * return false or null to disable editing.
39188      * @param {Number} colIndex The column index
39189      * @param {Number} rowIndex The row index
39190      * @return {Object}
39191      */
39192     getCellEditor : function(colIndex, rowIndex){
39193         return this.config[colIndex].editor;
39194     },
39195
39196     /**
39197      * Sets if a column is editable.
39198      * @param {Number} col The column index
39199      * @param {Boolean} editable True if the column is editable
39200      */
39201     setEditable : function(col, editable){
39202         this.config[col].editable = editable;
39203     },
39204
39205
39206     /**
39207      * Returns true if the column is hidden.
39208      * @param {Number} colIndex The column index
39209      * @return {Boolean}
39210      */
39211     isHidden : function(colIndex){
39212         return this.config[colIndex].hidden;
39213     },
39214
39215
39216     /**
39217      * Returns true if the column width cannot be changed
39218      */
39219     isFixed : function(colIndex){
39220         return this.config[colIndex].fixed;
39221     },
39222
39223     /**
39224      * Returns true if the column can be resized
39225      * @return {Boolean}
39226      */
39227     isResizable : function(colIndex){
39228         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39229     },
39230     /**
39231      * Sets if a column is hidden.
39232      * @param {Number} colIndex The column index
39233      * @param {Boolean} hidden True if the column is hidden
39234      */
39235     setHidden : function(colIndex, hidden){
39236         this.config[colIndex].hidden = hidden;
39237         this.totalWidth = null;
39238         this.fireEvent("hiddenchange", this, colIndex, hidden);
39239     },
39240
39241     /**
39242      * Sets the editor for a column.
39243      * @param {Number} col The column index
39244      * @param {Object} editor The editor object
39245      */
39246     setEditor : function(col, editor){
39247         this.config[col].editor = editor;
39248     }
39249 });
39250
39251 Roo.grid.ColumnModel.defaultRenderer = function(value){
39252         if(typeof value == "string" && value.length < 1){
39253             return "&#160;";
39254         }
39255         return value;
39256 };
39257
39258 // Alias for backwards compatibility
39259 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39260 /*
39261  * Based on:
39262  * Ext JS Library 1.1.1
39263  * Copyright(c) 2006-2007, Ext JS, LLC.
39264  *
39265  * Originally Released Under LGPL - original licence link has changed is not relivant.
39266  *
39267  * Fork - LGPL
39268  * <script type="text/javascript">
39269  */
39270
39271 /**
39272  * @class Roo.grid.AbstractSelectionModel
39273  * @extends Roo.util.Observable
39274  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39275  * implemented by descendant classes.  This class should not be directly instantiated.
39276  * @constructor
39277  */
39278 Roo.grid.AbstractSelectionModel = function(){
39279     this.locked = false;
39280     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39281 };
39282
39283 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39284     /** @ignore Called by the grid automatically. Do not call directly. */
39285     init : function(grid){
39286         this.grid = grid;
39287         this.initEvents();
39288     },
39289
39290     /**
39291      * Locks the selections.
39292      */
39293     lock : function(){
39294         this.locked = true;
39295     },
39296
39297     /**
39298      * Unlocks the selections.
39299      */
39300     unlock : function(){
39301         this.locked = false;
39302     },
39303
39304     /**
39305      * Returns true if the selections are locked.
39306      * @return {Boolean}
39307      */
39308     isLocked : function(){
39309         return this.locked;
39310     }
39311 });/*
39312  * Based on:
39313  * Ext JS Library 1.1.1
39314  * Copyright(c) 2006-2007, Ext JS, LLC.
39315  *
39316  * Originally Released Under LGPL - original licence link has changed is not relivant.
39317  *
39318  * Fork - LGPL
39319  * <script type="text/javascript">
39320  */
39321 /**
39322  * @extends Roo.grid.AbstractSelectionModel
39323  * @class Roo.grid.RowSelectionModel
39324  * The default SelectionModel used by {@link Roo.grid.Grid}.
39325  * It supports multiple selections and keyboard selection/navigation. 
39326  * @constructor
39327  * @param {Object} config
39328  */
39329 Roo.grid.RowSelectionModel = function(config){
39330     Roo.apply(this, config);
39331     this.selections = new Roo.util.MixedCollection(false, function(o){
39332         return o.id;
39333     });
39334
39335     this.last = false;
39336     this.lastActive = false;
39337
39338     this.addEvents({
39339         /**
39340              * @event selectionchange
39341              * Fires when the selection changes
39342              * @param {SelectionModel} this
39343              */
39344             "selectionchange" : true,
39345         /**
39346              * @event afterselectionchange
39347              * Fires after the selection changes (eg. by key press or clicking)
39348              * @param {SelectionModel} this
39349              */
39350             "afterselectionchange" : true,
39351         /**
39352              * @event beforerowselect
39353              * Fires when a row is selected being selected, return false to cancel.
39354              * @param {SelectionModel} this
39355              * @param {Number} rowIndex The selected index
39356              * @param {Boolean} keepExisting False if other selections will be cleared
39357              */
39358             "beforerowselect" : true,
39359         /**
39360              * @event rowselect
39361              * Fires when a row is selected.
39362              * @param {SelectionModel} this
39363              * @param {Number} rowIndex The selected index
39364              * @param {Roo.data.Record} r The record
39365              */
39366             "rowselect" : true,
39367         /**
39368              * @event rowdeselect
39369              * Fires when a row is deselected.
39370              * @param {SelectionModel} this
39371              * @param {Number} rowIndex The selected index
39372              */
39373         "rowdeselect" : true
39374     });
39375     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39376     this.locked = false;
39377 };
39378
39379 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39380     /**
39381      * @cfg {Boolean} singleSelect
39382      * True to allow selection of only one row at a time (defaults to false)
39383      */
39384     singleSelect : false,
39385
39386     // private
39387     initEvents : function(){
39388
39389         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39390             this.grid.on("mousedown", this.handleMouseDown, this);
39391         }else{ // allow click to work like normal
39392             this.grid.on("rowclick", this.handleDragableRowClick, this);
39393         }
39394
39395         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39396             "up" : function(e){
39397                 if(!e.shiftKey){
39398                     this.selectPrevious(e.shiftKey);
39399                 }else if(this.last !== false && this.lastActive !== false){
39400                     var last = this.last;
39401                     this.selectRange(this.last,  this.lastActive-1);
39402                     this.grid.getView().focusRow(this.lastActive);
39403                     if(last !== false){
39404                         this.last = last;
39405                     }
39406                 }else{
39407                     this.selectFirstRow();
39408                 }
39409                 this.fireEvent("afterselectionchange", this);
39410             },
39411             "down" : function(e){
39412                 if(!e.shiftKey){
39413                     this.selectNext(e.shiftKey);
39414                 }else if(this.last !== false && this.lastActive !== false){
39415                     var last = this.last;
39416                     this.selectRange(this.last,  this.lastActive+1);
39417                     this.grid.getView().focusRow(this.lastActive);
39418                     if(last !== false){
39419                         this.last = last;
39420                     }
39421                 }else{
39422                     this.selectFirstRow();
39423                 }
39424                 this.fireEvent("afterselectionchange", this);
39425             },
39426             scope: this
39427         });
39428
39429         var view = this.grid.view;
39430         view.on("refresh", this.onRefresh, this);
39431         view.on("rowupdated", this.onRowUpdated, this);
39432         view.on("rowremoved", this.onRemove, this);
39433     },
39434
39435     // private
39436     onRefresh : function(){
39437         var ds = this.grid.dataSource, i, v = this.grid.view;
39438         var s = this.selections;
39439         s.each(function(r){
39440             if((i = ds.indexOfId(r.id)) != -1){
39441                 v.onRowSelect(i);
39442             }else{
39443                 s.remove(r);
39444             }
39445         });
39446     },
39447
39448     // private
39449     onRemove : function(v, index, r){
39450         this.selections.remove(r);
39451     },
39452
39453     // private
39454     onRowUpdated : function(v, index, r){
39455         if(this.isSelected(r)){
39456             v.onRowSelect(index);
39457         }
39458     },
39459
39460     /**
39461      * Select records.
39462      * @param {Array} records The records to select
39463      * @param {Boolean} keepExisting (optional) True to keep existing selections
39464      */
39465     selectRecords : function(records, keepExisting){
39466         if(!keepExisting){
39467             this.clearSelections();
39468         }
39469         var ds = this.grid.dataSource;
39470         for(var i = 0, len = records.length; i < len; i++){
39471             this.selectRow(ds.indexOf(records[i]), true);
39472         }
39473     },
39474
39475     /**
39476      * Gets the number of selected rows.
39477      * @return {Number}
39478      */
39479     getCount : function(){
39480         return this.selections.length;
39481     },
39482
39483     /**
39484      * Selects the first row in the grid.
39485      */
39486     selectFirstRow : function(){
39487         this.selectRow(0);
39488     },
39489
39490     /**
39491      * Select the last row.
39492      * @param {Boolean} keepExisting (optional) True to keep existing selections
39493      */
39494     selectLastRow : function(keepExisting){
39495         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39496     },
39497
39498     /**
39499      * Selects the row immediately following the last selected row.
39500      * @param {Boolean} keepExisting (optional) True to keep existing selections
39501      */
39502     selectNext : function(keepExisting){
39503         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39504             this.selectRow(this.last+1, keepExisting);
39505             this.grid.getView().focusRow(this.last);
39506         }
39507     },
39508
39509     /**
39510      * Selects the row that precedes the last selected row.
39511      * @param {Boolean} keepExisting (optional) True to keep existing selections
39512      */
39513     selectPrevious : function(keepExisting){
39514         if(this.last){
39515             this.selectRow(this.last-1, keepExisting);
39516             this.grid.getView().focusRow(this.last);
39517         }
39518     },
39519
39520     /**
39521      * Returns the selected records
39522      * @return {Array} Array of selected records
39523      */
39524     getSelections : function(){
39525         return [].concat(this.selections.items);
39526     },
39527
39528     /**
39529      * Returns the first selected record.
39530      * @return {Record}
39531      */
39532     getSelected : function(){
39533         return this.selections.itemAt(0);
39534     },
39535
39536
39537     /**
39538      * Clears all selections.
39539      */
39540     clearSelections : function(fast){
39541         if(this.locked) return;
39542         if(fast !== true){
39543             var ds = this.grid.dataSource;
39544             var s = this.selections;
39545             s.each(function(r){
39546                 this.deselectRow(ds.indexOfId(r.id));
39547             }, this);
39548             s.clear();
39549         }else{
39550             this.selections.clear();
39551         }
39552         this.last = false;
39553     },
39554
39555
39556     /**
39557      * Selects all rows.
39558      */
39559     selectAll : function(){
39560         if(this.locked) return;
39561         this.selections.clear();
39562         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39563             this.selectRow(i, true);
39564         }
39565     },
39566
39567     /**
39568      * Returns True if there is a selection.
39569      * @return {Boolean}
39570      */
39571     hasSelection : function(){
39572         return this.selections.length > 0;
39573     },
39574
39575     /**
39576      * Returns True if the specified row is selected.
39577      * @param {Number/Record} record The record or index of the record to check
39578      * @return {Boolean}
39579      */
39580     isSelected : function(index){
39581         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39582         return (r && this.selections.key(r.id) ? true : false);
39583     },
39584
39585     /**
39586      * Returns True if the specified record id is selected.
39587      * @param {String} id The id of record to check
39588      * @return {Boolean}
39589      */
39590     isIdSelected : function(id){
39591         return (this.selections.key(id) ? true : false);
39592     },
39593
39594     // private
39595     handleMouseDown : function(e, t){
39596         var view = this.grid.getView(), rowIndex;
39597         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39598             return;
39599         };
39600         if(e.shiftKey && this.last !== false){
39601             var last = this.last;
39602             this.selectRange(last, rowIndex, e.ctrlKey);
39603             this.last = last; // reset the last
39604             view.focusRow(rowIndex);
39605         }else{
39606             var isSelected = this.isSelected(rowIndex);
39607             if(e.button !== 0 && isSelected){
39608                 view.focusRow(rowIndex);
39609             }else if(e.ctrlKey && isSelected){
39610                 this.deselectRow(rowIndex);
39611             }else if(!isSelected){
39612                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39613                 view.focusRow(rowIndex);
39614             }
39615         }
39616         this.fireEvent("afterselectionchange", this);
39617     },
39618     // private
39619     handleDragableRowClick :  function(grid, rowIndex, e) 
39620     {
39621         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39622             this.selectRow(rowIndex, false);
39623             grid.view.focusRow(rowIndex);
39624              this.fireEvent("afterselectionchange", this);
39625         }
39626     },
39627     
39628     /**
39629      * Selects multiple rows.
39630      * @param {Array} rows Array of the indexes of the row to select
39631      * @param {Boolean} keepExisting (optional) True to keep existing selections
39632      */
39633     selectRows : function(rows, keepExisting){
39634         if(!keepExisting){
39635             this.clearSelections();
39636         }
39637         for(var i = 0, len = rows.length; i < len; i++){
39638             this.selectRow(rows[i], true);
39639         }
39640     },
39641
39642     /**
39643      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39644      * @param {Number} startRow The index of the first row in the range
39645      * @param {Number} endRow The index of the last row in the range
39646      * @param {Boolean} keepExisting (optional) True to retain existing selections
39647      */
39648     selectRange : function(startRow, endRow, keepExisting){
39649         if(this.locked) return;
39650         if(!keepExisting){
39651             this.clearSelections();
39652         }
39653         if(startRow <= endRow){
39654             for(var i = startRow; i <= endRow; i++){
39655                 this.selectRow(i, true);
39656             }
39657         }else{
39658             for(var i = startRow; i >= endRow; i--){
39659                 this.selectRow(i, true);
39660             }
39661         }
39662     },
39663
39664     /**
39665      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39666      * @param {Number} startRow The index of the first row in the range
39667      * @param {Number} endRow The index of the last row in the range
39668      */
39669     deselectRange : function(startRow, endRow, preventViewNotify){
39670         if(this.locked) return;
39671         for(var i = startRow; i <= endRow; i++){
39672             this.deselectRow(i, preventViewNotify);
39673         }
39674     },
39675
39676     /**
39677      * Selects a row.
39678      * @param {Number} row The index of the row to select
39679      * @param {Boolean} keepExisting (optional) True to keep existing selections
39680      */
39681     selectRow : function(index, keepExisting, preventViewNotify){
39682         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39683         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39684             if(!keepExisting || this.singleSelect){
39685                 this.clearSelections();
39686             }
39687             var r = this.grid.dataSource.getAt(index);
39688             this.selections.add(r);
39689             this.last = this.lastActive = index;
39690             if(!preventViewNotify){
39691                 this.grid.getView().onRowSelect(index);
39692             }
39693             this.fireEvent("rowselect", this, index, r);
39694             this.fireEvent("selectionchange", this);
39695         }
39696     },
39697
39698     /**
39699      * Deselects a row.
39700      * @param {Number} row The index of the row to deselect
39701      */
39702     deselectRow : function(index, preventViewNotify){
39703         if(this.locked) return;
39704         if(this.last == index){
39705             this.last = false;
39706         }
39707         if(this.lastActive == index){
39708             this.lastActive = false;
39709         }
39710         var r = this.grid.dataSource.getAt(index);
39711         this.selections.remove(r);
39712         if(!preventViewNotify){
39713             this.grid.getView().onRowDeselect(index);
39714         }
39715         this.fireEvent("rowdeselect", this, index);
39716         this.fireEvent("selectionchange", this);
39717     },
39718
39719     // private
39720     restoreLast : function(){
39721         if(this._last){
39722             this.last = this._last;
39723         }
39724     },
39725
39726     // private
39727     acceptsNav : function(row, col, cm){
39728         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39729     },
39730
39731     // private
39732     onEditorKey : function(field, e){
39733         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39734         if(k == e.TAB){
39735             e.stopEvent();
39736             ed.completeEdit();
39737             if(e.shiftKey){
39738                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39739             }else{
39740                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39741             }
39742         }else if(k == e.ENTER && !e.ctrlKey){
39743             e.stopEvent();
39744             ed.completeEdit();
39745             if(e.shiftKey){
39746                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39747             }else{
39748                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39749             }
39750         }else if(k == e.ESC){
39751             ed.cancelEdit();
39752         }
39753         if(newCell){
39754             g.startEditing(newCell[0], newCell[1]);
39755         }
39756     }
39757 });/*
39758  * Based on:
39759  * Ext JS Library 1.1.1
39760  * Copyright(c) 2006-2007, Ext JS, LLC.
39761  *
39762  * Originally Released Under LGPL - original licence link has changed is not relivant.
39763  *
39764  * Fork - LGPL
39765  * <script type="text/javascript">
39766  */
39767 /**
39768  * @class Roo.grid.CellSelectionModel
39769  * @extends Roo.grid.AbstractSelectionModel
39770  * This class provides the basic implementation for cell selection in a grid.
39771  * @constructor
39772  * @param {Object} config The object containing the configuration of this model.
39773  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39774  */
39775 Roo.grid.CellSelectionModel = function(config){
39776     Roo.apply(this, config);
39777
39778     this.selection = null;
39779
39780     this.addEvents({
39781         /**
39782              * @event beforerowselect
39783              * Fires before a cell is selected.
39784              * @param {SelectionModel} this
39785              * @param {Number} rowIndex The selected row index
39786              * @param {Number} colIndex The selected cell index
39787              */
39788             "beforecellselect" : true,
39789         /**
39790              * @event cellselect
39791              * Fires when a cell is selected.
39792              * @param {SelectionModel} this
39793              * @param {Number} rowIndex The selected row index
39794              * @param {Number} colIndex The selected cell index
39795              */
39796             "cellselect" : true,
39797         /**
39798              * @event selectionchange
39799              * Fires when the active selection changes.
39800              * @param {SelectionModel} this
39801              * @param {Object} selection null for no selection or an object (o) with two properties
39802                 <ul>
39803                 <li>o.record: the record object for the row the selection is in</li>
39804                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39805                 </ul>
39806              */
39807             "selectionchange" : true,
39808         /**
39809              * @event tabend
39810              * Fires when the tab (or enter) was pressed on the last editable cell
39811              * You can use this to trigger add new row.
39812              * @param {SelectionModel} this
39813              */
39814             "tabend" : true,
39815          /**
39816              * @event beforeeditnext
39817              * Fires before the next editable sell is made active
39818              * You can use this to skip to another cell or fire the tabend
39819              *    if you set cell to false
39820              * @param {Object} eventdata object : { cell : [ row, col ] } 
39821              */
39822             "beforeeditnext" : true
39823     });
39824     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39825 };
39826
39827 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39828     
39829     enter_is_tab: false,
39830
39831     /** @ignore */
39832     initEvents : function(){
39833         this.grid.on("mousedown", this.handleMouseDown, this);
39834         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39835         var view = this.grid.view;
39836         view.on("refresh", this.onViewChange, this);
39837         view.on("rowupdated", this.onRowUpdated, this);
39838         view.on("beforerowremoved", this.clearSelections, this);
39839         view.on("beforerowsinserted", this.clearSelections, this);
39840         if(this.grid.isEditor){
39841             this.grid.on("beforeedit", this.beforeEdit,  this);
39842         }
39843     },
39844
39845         //private
39846     beforeEdit : function(e){
39847         this.select(e.row, e.column, false, true, e.record);
39848     },
39849
39850         //private
39851     onRowUpdated : function(v, index, r){
39852         if(this.selection && this.selection.record == r){
39853             v.onCellSelect(index, this.selection.cell[1]);
39854         }
39855     },
39856
39857         //private
39858     onViewChange : function(){
39859         this.clearSelections(true);
39860     },
39861
39862         /**
39863          * Returns the currently selected cell,.
39864          * @return {Array} The selected cell (row, column) or null if none selected.
39865          */
39866     getSelectedCell : function(){
39867         return this.selection ? this.selection.cell : null;
39868     },
39869
39870     /**
39871      * Clears all selections.
39872      * @param {Boolean} true to prevent the gridview from being notified about the change.
39873      */
39874     clearSelections : function(preventNotify){
39875         var s = this.selection;
39876         if(s){
39877             if(preventNotify !== true){
39878                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39879             }
39880             this.selection = null;
39881             this.fireEvent("selectionchange", this, null);
39882         }
39883     },
39884
39885     /**
39886      * Returns true if there is a selection.
39887      * @return {Boolean}
39888      */
39889     hasSelection : function(){
39890         return this.selection ? true : false;
39891     },
39892
39893     /** @ignore */
39894     handleMouseDown : function(e, t){
39895         var v = this.grid.getView();
39896         if(this.isLocked()){
39897             return;
39898         };
39899         var row = v.findRowIndex(t);
39900         var cell = v.findCellIndex(t);
39901         if(row !== false && cell !== false){
39902             this.select(row, cell);
39903         }
39904     },
39905
39906     /**
39907      * Selects a cell.
39908      * @param {Number} rowIndex
39909      * @param {Number} collIndex
39910      */
39911     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39912         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39913             this.clearSelections();
39914             r = r || this.grid.dataSource.getAt(rowIndex);
39915             this.selection = {
39916                 record : r,
39917                 cell : [rowIndex, colIndex]
39918             };
39919             if(!preventViewNotify){
39920                 var v = this.grid.getView();
39921                 v.onCellSelect(rowIndex, colIndex);
39922                 if(preventFocus !== true){
39923                     v.focusCell(rowIndex, colIndex);
39924                 }
39925             }
39926             this.fireEvent("cellselect", this, rowIndex, colIndex);
39927             this.fireEvent("selectionchange", this, this.selection);
39928         }
39929     },
39930
39931         //private
39932     isSelectable : function(rowIndex, colIndex, cm){
39933         return !cm.isHidden(colIndex);
39934     },
39935
39936     /** @ignore */
39937     handleKeyDown : function(e){
39938         //Roo.log('Cell Sel Model handleKeyDown');
39939         if(!e.isNavKeyPress()){
39940             return;
39941         }
39942         var g = this.grid, s = this.selection;
39943         if(!s){
39944             e.stopEvent();
39945             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39946             if(cell){
39947                 this.select(cell[0], cell[1]);
39948             }
39949             return;
39950         }
39951         var sm = this;
39952         var walk = function(row, col, step){
39953             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39954         };
39955         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39956         var newCell;
39957
39958       
39959
39960         switch(k){
39961             case e.TAB:
39962                 // handled by onEditorKey
39963                 if (g.isEditor && g.editing) {
39964                     return;
39965                 }
39966                 if(e.shiftKey) {
39967                     newCell = walk(r, c-1, -1);
39968                 } else {
39969                     newCell = walk(r, c+1, 1);
39970                 }
39971                 break;
39972             
39973             case e.DOWN:
39974                newCell = walk(r+1, c, 1);
39975                 break;
39976             
39977             case e.UP:
39978                 newCell = walk(r-1, c, -1);
39979                 break;
39980             
39981             case e.RIGHT:
39982                 newCell = walk(r, c+1, 1);
39983                 break;
39984             
39985             case e.LEFT:
39986                 newCell = walk(r, c-1, -1);
39987                 break;
39988             
39989             case e.ENTER:
39990                 
39991                 if(g.isEditor && !g.editing){
39992                    g.startEditing(r, c);
39993                    e.stopEvent();
39994                    return;
39995                 }
39996                 
39997                 
39998              break;
39999         };
40000         if(newCell){
40001             this.select(newCell[0], newCell[1]);
40002             e.stopEvent();
40003             
40004         }
40005     },
40006
40007     acceptsNav : function(row, col, cm){
40008         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40009     },
40010     /**
40011      * Selects a cell.
40012      * @param {Number} field (not used) - as it's normally used as a listener
40013      * @param {Number} e - event - fake it by using
40014      *
40015      * var e = Roo.EventObjectImpl.prototype;
40016      * e.keyCode = e.TAB
40017      *
40018      * 
40019      */
40020     onEditorKey : function(field, e){
40021         
40022         var k = e.getKey(),
40023             newCell,
40024             g = this.grid,
40025             ed = g.activeEditor,
40026             forward = false;
40027         ///Roo.log('onEditorKey' + k);
40028         
40029         
40030         if (this.enter_is_tab && k == e.ENTER) {
40031             k = e.TAB;
40032         }
40033         
40034         if(k == e.TAB){
40035             if(e.shiftKey){
40036                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40037             }else{
40038                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40039                 forward = true;
40040             }
40041             
40042             e.stopEvent();
40043             
40044         } else if(k == e.ENTER &&  !e.ctrlKey){
40045             ed.completeEdit();
40046             e.stopEvent();
40047             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40048         
40049                 } else if(k == e.ESC){
40050             ed.cancelEdit();
40051         }
40052                 
40053         if (newCell) {
40054             var ecall = { cell : newCell, forward : forward };
40055             this.fireEvent('beforeeditnext', ecall );
40056             newCell = ecall.cell;
40057                         forward = ecall.forward;
40058         }
40059                 
40060         if(newCell){
40061             //Roo.log('next cell after edit');
40062             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40063         } else if (forward) {
40064             // tabbed past last
40065             this.fireEvent.defer(100, this, ['tabend',this]);
40066         }
40067     }
40068 });/*
40069  * Based on:
40070  * Ext JS Library 1.1.1
40071  * Copyright(c) 2006-2007, Ext JS, LLC.
40072  *
40073  * Originally Released Under LGPL - original licence link has changed is not relivant.
40074  *
40075  * Fork - LGPL
40076  * <script type="text/javascript">
40077  */
40078  
40079 /**
40080  * @class Roo.grid.EditorGrid
40081  * @extends Roo.grid.Grid
40082  * Class for creating and editable grid.
40083  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40084  * The container MUST have some type of size defined for the grid to fill. The container will be 
40085  * automatically set to position relative if it isn't already.
40086  * @param {Object} dataSource The data model to bind to
40087  * @param {Object} colModel The column model with info about this grid's columns
40088  */
40089 Roo.grid.EditorGrid = function(container, config){
40090     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40091     this.getGridEl().addClass("xedit-grid");
40092
40093     if(!this.selModel){
40094         this.selModel = new Roo.grid.CellSelectionModel();
40095     }
40096
40097     this.activeEditor = null;
40098
40099         this.addEvents({
40100             /**
40101              * @event beforeedit
40102              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40103              * <ul style="padding:5px;padding-left:16px;">
40104              * <li>grid - This grid</li>
40105              * <li>record - The record being edited</li>
40106              * <li>field - The field name being edited</li>
40107              * <li>value - The value for the field being edited.</li>
40108              * <li>row - The grid row index</li>
40109              * <li>column - The grid column index</li>
40110              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40111              * </ul>
40112              * @param {Object} e An edit event (see above for description)
40113              */
40114             "beforeedit" : true,
40115             /**
40116              * @event afteredit
40117              * Fires after a cell is edited. <br />
40118              * <ul style="padding:5px;padding-left:16px;">
40119              * <li>grid - This grid</li>
40120              * <li>record - The record being edited</li>
40121              * <li>field - The field name being edited</li>
40122              * <li>value - The value being set</li>
40123              * <li>originalValue - The original value for the field, before the edit.</li>
40124              * <li>row - The grid row index</li>
40125              * <li>column - The grid column index</li>
40126              * </ul>
40127              * @param {Object} e An edit event (see above for description)
40128              */
40129             "afteredit" : true,
40130             /**
40131              * @event validateedit
40132              * Fires after a cell is edited, but before the value is set in the record. 
40133          * You can use this to modify the value being set in the field, Return false
40134              * to cancel the change. The edit event object has the following properties <br />
40135              * <ul style="padding:5px;padding-left:16px;">
40136          * <li>editor - This editor</li>
40137              * <li>grid - This grid</li>
40138              * <li>record - The record being edited</li>
40139              * <li>field - The field name being edited</li>
40140              * <li>value - The value being set</li>
40141              * <li>originalValue - The original value for the field, before the edit.</li>
40142              * <li>row - The grid row index</li>
40143              * <li>column - The grid column index</li>
40144              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40145              * </ul>
40146              * @param {Object} e An edit event (see above for description)
40147              */
40148             "validateedit" : true
40149         });
40150     this.on("bodyscroll", this.stopEditing,  this);
40151     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40152 };
40153
40154 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40155     /**
40156      * @cfg {Number} clicksToEdit
40157      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40158      */
40159     clicksToEdit: 2,
40160
40161     // private
40162     isEditor : true,
40163     // private
40164     trackMouseOver: false, // causes very odd FF errors
40165
40166     onCellDblClick : function(g, row, col){
40167         this.startEditing(row, col);
40168     },
40169
40170     onEditComplete : function(ed, value, startValue){
40171         this.editing = false;
40172         this.activeEditor = null;
40173         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40174         var r = ed.record;
40175         var field = this.colModel.getDataIndex(ed.col);
40176         var e = {
40177             grid: this,
40178             record: r,
40179             field: field,
40180             originalValue: startValue,
40181             value: value,
40182             row: ed.row,
40183             column: ed.col,
40184             cancel:false,
40185             editor: ed
40186         };
40187         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
40188         cell.show();
40189           
40190         if(String(value) !== String(startValue)){
40191             
40192             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40193                 r.set(field, e.value);
40194                 // if we are dealing with a combo box..
40195                 // then we also set the 'name' colum to be the displayField
40196                 if (ed.field.displayField && ed.field.name) {
40197                     r.set(ed.field.name, ed.field.el.dom.value);
40198                 }
40199                 
40200                 delete e.cancel; //?? why!!!
40201                 this.fireEvent("afteredit", e);
40202             }
40203         } else {
40204             this.fireEvent("afteredit", e); // always fire it!
40205         }
40206         this.view.focusCell(ed.row, ed.col);
40207     },
40208
40209     /**
40210      * Starts editing the specified for the specified row/column
40211      * @param {Number} rowIndex
40212      * @param {Number} colIndex
40213      */
40214     startEditing : function(row, col){
40215         this.stopEditing();
40216         if(this.colModel.isCellEditable(col, row)){
40217             this.view.ensureVisible(row, col, true);
40218           
40219             var r = this.dataSource.getAt(row);
40220             var field = this.colModel.getDataIndex(col);
40221             var cell = Roo.get(this.view.getCell(row,col));
40222             var e = {
40223                 grid: this,
40224                 record: r,
40225                 field: field,
40226                 value: r.data[field],
40227                 row: row,
40228                 column: col,
40229                 cancel:false 
40230             };
40231             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40232                 this.editing = true;
40233                 var ed = this.colModel.getCellEditor(col, row);
40234                 
40235                 if (!ed) {
40236                     return;
40237                 }
40238                 if(!ed.rendered){
40239                     ed.render(ed.parentEl || document.body);
40240                 }
40241                 ed.field.reset();
40242                
40243                 cell.hide();
40244                 
40245                 (function(){ // complex but required for focus issues in safari, ie and opera
40246                     ed.row = row;
40247                     ed.col = col;
40248                     ed.record = r;
40249                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40250                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40251                     this.activeEditor = ed;
40252                     var v = r.data[field];
40253                     ed.startEdit(this.view.getCell(row, col), v);
40254                     // combo's with 'displayField and name set
40255                     if (ed.field.displayField && ed.field.name) {
40256                         ed.field.el.dom.value = r.data[ed.field.name];
40257                     }
40258                     
40259                     
40260                 }).defer(50, this);
40261             }
40262         }
40263     },
40264         
40265     /**
40266      * Stops any active editing
40267      */
40268     stopEditing : function(){
40269         if(this.activeEditor){
40270             this.activeEditor.completeEdit();
40271         }
40272         this.activeEditor = null;
40273     },
40274         
40275          /**
40276      * Called to get grid's drag proxy text, by default returns this.ddText.
40277      * @return {String}
40278      */
40279     getDragDropText : function(){
40280         var count = this.selModel.getSelectedCell() ? 1 : 0;
40281         return String.format(this.ddText, count, count == 1 ? '' : 's');
40282     }
40283         
40284 });/*
40285  * Based on:
40286  * Ext JS Library 1.1.1
40287  * Copyright(c) 2006-2007, Ext JS, LLC.
40288  *
40289  * Originally Released Under LGPL - original licence link has changed is not relivant.
40290  *
40291  * Fork - LGPL
40292  * <script type="text/javascript">
40293  */
40294
40295 // private - not really -- you end up using it !
40296 // This is a support class used internally by the Grid components
40297
40298 /**
40299  * @class Roo.grid.GridEditor
40300  * @extends Roo.Editor
40301  * Class for creating and editable grid elements.
40302  * @param {Object} config any settings (must include field)
40303  */
40304 Roo.grid.GridEditor = function(field, config){
40305     if (!config && field.field) {
40306         config = field;
40307         field = Roo.factory(config.field, Roo.form);
40308     }
40309     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40310     field.monitorTab = false;
40311 };
40312
40313 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40314     
40315     /**
40316      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40317      */
40318     
40319     alignment: "tl-tl",
40320     autoSize: "width",
40321     hideEl : false,
40322     cls: "x-small-editor x-grid-editor",
40323     shim:false,
40324     shadow:"frame"
40325 });/*
40326  * Based on:
40327  * Ext JS Library 1.1.1
40328  * Copyright(c) 2006-2007, Ext JS, LLC.
40329  *
40330  * Originally Released Under LGPL - original licence link has changed is not relivant.
40331  *
40332  * Fork - LGPL
40333  * <script type="text/javascript">
40334  */
40335   
40336
40337   
40338 Roo.grid.PropertyRecord = Roo.data.Record.create([
40339     {name:'name',type:'string'},  'value'
40340 ]);
40341
40342
40343 Roo.grid.PropertyStore = function(grid, source){
40344     this.grid = grid;
40345     this.store = new Roo.data.Store({
40346         recordType : Roo.grid.PropertyRecord
40347     });
40348     this.store.on('update', this.onUpdate,  this);
40349     if(source){
40350         this.setSource(source);
40351     }
40352     Roo.grid.PropertyStore.superclass.constructor.call(this);
40353 };
40354
40355
40356
40357 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40358     setSource : function(o){
40359         this.source = o;
40360         this.store.removeAll();
40361         var data = [];
40362         for(var k in o){
40363             if(this.isEditableValue(o[k])){
40364                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40365             }
40366         }
40367         this.store.loadRecords({records: data}, {}, true);
40368     },
40369
40370     onUpdate : function(ds, record, type){
40371         if(type == Roo.data.Record.EDIT){
40372             var v = record.data['value'];
40373             var oldValue = record.modified['value'];
40374             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40375                 this.source[record.id] = v;
40376                 record.commit();
40377                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40378             }else{
40379                 record.reject();
40380             }
40381         }
40382     },
40383
40384     getProperty : function(row){
40385        return this.store.getAt(row);
40386     },
40387
40388     isEditableValue: function(val){
40389         if(val && val instanceof Date){
40390             return true;
40391         }else if(typeof val == 'object' || typeof val == 'function'){
40392             return false;
40393         }
40394         return true;
40395     },
40396
40397     setValue : function(prop, value){
40398         this.source[prop] = value;
40399         this.store.getById(prop).set('value', value);
40400     },
40401
40402     getSource : function(){
40403         return this.source;
40404     }
40405 });
40406
40407 Roo.grid.PropertyColumnModel = function(grid, store){
40408     this.grid = grid;
40409     var g = Roo.grid;
40410     g.PropertyColumnModel.superclass.constructor.call(this, [
40411         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40412         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40413     ]);
40414     this.store = store;
40415     this.bselect = Roo.DomHelper.append(document.body, {
40416         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40417             {tag: 'option', value: 'true', html: 'true'},
40418             {tag: 'option', value: 'false', html: 'false'}
40419         ]
40420     });
40421     Roo.id(this.bselect);
40422     var f = Roo.form;
40423     this.editors = {
40424         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40425         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40426         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40427         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40428         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40429     };
40430     this.renderCellDelegate = this.renderCell.createDelegate(this);
40431     this.renderPropDelegate = this.renderProp.createDelegate(this);
40432 };
40433
40434 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40435     
40436     
40437     nameText : 'Name',
40438     valueText : 'Value',
40439     
40440     dateFormat : 'm/j/Y',
40441     
40442     
40443     renderDate : function(dateVal){
40444         return dateVal.dateFormat(this.dateFormat);
40445     },
40446
40447     renderBool : function(bVal){
40448         return bVal ? 'true' : 'false';
40449     },
40450
40451     isCellEditable : function(colIndex, rowIndex){
40452         return colIndex == 1;
40453     },
40454
40455     getRenderer : function(col){
40456         return col == 1 ?
40457             this.renderCellDelegate : this.renderPropDelegate;
40458     },
40459
40460     renderProp : function(v){
40461         return this.getPropertyName(v);
40462     },
40463
40464     renderCell : function(val){
40465         var rv = val;
40466         if(val instanceof Date){
40467             rv = this.renderDate(val);
40468         }else if(typeof val == 'boolean'){
40469             rv = this.renderBool(val);
40470         }
40471         return Roo.util.Format.htmlEncode(rv);
40472     },
40473
40474     getPropertyName : function(name){
40475         var pn = this.grid.propertyNames;
40476         return pn && pn[name] ? pn[name] : name;
40477     },
40478
40479     getCellEditor : function(colIndex, rowIndex){
40480         var p = this.store.getProperty(rowIndex);
40481         var n = p.data['name'], val = p.data['value'];
40482         
40483         if(typeof(this.grid.customEditors[n]) == 'string'){
40484             return this.editors[this.grid.customEditors[n]];
40485         }
40486         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40487             return this.grid.customEditors[n];
40488         }
40489         if(val instanceof Date){
40490             return this.editors['date'];
40491         }else if(typeof val == 'number'){
40492             return this.editors['number'];
40493         }else if(typeof val == 'boolean'){
40494             return this.editors['boolean'];
40495         }else{
40496             return this.editors['string'];
40497         }
40498     }
40499 });
40500
40501 /**
40502  * @class Roo.grid.PropertyGrid
40503  * @extends Roo.grid.EditorGrid
40504  * This class represents the  interface of a component based property grid control.
40505  * <br><br>Usage:<pre><code>
40506  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40507       
40508  });
40509  // set any options
40510  grid.render();
40511  * </code></pre>
40512   
40513  * @constructor
40514  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40515  * The container MUST have some type of size defined for the grid to fill. The container will be
40516  * automatically set to position relative if it isn't already.
40517  * @param {Object} config A config object that sets properties on this grid.
40518  */
40519 Roo.grid.PropertyGrid = function(container, config){
40520     config = config || {};
40521     var store = new Roo.grid.PropertyStore(this);
40522     this.store = store;
40523     var cm = new Roo.grid.PropertyColumnModel(this, store);
40524     store.store.sort('name', 'ASC');
40525     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40526         ds: store.store,
40527         cm: cm,
40528         enableColLock:false,
40529         enableColumnMove:false,
40530         stripeRows:false,
40531         trackMouseOver: false,
40532         clicksToEdit:1
40533     }, config));
40534     this.getGridEl().addClass('x-props-grid');
40535     this.lastEditRow = null;
40536     this.on('columnresize', this.onColumnResize, this);
40537     this.addEvents({
40538          /**
40539              * @event beforepropertychange
40540              * Fires before a property changes (return false to stop?)
40541              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40542              * @param {String} id Record Id
40543              * @param {String} newval New Value
40544          * @param {String} oldval Old Value
40545              */
40546         "beforepropertychange": true,
40547         /**
40548              * @event propertychange
40549              * Fires after a property changes
40550              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40551              * @param {String} id Record Id
40552              * @param {String} newval New Value
40553          * @param {String} oldval Old Value
40554              */
40555         "propertychange": true
40556     });
40557     this.customEditors = this.customEditors || {};
40558 };
40559 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40560     
40561      /**
40562      * @cfg {Object} customEditors map of colnames=> custom editors.
40563      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40564      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40565      * false disables editing of the field.
40566          */
40567     
40568       /**
40569      * @cfg {Object} propertyNames map of property Names to their displayed value
40570          */
40571     
40572     render : function(){
40573         Roo.grid.PropertyGrid.superclass.render.call(this);
40574         this.autoSize.defer(100, this);
40575     },
40576
40577     autoSize : function(){
40578         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40579         if(this.view){
40580             this.view.fitColumns();
40581         }
40582     },
40583
40584     onColumnResize : function(){
40585         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40586         this.autoSize();
40587     },
40588     /**
40589      * Sets the data for the Grid
40590      * accepts a Key => Value object of all the elements avaiable.
40591      * @param {Object} data  to appear in grid.
40592      */
40593     setSource : function(source){
40594         this.store.setSource(source);
40595         //this.autoSize();
40596     },
40597     /**
40598      * Gets all the data from the grid.
40599      * @return {Object} data  data stored in grid
40600      */
40601     getSource : function(){
40602         return this.store.getSource();
40603     }
40604 });/*
40605   
40606  * Licence LGPL
40607  
40608  */
40609  
40610 /**
40611  * @class Roo.grid.Calendar
40612  * @extends Roo.util.Grid
40613  * This class extends the Grid to provide a calendar widget
40614  * <br><br>Usage:<pre><code>
40615  var grid = new Roo.grid.Calendar("my-container-id", {
40616      ds: myDataStore,
40617      cm: myColModel,
40618      selModel: mySelectionModel,
40619      autoSizeColumns: true,
40620      monitorWindowResize: false,
40621      trackMouseOver: true
40622      eventstore : real data store..
40623  });
40624  // set any options
40625  grid.render();
40626   
40627   * @constructor
40628  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40629  * The container MUST have some type of size defined for the grid to fill. The container will be
40630  * automatically set to position relative if it isn't already.
40631  * @param {Object} config A config object that sets properties on this grid.
40632  */
40633 Roo.grid.Calendar = function(container, config){
40634         // initialize the container
40635         this.container = Roo.get(container);
40636         this.container.update("");
40637         this.container.setStyle("overflow", "hidden");
40638     this.container.addClass('x-grid-container');
40639
40640     this.id = this.container.id;
40641
40642     Roo.apply(this, config);
40643     // check and correct shorthanded configs
40644     
40645     var rows = [];
40646     var d =1;
40647     for (var r = 0;r < 6;r++) {
40648         
40649         rows[r]=[];
40650         for (var c =0;c < 7;c++) {
40651             rows[r][c]= '';
40652         }
40653     }
40654     if (this.eventStore) {
40655         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40656         this.eventStore.on('load',this.onLoad, this);
40657         this.eventStore.on('beforeload',this.clearEvents, this);
40658          
40659     }
40660     
40661     this.dataSource = new Roo.data.Store({
40662             proxy: new Roo.data.MemoryProxy(rows),
40663             reader: new Roo.data.ArrayReader({}, [
40664                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40665     });
40666
40667     this.dataSource.load();
40668     this.ds = this.dataSource;
40669     this.ds.xmodule = this.xmodule || false;
40670     
40671     
40672     var cellRender = function(v,x,r)
40673     {
40674         return String.format(
40675             '<div class="fc-day  fc-widget-content"><div>' +
40676                 '<div class="fc-event-container"></div>' +
40677                 '<div class="fc-day-number">{0}</div>'+
40678                 
40679                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40680             '</div></div>', v);
40681     
40682     }
40683     
40684     
40685     this.colModel = new Roo.grid.ColumnModel( [
40686         {
40687             xtype: 'ColumnModel',
40688             xns: Roo.grid,
40689             dataIndex : 'weekday0',
40690             header : 'Sunday',
40691             renderer : cellRender
40692         },
40693         {
40694             xtype: 'ColumnModel',
40695             xns: Roo.grid,
40696             dataIndex : 'weekday1',
40697             header : 'Monday',
40698             renderer : cellRender
40699         },
40700         {
40701             xtype: 'ColumnModel',
40702             xns: Roo.grid,
40703             dataIndex : 'weekday2',
40704             header : 'Tuesday',
40705             renderer : cellRender
40706         },
40707         {
40708             xtype: 'ColumnModel',
40709             xns: Roo.grid,
40710             dataIndex : 'weekday3',
40711             header : 'Wednesday',
40712             renderer : cellRender
40713         },
40714         {
40715             xtype: 'ColumnModel',
40716             xns: Roo.grid,
40717             dataIndex : 'weekday4',
40718             header : 'Thursday',
40719             renderer : cellRender
40720         },
40721         {
40722             xtype: 'ColumnModel',
40723             xns: Roo.grid,
40724             dataIndex : 'weekday5',
40725             header : 'Friday',
40726             renderer : cellRender
40727         },
40728         {
40729             xtype: 'ColumnModel',
40730             xns: Roo.grid,
40731             dataIndex : 'weekday6',
40732             header : 'Saturday',
40733             renderer : cellRender
40734         }
40735     ]);
40736     this.cm = this.colModel;
40737     this.cm.xmodule = this.xmodule || false;
40738  
40739         
40740           
40741     //this.selModel = new Roo.grid.CellSelectionModel();
40742     //this.sm = this.selModel;
40743     //this.selModel.init(this);
40744     
40745     
40746     if(this.width){
40747         this.container.setWidth(this.width);
40748     }
40749
40750     if(this.height){
40751         this.container.setHeight(this.height);
40752     }
40753     /** @private */
40754         this.addEvents({
40755         // raw events
40756         /**
40757          * @event click
40758          * The raw click event for the entire grid.
40759          * @param {Roo.EventObject} e
40760          */
40761         "click" : true,
40762         /**
40763          * @event dblclick
40764          * The raw dblclick event for the entire grid.
40765          * @param {Roo.EventObject} e
40766          */
40767         "dblclick" : true,
40768         /**
40769          * @event contextmenu
40770          * The raw contextmenu event for the entire grid.
40771          * @param {Roo.EventObject} e
40772          */
40773         "contextmenu" : true,
40774         /**
40775          * @event mousedown
40776          * The raw mousedown event for the entire grid.
40777          * @param {Roo.EventObject} e
40778          */
40779         "mousedown" : true,
40780         /**
40781          * @event mouseup
40782          * The raw mouseup event for the entire grid.
40783          * @param {Roo.EventObject} e
40784          */
40785         "mouseup" : true,
40786         /**
40787          * @event mouseover
40788          * The raw mouseover event for the entire grid.
40789          * @param {Roo.EventObject} e
40790          */
40791         "mouseover" : true,
40792         /**
40793          * @event mouseout
40794          * The raw mouseout event for the entire grid.
40795          * @param {Roo.EventObject} e
40796          */
40797         "mouseout" : true,
40798         /**
40799          * @event keypress
40800          * The raw keypress event for the entire grid.
40801          * @param {Roo.EventObject} e
40802          */
40803         "keypress" : true,
40804         /**
40805          * @event keydown
40806          * The raw keydown event for the entire grid.
40807          * @param {Roo.EventObject} e
40808          */
40809         "keydown" : true,
40810
40811         // custom events
40812
40813         /**
40814          * @event cellclick
40815          * Fires when a cell is clicked
40816          * @param {Grid} this
40817          * @param {Number} rowIndex
40818          * @param {Number} columnIndex
40819          * @param {Roo.EventObject} e
40820          */
40821         "cellclick" : true,
40822         /**
40823          * @event celldblclick
40824          * Fires when a cell is double clicked
40825          * @param {Grid} this
40826          * @param {Number} rowIndex
40827          * @param {Number} columnIndex
40828          * @param {Roo.EventObject} e
40829          */
40830         "celldblclick" : true,
40831         /**
40832          * @event rowclick
40833          * Fires when a row is clicked
40834          * @param {Grid} this
40835          * @param {Number} rowIndex
40836          * @param {Roo.EventObject} e
40837          */
40838         "rowclick" : true,
40839         /**
40840          * @event rowdblclick
40841          * Fires when a row is double clicked
40842          * @param {Grid} this
40843          * @param {Number} rowIndex
40844          * @param {Roo.EventObject} e
40845          */
40846         "rowdblclick" : true,
40847         /**
40848          * @event headerclick
40849          * Fires when a header is clicked
40850          * @param {Grid} this
40851          * @param {Number} columnIndex
40852          * @param {Roo.EventObject} e
40853          */
40854         "headerclick" : true,
40855         /**
40856          * @event headerdblclick
40857          * Fires when a header cell is double clicked
40858          * @param {Grid} this
40859          * @param {Number} columnIndex
40860          * @param {Roo.EventObject} e
40861          */
40862         "headerdblclick" : true,
40863         /**
40864          * @event rowcontextmenu
40865          * Fires when a row is right clicked
40866          * @param {Grid} this
40867          * @param {Number} rowIndex
40868          * @param {Roo.EventObject} e
40869          */
40870         "rowcontextmenu" : true,
40871         /**
40872          * @event cellcontextmenu
40873          * Fires when a cell is right clicked
40874          * @param {Grid} this
40875          * @param {Number} rowIndex
40876          * @param {Number} cellIndex
40877          * @param {Roo.EventObject} e
40878          */
40879          "cellcontextmenu" : true,
40880         /**
40881          * @event headercontextmenu
40882          * Fires when a header is right clicked
40883          * @param {Grid} this
40884          * @param {Number} columnIndex
40885          * @param {Roo.EventObject} e
40886          */
40887         "headercontextmenu" : true,
40888         /**
40889          * @event bodyscroll
40890          * Fires when the body element is scrolled
40891          * @param {Number} scrollLeft
40892          * @param {Number} scrollTop
40893          */
40894         "bodyscroll" : true,
40895         /**
40896          * @event columnresize
40897          * Fires when the user resizes a column
40898          * @param {Number} columnIndex
40899          * @param {Number} newSize
40900          */
40901         "columnresize" : true,
40902         /**
40903          * @event columnmove
40904          * Fires when the user moves a column
40905          * @param {Number} oldIndex
40906          * @param {Number} newIndex
40907          */
40908         "columnmove" : true,
40909         /**
40910          * @event startdrag
40911          * Fires when row(s) start being dragged
40912          * @param {Grid} this
40913          * @param {Roo.GridDD} dd The drag drop object
40914          * @param {event} e The raw browser event
40915          */
40916         "startdrag" : true,
40917         /**
40918          * @event enddrag
40919          * Fires when a drag operation is complete
40920          * @param {Grid} this
40921          * @param {Roo.GridDD} dd The drag drop object
40922          * @param {event} e The raw browser event
40923          */
40924         "enddrag" : true,
40925         /**
40926          * @event dragdrop
40927          * Fires when dragged row(s) are dropped on a valid DD target
40928          * @param {Grid} this
40929          * @param {Roo.GridDD} dd The drag drop object
40930          * @param {String} targetId The target drag drop object
40931          * @param {event} e The raw browser event
40932          */
40933         "dragdrop" : true,
40934         /**
40935          * @event dragover
40936          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40937          * @param {Grid} this
40938          * @param {Roo.GridDD} dd The drag drop object
40939          * @param {String} targetId The target drag drop object
40940          * @param {event} e The raw browser event
40941          */
40942         "dragover" : true,
40943         /**
40944          * @event dragenter
40945          *  Fires when the dragged row(s) first cross another DD target while being dragged
40946          * @param {Grid} this
40947          * @param {Roo.GridDD} dd The drag drop object
40948          * @param {String} targetId The target drag drop object
40949          * @param {event} e The raw browser event
40950          */
40951         "dragenter" : true,
40952         /**
40953          * @event dragout
40954          * Fires when the dragged row(s) leave another DD target while being dragged
40955          * @param {Grid} this
40956          * @param {Roo.GridDD} dd The drag drop object
40957          * @param {String} targetId The target drag drop object
40958          * @param {event} e The raw browser event
40959          */
40960         "dragout" : true,
40961         /**
40962          * @event rowclass
40963          * Fires when a row is rendered, so you can change add a style to it.
40964          * @param {GridView} gridview   The grid view
40965          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40966          */
40967         'rowclass' : true,
40968
40969         /**
40970          * @event render
40971          * Fires when the grid is rendered
40972          * @param {Grid} grid
40973          */
40974         'render' : true,
40975             /**
40976              * @event select
40977              * Fires when a date is selected
40978              * @param {DatePicker} this
40979              * @param {Date} date The selected date
40980              */
40981         'select': true,
40982         /**
40983              * @event monthchange
40984              * Fires when the displayed month changes 
40985              * @param {DatePicker} this
40986              * @param {Date} date The selected month
40987              */
40988         'monthchange': true,
40989         /**
40990              * @event evententer
40991              * Fires when mouse over an event
40992              * @param {Calendar} this
40993              * @param {event} Event
40994              */
40995         'evententer': true,
40996         /**
40997              * @event eventleave
40998              * Fires when the mouse leaves an
40999              * @param {Calendar} this
41000              * @param {event}
41001              */
41002         'eventleave': true,
41003         /**
41004              * @event eventclick
41005              * Fires when the mouse click an
41006              * @param {Calendar} this
41007              * @param {event}
41008              */
41009         'eventclick': true,
41010         /**
41011              * @event eventrender
41012              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41013              * @param {Calendar} this
41014              * @param {data} data to be modified
41015              */
41016         'eventrender': true
41017         
41018     });
41019
41020     Roo.grid.Grid.superclass.constructor.call(this);
41021     this.on('render', function() {
41022         this.view.el.addClass('x-grid-cal'); 
41023         
41024         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41025
41026     },this);
41027     
41028     if (!Roo.grid.Calendar.style) {
41029         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41030             
41031             
41032             '.x-grid-cal .x-grid-col' :  {
41033                 height: 'auto !important',
41034                 'vertical-align': 'top'
41035             },
41036             '.x-grid-cal  .fc-event-hori' : {
41037                 height: '14px'
41038             }
41039              
41040             
41041         }, Roo.id());
41042     }
41043
41044     
41045     
41046 };
41047 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41048     /**
41049      * @cfg {Store} eventStore The store that loads events.
41050      */
41051     eventStore : 25,
41052
41053      
41054     activeDate : false,
41055     startDay : 0,
41056     autoWidth : true,
41057     monitorWindowResize : false,
41058
41059     
41060     resizeColumns : function() {
41061         var col = (this.view.el.getWidth() / 7) - 3;
41062         // loop through cols, and setWidth
41063         for(var i =0 ; i < 7 ; i++){
41064             this.cm.setColumnWidth(i, col);
41065         }
41066     },
41067      setDate :function(date) {
41068         
41069         Roo.log('setDate?');
41070         
41071         this.resizeColumns();
41072         var vd = this.activeDate;
41073         this.activeDate = date;
41074 //        if(vd && this.el){
41075 //            var t = date.getTime();
41076 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41077 //                Roo.log('using add remove');
41078 //                
41079 //                this.fireEvent('monthchange', this, date);
41080 //                
41081 //                this.cells.removeClass("fc-state-highlight");
41082 //                this.cells.each(function(c){
41083 //                   if(c.dateValue == t){
41084 //                       c.addClass("fc-state-highlight");
41085 //                       setTimeout(function(){
41086 //                            try{c.dom.firstChild.focus();}catch(e){}
41087 //                       }, 50);
41088 //                       return false;
41089 //                   }
41090 //                   return true;
41091 //                });
41092 //                return;
41093 //            }
41094 //        }
41095         
41096         var days = date.getDaysInMonth();
41097         
41098         var firstOfMonth = date.getFirstDateOfMonth();
41099         var startingPos = firstOfMonth.getDay()-this.startDay;
41100         
41101         if(startingPos < this.startDay){
41102             startingPos += 7;
41103         }
41104         
41105         var pm = date.add(Date.MONTH, -1);
41106         var prevStart = pm.getDaysInMonth()-startingPos;
41107 //        
41108         
41109         
41110         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41111         
41112         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41113         //this.cells.addClassOnOver('fc-state-hover');
41114         
41115         var cells = this.cells.elements;
41116         var textEls = this.textNodes;
41117         
41118         //Roo.each(cells, function(cell){
41119         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41120         //});
41121         
41122         days += startingPos;
41123
41124         // convert everything to numbers so it's fast
41125         var day = 86400000;
41126         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41127         //Roo.log(d);
41128         //Roo.log(pm);
41129         //Roo.log(prevStart);
41130         
41131         var today = new Date().clearTime().getTime();
41132         var sel = date.clearTime().getTime();
41133         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41134         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41135         var ddMatch = this.disabledDatesRE;
41136         var ddText = this.disabledDatesText;
41137         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41138         var ddaysText = this.disabledDaysText;
41139         var format = this.format;
41140         
41141         var setCellClass = function(cal, cell){
41142             
41143             //Roo.log('set Cell Class');
41144             cell.title = "";
41145             var t = d.getTime();
41146             
41147             //Roo.log(d);
41148             
41149             
41150             cell.dateValue = t;
41151             if(t == today){
41152                 cell.className += " fc-today";
41153                 cell.className += " fc-state-highlight";
41154                 cell.title = cal.todayText;
41155             }
41156             if(t == sel){
41157                 // disable highlight in other month..
41158                 cell.className += " fc-state-highlight";
41159                 
41160             }
41161             // disabling
41162             if(t < min) {
41163                 //cell.className = " fc-state-disabled";
41164                 cell.title = cal.minText;
41165                 return;
41166             }
41167             if(t > max) {
41168                 //cell.className = " fc-state-disabled";
41169                 cell.title = cal.maxText;
41170                 return;
41171             }
41172             if(ddays){
41173                 if(ddays.indexOf(d.getDay()) != -1){
41174                     // cell.title = ddaysText;
41175                    // cell.className = " fc-state-disabled";
41176                 }
41177             }
41178             if(ddMatch && format){
41179                 var fvalue = d.dateFormat(format);
41180                 if(ddMatch.test(fvalue)){
41181                     cell.title = ddText.replace("%0", fvalue);
41182                    cell.className = " fc-state-disabled";
41183                 }
41184             }
41185             
41186             if (!cell.initialClassName) {
41187                 cell.initialClassName = cell.dom.className;
41188             }
41189             
41190             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41191         };
41192
41193         var i = 0;
41194         
41195         for(; i < startingPos; i++) {
41196             cells[i].dayName =  (++prevStart);
41197             Roo.log(textEls[i]);
41198             d.setDate(d.getDate()+1);
41199             
41200             //cells[i].className = "fc-past fc-other-month";
41201             setCellClass(this, cells[i]);
41202         }
41203         
41204         var intDay = 0;
41205         
41206         for(; i < days; i++){
41207             intDay = i - startingPos + 1;
41208             cells[i].dayName =  (intDay);
41209             d.setDate(d.getDate()+1);
41210             
41211             cells[i].className = ''; // "x-date-active";
41212             setCellClass(this, cells[i]);
41213         }
41214         var extraDays = 0;
41215         
41216         for(; i < 42; i++) {
41217             //textEls[i].innerHTML = (++extraDays);
41218             
41219             d.setDate(d.getDate()+1);
41220             cells[i].dayName = (++extraDays);
41221             cells[i].className = "fc-future fc-other-month";
41222             setCellClass(this, cells[i]);
41223         }
41224         
41225         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41226         
41227         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41228         
41229         // this will cause all the cells to mis
41230         var rows= [];
41231         var i =0;
41232         for (var r = 0;r < 6;r++) {
41233             for (var c =0;c < 7;c++) {
41234                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41235             }    
41236         }
41237         
41238         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41239         for(i=0;i<cells.length;i++) {
41240             
41241             this.cells.elements[i].dayName = cells[i].dayName ;
41242             this.cells.elements[i].className = cells[i].className;
41243             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41244             this.cells.elements[i].title = cells[i].title ;
41245             this.cells.elements[i].dateValue = cells[i].dateValue ;
41246         }
41247         
41248         
41249         
41250         
41251         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41252         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41253         
41254         ////if(totalRows != 6){
41255             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41256            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41257        // }
41258         
41259         this.fireEvent('monthchange', this, date);
41260         
41261         
41262     },
41263  /**
41264      * Returns the grid's SelectionModel.
41265      * @return {SelectionModel}
41266      */
41267     getSelectionModel : function(){
41268         if(!this.selModel){
41269             this.selModel = new Roo.grid.CellSelectionModel();
41270         }
41271         return this.selModel;
41272     },
41273
41274     load: function() {
41275         this.eventStore.load()
41276         
41277         
41278         
41279     },
41280     
41281     findCell : function(dt) {
41282         dt = dt.clearTime().getTime();
41283         var ret = false;
41284         this.cells.each(function(c){
41285             //Roo.log("check " +c.dateValue + '?=' + dt);
41286             if(c.dateValue == dt){
41287                 ret = c;
41288                 return false;
41289             }
41290             return true;
41291         });
41292         
41293         return ret;
41294     },
41295     
41296     findCells : function(rec) {
41297         var s = rec.data.start_dt.clone().clearTime().getTime();
41298        // Roo.log(s);
41299         var e= rec.data.end_dt.clone().clearTime().getTime();
41300        // Roo.log(e);
41301         var ret = [];
41302         this.cells.each(function(c){
41303              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41304             
41305             if(c.dateValue > e){
41306                 return ;
41307             }
41308             if(c.dateValue < s){
41309                 return ;
41310             }
41311             ret.push(c);
41312         });
41313         
41314         return ret;    
41315     },
41316     
41317     findBestRow: function(cells)
41318     {
41319         var ret = 0;
41320         
41321         for (var i =0 ; i < cells.length;i++) {
41322             ret  = Math.max(cells[i].rows || 0,ret);
41323         }
41324         return ret;
41325         
41326     },
41327     
41328     
41329     addItem : function(rec)
41330     {
41331         // look for vertical location slot in
41332         var cells = this.findCells(rec);
41333         
41334         rec.row = this.findBestRow(cells);
41335         
41336         // work out the location.
41337         
41338         var crow = false;
41339         var rows = [];
41340         for(var i =0; i < cells.length; i++) {
41341             if (!crow) {
41342                 crow = {
41343                     start : cells[i],
41344                     end :  cells[i]
41345                 };
41346                 continue;
41347             }
41348             if (crow.start.getY() == cells[i].getY()) {
41349                 // on same row.
41350                 crow.end = cells[i];
41351                 continue;
41352             }
41353             // different row.
41354             rows.push(crow);
41355             crow = {
41356                 start: cells[i],
41357                 end : cells[i]
41358             };
41359             
41360         }
41361         
41362         rows.push(crow);
41363         rec.els = [];
41364         rec.rows = rows;
41365         rec.cells = cells;
41366         for (var i = 0; i < cells.length;i++) {
41367             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41368             
41369         }
41370         
41371         
41372     },
41373     
41374     clearEvents: function() {
41375         
41376         if (!this.eventStore.getCount()) {
41377             return;
41378         }
41379         // reset number of rows in cells.
41380         Roo.each(this.cells.elements, function(c){
41381             c.rows = 0;
41382         });
41383         
41384         this.eventStore.each(function(e) {
41385             this.clearEvent(e);
41386         },this);
41387         
41388     },
41389     
41390     clearEvent : function(ev)
41391     {
41392         if (ev.els) {
41393             Roo.each(ev.els, function(el) {
41394                 el.un('mouseenter' ,this.onEventEnter, this);
41395                 el.un('mouseleave' ,this.onEventLeave, this);
41396                 el.remove();
41397             },this);
41398             ev.els = [];
41399         }
41400     },
41401     
41402     
41403     renderEvent : function(ev,ctr) {
41404         if (!ctr) {
41405              ctr = this.view.el.select('.fc-event-container',true).first();
41406         }
41407         
41408          
41409         this.clearEvent(ev);
41410             //code
41411        
41412         
41413         
41414         ev.els = [];
41415         var cells = ev.cells;
41416         var rows = ev.rows;
41417         this.fireEvent('eventrender', this, ev);
41418         
41419         for(var i =0; i < rows.length; i++) {
41420             
41421             cls = '';
41422             if (i == 0) {
41423                 cls += ' fc-event-start';
41424             }
41425             if ((i+1) == rows.length) {
41426                 cls += ' fc-event-end';
41427             }
41428             
41429             //Roo.log(ev.data);
41430             // how many rows should it span..
41431             var cg = this.eventTmpl.append(ctr,Roo.apply({
41432                 fccls : cls
41433                 
41434             }, ev.data) , true);
41435             
41436             
41437             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41438             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41439             cg.on('click', this.onEventClick, this, ev);
41440             
41441             ev.els.push(cg);
41442             
41443             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41444             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41445             //Roo.log(cg);
41446              
41447             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41448             cg.setWidth(ebox.right - sbox.x -2);
41449         }
41450     },
41451     
41452     renderEvents: function()
41453     {   
41454         // first make sure there is enough space..
41455         
41456         if (!this.eventTmpl) {
41457             this.eventTmpl = new Roo.Template(
41458                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41459                     '<div class="fc-event-inner">' +
41460                         '<span class="fc-event-time">{time}</span>' +
41461                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41462                     '</div>' +
41463                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41464                 '</div>'
41465             );
41466                 
41467         }
41468                
41469         
41470         
41471         this.cells.each(function(c) {
41472             //Roo.log(c.select('.fc-day-content div',true).first());
41473             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41474         });
41475         
41476         var ctr = this.view.el.select('.fc-event-container',true).first();
41477         
41478         var cls;
41479         this.eventStore.each(function(ev){
41480             
41481             this.renderEvent(ev);
41482              
41483              
41484         }, this);
41485         this.view.layout();
41486         
41487     },
41488     
41489     onEventEnter: function (e, el,event,d) {
41490         this.fireEvent('evententer', this, el, event);
41491     },
41492     
41493     onEventLeave: function (e, el,event,d) {
41494         this.fireEvent('eventleave', this, el, event);
41495     },
41496     
41497     onEventClick: function (e, el,event,d) {
41498         this.fireEvent('eventclick', this, el, event);
41499     },
41500     
41501     onMonthChange: function () {
41502         this.store.load();
41503     },
41504     
41505     onLoad: function () {
41506         
41507         //Roo.log('calendar onload');
41508 //         
41509         if(this.eventStore.getCount() > 0){
41510             
41511            
41512             
41513             this.eventStore.each(function(d){
41514                 
41515                 
41516                 // FIXME..
41517                 var add =   d.data;
41518                 if (typeof(add.end_dt) == 'undefined')  {
41519                     Roo.log("Missing End time in calendar data: ");
41520                     Roo.log(d);
41521                     return;
41522                 }
41523                 if (typeof(add.start_dt) == 'undefined')  {
41524                     Roo.log("Missing Start time in calendar data: ");
41525                     Roo.log(d);
41526                     return;
41527                 }
41528                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41529                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41530                 add.id = add.id || d.id;
41531                 add.title = add.title || '??';
41532                 
41533                 this.addItem(d);
41534                 
41535              
41536             },this);
41537         }
41538         
41539         this.renderEvents();
41540     }
41541     
41542
41543 });
41544 /*
41545  grid : {
41546                 xtype: 'Grid',
41547                 xns: Roo.grid,
41548                 listeners : {
41549                     render : function ()
41550                     {
41551                         _this.grid = this;
41552                         
41553                         if (!this.view.el.hasClass('course-timesheet')) {
41554                             this.view.el.addClass('course-timesheet');
41555                         }
41556                         if (this.tsStyle) {
41557                             this.ds.load({});
41558                             return; 
41559                         }
41560                         Roo.log('width');
41561                         Roo.log(_this.grid.view.el.getWidth());
41562                         
41563                         
41564                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41565                             '.course-timesheet .x-grid-row' : {
41566                                 height: '80px'
41567                             },
41568                             '.x-grid-row td' : {
41569                                 'vertical-align' : 0
41570                             },
41571                             '.course-edit-link' : {
41572                                 'color' : 'blue',
41573                                 'text-overflow' : 'ellipsis',
41574                                 'overflow' : 'hidden',
41575                                 'white-space' : 'nowrap',
41576                                 'cursor' : 'pointer'
41577                             },
41578                             '.sub-link' : {
41579                                 'color' : 'green'
41580                             },
41581                             '.de-act-sup-link' : {
41582                                 'color' : 'purple',
41583                                 'text-decoration' : 'line-through'
41584                             },
41585                             '.de-act-link' : {
41586                                 'color' : 'red',
41587                                 'text-decoration' : 'line-through'
41588                             },
41589                             '.course-timesheet .course-highlight' : {
41590                                 'border-top-style': 'dashed !important',
41591                                 'border-bottom-bottom': 'dashed !important'
41592                             },
41593                             '.course-timesheet .course-item' : {
41594                                 'font-family'   : 'tahoma, arial, helvetica',
41595                                 'font-size'     : '11px',
41596                                 'overflow'      : 'hidden',
41597                                 'padding-left'  : '10px',
41598                                 'padding-right' : '10px',
41599                                 'padding-top' : '10px' 
41600                             }
41601                             
41602                         }, Roo.id());
41603                                 this.ds.load({});
41604                     }
41605                 },
41606                 autoWidth : true,
41607                 monitorWindowResize : false,
41608                 cellrenderer : function(v,x,r)
41609                 {
41610                     return v;
41611                 },
41612                 sm : {
41613                     xtype: 'CellSelectionModel',
41614                     xns: Roo.grid
41615                 },
41616                 dataSource : {
41617                     xtype: 'Store',
41618                     xns: Roo.data,
41619                     listeners : {
41620                         beforeload : function (_self, options)
41621                         {
41622                             options.params = options.params || {};
41623                             options.params._month = _this.monthField.getValue();
41624                             options.params.limit = 9999;
41625                             options.params['sort'] = 'when_dt';    
41626                             options.params['dir'] = 'ASC';    
41627                             this.proxy.loadResponse = this.loadResponse;
41628                             Roo.log("load?");
41629                             //this.addColumns();
41630                         },
41631                         load : function (_self, records, options)
41632                         {
41633                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41634                                 // if you click on the translation.. you can edit it...
41635                                 var el = Roo.get(this);
41636                                 var id = el.dom.getAttribute('data-id');
41637                                 var d = el.dom.getAttribute('data-date');
41638                                 var t = el.dom.getAttribute('data-time');
41639                                 //var id = this.child('span').dom.textContent;
41640                                 
41641                                 //Roo.log(this);
41642                                 Pman.Dialog.CourseCalendar.show({
41643                                     id : id,
41644                                     when_d : d,
41645                                     when_t : t,
41646                                     productitem_active : id ? 1 : 0
41647                                 }, function() {
41648                                     _this.grid.ds.load({});
41649                                 });
41650                            
41651                            });
41652                            
41653                            _this.panel.fireEvent('resize', [ '', '' ]);
41654                         }
41655                     },
41656                     loadResponse : function(o, success, response){
41657                             // this is overridden on before load..
41658                             
41659                             Roo.log("our code?");       
41660                             //Roo.log(success);
41661                             //Roo.log(response)
41662                             delete this.activeRequest;
41663                             if(!success){
41664                                 this.fireEvent("loadexception", this, o, response);
41665                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41666                                 return;
41667                             }
41668                             var result;
41669                             try {
41670                                 result = o.reader.read(response);
41671                             }catch(e){
41672                                 Roo.log("load exception?");
41673                                 this.fireEvent("loadexception", this, o, response, e);
41674                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41675                                 return;
41676                             }
41677                             Roo.log("ready...");        
41678                             // loop through result.records;
41679                             // and set this.tdate[date] = [] << array of records..
41680                             _this.tdata  = {};
41681                             Roo.each(result.records, function(r){
41682                                 //Roo.log(r.data);
41683                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41684                                     _this.tdata[r.data.when_dt.format('j')] = [];
41685                                 }
41686                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41687                             });
41688                             
41689                             //Roo.log(_this.tdata);
41690                             
41691                             result.records = [];
41692                             result.totalRecords = 6;
41693                     
41694                             // let's generate some duumy records for the rows.
41695                             //var st = _this.dateField.getValue();
41696                             
41697                             // work out monday..
41698                             //st = st.add(Date.DAY, -1 * st.format('w'));
41699                             
41700                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41701                             
41702                             var firstOfMonth = date.getFirstDayOfMonth();
41703                             var days = date.getDaysInMonth();
41704                             var d = 1;
41705                             var firstAdded = false;
41706                             for (var i = 0; i < result.totalRecords ; i++) {
41707                                 //var d= st.add(Date.DAY, i);
41708                                 var row = {};
41709                                 var added = 0;
41710                                 for(var w = 0 ; w < 7 ; w++){
41711                                     if(!firstAdded && firstOfMonth != w){
41712                                         continue;
41713                                     }
41714                                     if(d > days){
41715                                         continue;
41716                                     }
41717                                     firstAdded = true;
41718                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41719                                     row['weekday'+w] = String.format(
41720                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41721                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41722                                                     d,
41723                                                     date.format('Y-m-')+dd
41724                                                 );
41725                                     added++;
41726                                     if(typeof(_this.tdata[d]) != 'undefined'){
41727                                         Roo.each(_this.tdata[d], function(r){
41728                                             var is_sub = '';
41729                                             var deactive = '';
41730                                             var id = r.id;
41731                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41732                                             if(r.parent_id*1>0){
41733                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41734                                                 id = r.parent_id;
41735                                             }
41736                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41737                                                 deactive = 'de-act-link';
41738                                             }
41739                                             
41740                                             row['weekday'+w] += String.format(
41741                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41742                                                     id, //0
41743                                                     r.product_id_name, //1
41744                                                     r.when_dt.format('h:ia'), //2
41745                                                     is_sub, //3
41746                                                     deactive, //4
41747                                                     desc // 5
41748                                             );
41749                                         });
41750                                     }
41751                                     d++;
41752                                 }
41753                                 
41754                                 // only do this if something added..
41755                                 if(added > 0){ 
41756                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41757                                 }
41758                                 
41759                                 
41760                                 // push it twice. (second one with an hour..
41761                                 
41762                             }
41763                             //Roo.log(result);
41764                             this.fireEvent("load", this, o, o.request.arg);
41765                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41766                         },
41767                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41768                     proxy : {
41769                         xtype: 'HttpProxy',
41770                         xns: Roo.data,
41771                         method : 'GET',
41772                         url : baseURL + '/Roo/Shop_course.php'
41773                     },
41774                     reader : {
41775                         xtype: 'JsonReader',
41776                         xns: Roo.data,
41777                         id : 'id',
41778                         fields : [
41779                             {
41780                                 'name': 'id',
41781                                 'type': 'int'
41782                             },
41783                             {
41784                                 'name': 'when_dt',
41785                                 'type': 'string'
41786                             },
41787                             {
41788                                 'name': 'end_dt',
41789                                 'type': 'string'
41790                             },
41791                             {
41792                                 'name': 'parent_id',
41793                                 'type': 'int'
41794                             },
41795                             {
41796                                 'name': 'product_id',
41797                                 'type': 'int'
41798                             },
41799                             {
41800                                 'name': 'productitem_id',
41801                                 'type': 'int'
41802                             },
41803                             {
41804                                 'name': 'guid',
41805                                 'type': 'int'
41806                             }
41807                         ]
41808                     }
41809                 },
41810                 toolbar : {
41811                     xtype: 'Toolbar',
41812                     xns: Roo,
41813                     items : [
41814                         {
41815                             xtype: 'Button',
41816                             xns: Roo.Toolbar,
41817                             listeners : {
41818                                 click : function (_self, e)
41819                                 {
41820                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41821                                     sd.setMonth(sd.getMonth()-1);
41822                                     _this.monthField.setValue(sd.format('Y-m-d'));
41823                                     _this.grid.ds.load({});
41824                                 }
41825                             },
41826                             text : "Back"
41827                         },
41828                         {
41829                             xtype: 'Separator',
41830                             xns: Roo.Toolbar
41831                         },
41832                         {
41833                             xtype: 'MonthField',
41834                             xns: Roo.form,
41835                             listeners : {
41836                                 render : function (_self)
41837                                 {
41838                                     _this.monthField = _self;
41839                                    // _this.monthField.set  today
41840                                 },
41841                                 select : function (combo, date)
41842                                 {
41843                                     _this.grid.ds.load({});
41844                                 }
41845                             },
41846                             value : (function() { return new Date(); })()
41847                         },
41848                         {
41849                             xtype: 'Separator',
41850                             xns: Roo.Toolbar
41851                         },
41852                         {
41853                             xtype: 'TextItem',
41854                             xns: Roo.Toolbar,
41855                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41856                         },
41857                         {
41858                             xtype: 'Fill',
41859                             xns: Roo.Toolbar
41860                         },
41861                         {
41862                             xtype: 'Button',
41863                             xns: Roo.Toolbar,
41864                             listeners : {
41865                                 click : function (_self, e)
41866                                 {
41867                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41868                                     sd.setMonth(sd.getMonth()+1);
41869                                     _this.monthField.setValue(sd.format('Y-m-d'));
41870                                     _this.grid.ds.load({});
41871                                 }
41872                             },
41873                             text : "Next"
41874                         }
41875                     ]
41876                 },
41877                  
41878             }
41879         };
41880         
41881         *//*
41882  * Based on:
41883  * Ext JS Library 1.1.1
41884  * Copyright(c) 2006-2007, Ext JS, LLC.
41885  *
41886  * Originally Released Under LGPL - original licence link has changed is not relivant.
41887  *
41888  * Fork - LGPL
41889  * <script type="text/javascript">
41890  */
41891  
41892 /**
41893  * @class Roo.LoadMask
41894  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41895  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41896  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41897  * element's UpdateManager load indicator and will be destroyed after the initial load.
41898  * @constructor
41899  * Create a new LoadMask
41900  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41901  * @param {Object} config The config object
41902  */
41903 Roo.LoadMask = function(el, config){
41904     this.el = Roo.get(el);
41905     Roo.apply(this, config);
41906     if(this.store){
41907         this.store.on('beforeload', this.onBeforeLoad, this);
41908         this.store.on('load', this.onLoad, this);
41909         this.store.on('loadexception', this.onLoadException, this);
41910         this.removeMask = false;
41911     }else{
41912         var um = this.el.getUpdateManager();
41913         um.showLoadIndicator = false; // disable the default indicator
41914         um.on('beforeupdate', this.onBeforeLoad, this);
41915         um.on('update', this.onLoad, this);
41916         um.on('failure', this.onLoad, this);
41917         this.removeMask = true;
41918     }
41919 };
41920
41921 Roo.LoadMask.prototype = {
41922     /**
41923      * @cfg {Boolean} removeMask
41924      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41925      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41926      */
41927     /**
41928      * @cfg {String} msg
41929      * The text to display in a centered loading message box (defaults to 'Loading...')
41930      */
41931     msg : 'Loading...',
41932     /**
41933      * @cfg {String} msgCls
41934      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41935      */
41936     msgCls : 'x-mask-loading',
41937
41938     /**
41939      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41940      * @type Boolean
41941      */
41942     disabled: false,
41943
41944     /**
41945      * Disables the mask to prevent it from being displayed
41946      */
41947     disable : function(){
41948        this.disabled = true;
41949     },
41950
41951     /**
41952      * Enables the mask so that it can be displayed
41953      */
41954     enable : function(){
41955         this.disabled = false;
41956     },
41957     
41958     onLoadException : function()
41959     {
41960         Roo.log(arguments);
41961         
41962         if (typeof(arguments[3]) != 'undefined') {
41963             Roo.MessageBox.alert("Error loading",arguments[3]);
41964         } 
41965         /*
41966         try {
41967             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41968                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41969             }   
41970         } catch(e) {
41971             
41972         }
41973         */
41974     
41975         
41976         
41977         this.el.unmask(this.removeMask);
41978     },
41979     // private
41980     onLoad : function()
41981     {
41982         this.el.unmask(this.removeMask);
41983     },
41984
41985     // private
41986     onBeforeLoad : function(){
41987         if(!this.disabled){
41988             this.el.mask(this.msg, this.msgCls);
41989         }
41990     },
41991
41992     // private
41993     destroy : function(){
41994         if(this.store){
41995             this.store.un('beforeload', this.onBeforeLoad, this);
41996             this.store.un('load', this.onLoad, this);
41997             this.store.un('loadexception', this.onLoadException, this);
41998         }else{
41999             var um = this.el.getUpdateManager();
42000             um.un('beforeupdate', this.onBeforeLoad, this);
42001             um.un('update', this.onLoad, this);
42002             um.un('failure', this.onLoad, this);
42003         }
42004     }
42005 };/*
42006  * Based on:
42007  * Ext JS Library 1.1.1
42008  * Copyright(c) 2006-2007, Ext JS, LLC.
42009  *
42010  * Originally Released Under LGPL - original licence link has changed is not relivant.
42011  *
42012  * Fork - LGPL
42013  * <script type="text/javascript">
42014  */
42015
42016
42017 /**
42018  * @class Roo.XTemplate
42019  * @extends Roo.Template
42020  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42021 <pre><code>
42022 var t = new Roo.XTemplate(
42023         '&lt;select name="{name}"&gt;',
42024                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42025         '&lt;/select&gt;'
42026 );
42027  
42028 // then append, applying the master template values
42029  </code></pre>
42030  *
42031  * Supported features:
42032  *
42033  *  Tags:
42034
42035 <pre><code>
42036       {a_variable} - output encoded.
42037       {a_variable.format:("Y-m-d")} - call a method on the variable
42038       {a_variable:raw} - unencoded output
42039       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42040       {a_variable:this.method_on_template(...)} - call a method on the template object.
42041  
42042 </code></pre>
42043  *  The tpl tag:
42044 <pre><code>
42045         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42046         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42047         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42048         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42049   
42050         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42051         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42052 </code></pre>
42053  *      
42054  */
42055 Roo.XTemplate = function()
42056 {
42057     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42058     if (this.html) {
42059         this.compile();
42060     }
42061 };
42062
42063
42064 Roo.extend(Roo.XTemplate, Roo.Template, {
42065
42066     /**
42067      * The various sub templates
42068      */
42069     tpls : false,
42070     /**
42071      *
42072      * basic tag replacing syntax
42073      * WORD:WORD()
42074      *
42075      * // you can fake an object call by doing this
42076      *  x.t:(test,tesT) 
42077      * 
42078      */
42079     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42080
42081     /**
42082      * compile the template
42083      *
42084      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42085      *
42086      */
42087     compile: function()
42088     {
42089         var s = this.html;
42090      
42091         s = ['<tpl>', s, '</tpl>'].join('');
42092     
42093         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42094             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42095             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42096             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42097             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42098             m,
42099             id     = 0,
42100             tpls   = [];
42101     
42102         while(true == !!(m = s.match(re))){
42103             var forMatch   = m[0].match(nameRe),
42104                 ifMatch   = m[0].match(ifRe),
42105                 execMatch   = m[0].match(execRe),
42106                 namedMatch   = m[0].match(namedRe),
42107                 
42108                 exp  = null, 
42109                 fn   = null,
42110                 exec = null,
42111                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42112                 
42113             if (ifMatch) {
42114                 // if - puts fn into test..
42115                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42116                 if(exp){
42117                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42118                 }
42119             }
42120             
42121             if (execMatch) {
42122                 // exec - calls a function... returns empty if true is  returned.
42123                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42124                 if(exp){
42125                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42126                 }
42127             }
42128             
42129             
42130             if (name) {
42131                 // for = 
42132                 switch(name){
42133                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42134                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42135                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42136                 }
42137             }
42138             var uid = namedMatch ? namedMatch[1] : id;
42139             
42140             
42141             tpls.push({
42142                 id:     namedMatch ? namedMatch[1] : id,
42143                 target: name,
42144                 exec:   exec,
42145                 test:   fn,
42146                 body:   m[1] || ''
42147             });
42148             if (namedMatch) {
42149                 s = s.replace(m[0], '');
42150             } else { 
42151                 s = s.replace(m[0], '{xtpl'+ id + '}');
42152             }
42153             ++id;
42154         }
42155         this.tpls = [];
42156         for(var i = tpls.length-1; i >= 0; --i){
42157             this.compileTpl(tpls[i]);
42158             this.tpls[tpls[i].id] = tpls[i];
42159         }
42160         this.master = tpls[tpls.length-1];
42161         return this;
42162     },
42163     /**
42164      * same as applyTemplate, except it's done to one of the subTemplates
42165      * when using named templates, you can do:
42166      *
42167      * var str = pl.applySubTemplate('your-name', values);
42168      *
42169      * 
42170      * @param {Number} id of the template
42171      * @param {Object} values to apply to template
42172      * @param {Object} parent (normaly the instance of this object)
42173      */
42174     applySubTemplate : function(id, values, parent)
42175     {
42176         
42177         
42178         var t = this.tpls[id];
42179         
42180         
42181         try { 
42182             if(t.test && !t.test.call(this, values, parent)){
42183                 return '';
42184             }
42185         } catch(e) {
42186             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42187             Roo.log(e.toString());
42188             Roo.log(t.test);
42189             return ''
42190         }
42191         try { 
42192             
42193             if(t.exec && t.exec.call(this, values, parent)){
42194                 return '';
42195             }
42196         } catch(e) {
42197             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42198             Roo.log(e.toString());
42199             Roo.log(t.exec);
42200             return ''
42201         }
42202         try {
42203             var vs = t.target ? t.target.call(this, values, parent) : values;
42204             parent = t.target ? values : parent;
42205             if(t.target && vs instanceof Array){
42206                 var buf = [];
42207                 for(var i = 0, len = vs.length; i < len; i++){
42208                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42209                 }
42210                 return buf.join('');
42211             }
42212             return t.compiled.call(this, vs, parent);
42213         } catch (e) {
42214             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42215             Roo.log(e.toString());
42216             Roo.log(t.compiled);
42217             return '';
42218         }
42219     },
42220
42221     compileTpl : function(tpl)
42222     {
42223         var fm = Roo.util.Format;
42224         var useF = this.disableFormats !== true;
42225         var sep = Roo.isGecko ? "+" : ",";
42226         var undef = function(str) {
42227             Roo.log("Property not found :"  + str);
42228             return '';
42229         };
42230         
42231         var fn = function(m, name, format, args)
42232         {
42233             //Roo.log(arguments);
42234             args = args ? args.replace(/\\'/g,"'") : args;
42235             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42236             if (typeof(format) == 'undefined') {
42237                 format= 'htmlEncode';
42238             }
42239             if (format == 'raw' ) {
42240                 format = false;
42241             }
42242             
42243             if(name.substr(0, 4) == 'xtpl'){
42244                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42245             }
42246             
42247             // build an array of options to determine if value is undefined..
42248             
42249             // basically get 'xxxx.yyyy' then do
42250             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42251             //    (function () { Roo.log("Property not found"); return ''; })() :
42252             //    ......
42253             
42254             var udef_ar = [];
42255             var lookfor = '';
42256             Roo.each(name.split('.'), function(st) {
42257                 lookfor += (lookfor.length ? '.': '') + st;
42258                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42259             });
42260             
42261             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42262             
42263             
42264             if(format && useF){
42265                 
42266                 args = args ? ',' + args : "";
42267                  
42268                 if(format.substr(0, 5) != "this."){
42269                     format = "fm." + format + '(';
42270                 }else{
42271                     format = 'this.call("'+ format.substr(5) + '", ';
42272                     args = ", values";
42273                 }
42274                 
42275                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42276             }
42277              
42278             if (args.length) {
42279                 // called with xxyx.yuu:(test,test)
42280                 // change to ()
42281                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42282             }
42283             // raw.. - :raw modifier..
42284             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42285             
42286         };
42287         var body;
42288         // branched to use + in gecko and [].join() in others
42289         if(Roo.isGecko){
42290             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42291                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42292                     "';};};";
42293         }else{
42294             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42295             body.push(tpl.body.replace(/(\r\n|\n)/g,
42296                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42297             body.push("'].join('');};};");
42298             body = body.join('');
42299         }
42300         
42301         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42302        
42303         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42304         eval(body);
42305         
42306         return this;
42307     },
42308
42309     applyTemplate : function(values){
42310         return this.master.compiled.call(this, values, {});
42311         //var s = this.subs;
42312     },
42313
42314     apply : function(){
42315         return this.applyTemplate.apply(this, arguments);
42316     }
42317
42318  });
42319
42320 Roo.XTemplate.from = function(el){
42321     el = Roo.getDom(el);
42322     return new Roo.XTemplate(el.value || el.innerHTML);
42323 };