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         return function(r){
5263             return value.test(r.data[property]);
5264         };
5265     },
5266
5267     /**
5268      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5269      * @param {String} property A field on your records
5270      * @param {Number} start The record index to start at (defaults to 0)
5271      * @param {Number} end The last record index to include (defaults to length - 1)
5272      * @return {Number} The sum
5273      */
5274     sum : function(property, start, end){
5275         var rs = this.data.items, v = 0;
5276         start = start || 0;
5277         end = (end || end === 0) ? end : rs.length-1;
5278
5279         for(var i = start; i <= end; i++){
5280             v += (rs[i].data[property] || 0);
5281         }
5282         return v;
5283     },
5284
5285     /**
5286      * Filter the records by a specified property.
5287      * @param {String} field A field on your records
5288      * @param {String/RegExp} value Either a string that the field
5289      * should start with or a RegExp to test against the field
5290      * @param {Boolean} anyMatch True to match any part not just the beginning
5291      */
5292     filter : function(property, value, anyMatch){
5293         var fn = this.createFilterFn(property, value, anyMatch);
5294         return fn ? this.filterBy(fn) : this.clearFilter();
5295     },
5296
5297     /**
5298      * Filter by a function. The specified function will be called with each
5299      * record in this data source. If the function returns true the record is included,
5300      * otherwise it is filtered.
5301      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5302      * @param {Object} scope (optional) The scope of the function (defaults to this)
5303      */
5304     filterBy : function(fn, scope){
5305         this.snapshot = this.snapshot || this.data;
5306         this.data = this.queryBy(fn, scope||this);
5307         this.fireEvent("datachanged", this);
5308     },
5309
5310     /**
5311      * Query the records by a specified property.
5312      * @param {String} field A field on your records
5313      * @param {String/RegExp} value Either a string that the field
5314      * should start with or a RegExp to test against the field
5315      * @param {Boolean} anyMatch True to match any part not just the beginning
5316      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5317      */
5318     query : function(property, value, anyMatch){
5319         var fn = this.createFilterFn(property, value, anyMatch);
5320         return fn ? this.queryBy(fn) : this.data.clone();
5321     },
5322
5323     /**
5324      * Query by a function. The specified function will be called with each
5325      * record in this data source. If the function returns true the record is included
5326      * in the results.
5327      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5328      * @param {Object} scope (optional) The scope of the function (defaults to this)
5329       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5330      **/
5331     queryBy : function(fn, scope){
5332         var data = this.snapshot || this.data;
5333         return data.filterBy(fn, scope||this);
5334     },
5335
5336     /**
5337      * Collects unique values for a particular dataIndex from this store.
5338      * @param {String} dataIndex The property to collect
5339      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5340      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5341      * @return {Array} An array of the unique values
5342      **/
5343     collect : function(dataIndex, allowNull, bypassFilter){
5344         var d = (bypassFilter === true && this.snapshot) ?
5345                 this.snapshot.items : this.data.items;
5346         var v, sv, r = [], l = {};
5347         for(var i = 0, len = d.length; i < len; i++){
5348             v = d[i].data[dataIndex];
5349             sv = String(v);
5350             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5351                 l[sv] = true;
5352                 r[r.length] = v;
5353             }
5354         }
5355         return r;
5356     },
5357
5358     /**
5359      * Revert to a view of the Record cache with no filtering applied.
5360      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5361      */
5362     clearFilter : function(suppressEvent){
5363         if(this.snapshot && this.snapshot != this.data){
5364             this.data = this.snapshot;
5365             delete this.snapshot;
5366             if(suppressEvent !== true){
5367                 this.fireEvent("datachanged", this);
5368             }
5369         }
5370     },
5371
5372     // private
5373     afterEdit : function(record){
5374         if(this.modified.indexOf(record) == -1){
5375             this.modified.push(record);
5376         }
5377         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5378     },
5379     
5380     // private
5381     afterReject : function(record){
5382         this.modified.remove(record);
5383         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5384     },
5385
5386     // private
5387     afterCommit : function(record){
5388         this.modified.remove(record);
5389         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5390     },
5391
5392     /**
5393      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5394      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5395      */
5396     commitChanges : function(){
5397         var m = this.modified.slice(0);
5398         this.modified = [];
5399         for(var i = 0, len = m.length; i < len; i++){
5400             m[i].commit();
5401         }
5402     },
5403
5404     /**
5405      * Cancel outstanding changes on all changed records.
5406      */
5407     rejectChanges : function(){
5408         var m = this.modified.slice(0);
5409         this.modified = [];
5410         for(var i = 0, len = m.length; i < len; i++){
5411             m[i].reject();
5412         }
5413     },
5414
5415     onMetaChange : function(meta, rtype, o){
5416         this.recordType = rtype;
5417         this.fields = rtype.prototype.fields;
5418         delete this.snapshot;
5419         this.sortInfo = meta.sortInfo || this.sortInfo;
5420         this.modified = [];
5421         this.fireEvent('metachange', this, this.reader.meta);
5422     },
5423     
5424     moveIndex : function(data, type)
5425     {
5426         var index = this.indexOf(data);
5427         
5428         var newIndex = index + type;
5429         
5430         this.remove(data);
5431         
5432         this.insert(newIndex, data);
5433         
5434     }
5435 });/*
5436  * Based on:
5437  * Ext JS Library 1.1.1
5438  * Copyright(c) 2006-2007, Ext JS, LLC.
5439  *
5440  * Originally Released Under LGPL - original licence link has changed is not relivant.
5441  *
5442  * Fork - LGPL
5443  * <script type="text/javascript">
5444  */
5445
5446 /**
5447  * @class Roo.data.SimpleStore
5448  * @extends Roo.data.Store
5449  * Small helper class to make creating Stores from Array data easier.
5450  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5451  * @cfg {Array} fields An array of field definition objects, or field name strings.
5452  * @cfg {Array} data The multi-dimensional array of data
5453  * @constructor
5454  * @param {Object} config
5455  */
5456 Roo.data.SimpleStore = function(config){
5457     Roo.data.SimpleStore.superclass.constructor.call(this, {
5458         isLocal : true,
5459         reader: new Roo.data.ArrayReader({
5460                 id: config.id
5461             },
5462             Roo.data.Record.create(config.fields)
5463         ),
5464         proxy : new Roo.data.MemoryProxy(config.data)
5465     });
5466     this.load();
5467 };
5468 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478
5479 /**
5480 /**
5481  * @extends Roo.data.Store
5482  * @class Roo.data.JsonStore
5483  * Small helper class to make creating Stores for JSON data easier. <br/>
5484 <pre><code>
5485 var store = new Roo.data.JsonStore({
5486     url: 'get-images.php',
5487     root: 'images',
5488     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5489 });
5490 </code></pre>
5491  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5492  * JsonReader and HttpProxy (unless inline data is provided).</b>
5493  * @cfg {Array} fields An array of field definition objects, or field name strings.
5494  * @constructor
5495  * @param {Object} config
5496  */
5497 Roo.data.JsonStore = function(c){
5498     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5499         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5500         reader: new Roo.data.JsonReader(c, c.fields)
5501     }));
5502 };
5503 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5504  * Based on:
5505  * Ext JS Library 1.1.1
5506  * Copyright(c) 2006-2007, Ext JS, LLC.
5507  *
5508  * Originally Released Under LGPL - original licence link has changed is not relivant.
5509  *
5510  * Fork - LGPL
5511  * <script type="text/javascript">
5512  */
5513
5514  
5515 Roo.data.Field = function(config){
5516     if(typeof config == "string"){
5517         config = {name: config};
5518     }
5519     Roo.apply(this, config);
5520     
5521     if(!this.type){
5522         this.type = "auto";
5523     }
5524     
5525     var st = Roo.data.SortTypes;
5526     // named sortTypes are supported, here we look them up
5527     if(typeof this.sortType == "string"){
5528         this.sortType = st[this.sortType];
5529     }
5530     
5531     // set default sortType for strings and dates
5532     if(!this.sortType){
5533         switch(this.type){
5534             case "string":
5535                 this.sortType = st.asUCString;
5536                 break;
5537             case "date":
5538                 this.sortType = st.asDate;
5539                 break;
5540             default:
5541                 this.sortType = st.none;
5542         }
5543     }
5544
5545     // define once
5546     var stripRe = /[\$,%]/g;
5547
5548     // prebuilt conversion function for this field, instead of
5549     // switching every time we're reading a value
5550     if(!this.convert){
5551         var cv, dateFormat = this.dateFormat;
5552         switch(this.type){
5553             case "":
5554             case "auto":
5555             case undefined:
5556                 cv = function(v){ return v; };
5557                 break;
5558             case "string":
5559                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5560                 break;
5561             case "int":
5562                 cv = function(v){
5563                     return v !== undefined && v !== null && v !== '' ?
5564                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5565                     };
5566                 break;
5567             case "float":
5568                 cv = function(v){
5569                     return v !== undefined && v !== null && v !== '' ?
5570                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5571                     };
5572                 break;
5573             case "bool":
5574             case "boolean":
5575                 cv = function(v){ return v === true || v === "true" || v == 1; };
5576                 break;
5577             case "date":
5578                 cv = function(v){
5579                     if(!v){
5580                         return '';
5581                     }
5582                     if(v instanceof Date){
5583                         return v;
5584                     }
5585                     if(dateFormat){
5586                         if(dateFormat == "timestamp"){
5587                             return new Date(v*1000);
5588                         }
5589                         return Date.parseDate(v, dateFormat);
5590                     }
5591                     var parsed = Date.parse(v);
5592                     return parsed ? new Date(parsed) : null;
5593                 };
5594              break;
5595             
5596         }
5597         this.convert = cv;
5598     }
5599 };
5600
5601 Roo.data.Field.prototype = {
5602     dateFormat: null,
5603     defaultValue: "",
5604     mapping: null,
5605     sortType : null,
5606     sortDir : "ASC"
5607 };/*
5608  * Based on:
5609  * Ext JS Library 1.1.1
5610  * Copyright(c) 2006-2007, Ext JS, LLC.
5611  *
5612  * Originally Released Under LGPL - original licence link has changed is not relivant.
5613  *
5614  * Fork - LGPL
5615  * <script type="text/javascript">
5616  */
5617  
5618 // Base class for reading structured data from a data source.  This class is intended to be
5619 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5620
5621 /**
5622  * @class Roo.data.DataReader
5623  * Base class for reading structured data from a data source.  This class is intended to be
5624  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5625  */
5626
5627 Roo.data.DataReader = function(meta, recordType){
5628     
5629     this.meta = meta;
5630     
5631     this.recordType = recordType instanceof Array ? 
5632         Roo.data.Record.create(recordType) : recordType;
5633 };
5634
5635 Roo.data.DataReader.prototype = {
5636      /**
5637      * Create an empty record
5638      * @param {Object} data (optional) - overlay some values
5639      * @return {Roo.data.Record} record created.
5640      */
5641     newRow :  function(d) {
5642         var da =  {};
5643         this.recordType.prototype.fields.each(function(c) {
5644             switch( c.type) {
5645                 case 'int' : da[c.name] = 0; break;
5646                 case 'date' : da[c.name] = new Date(); break;
5647                 case 'float' : da[c.name] = 0.0; break;
5648                 case 'boolean' : da[c.name] = false; break;
5649                 default : da[c.name] = ""; break;
5650             }
5651             
5652         });
5653         return new this.recordType(Roo.apply(da, d));
5654     }
5655     
5656 };/*
5657  * Based on:
5658  * Ext JS Library 1.1.1
5659  * Copyright(c) 2006-2007, Ext JS, LLC.
5660  *
5661  * Originally Released Under LGPL - original licence link has changed is not relivant.
5662  *
5663  * Fork - LGPL
5664  * <script type="text/javascript">
5665  */
5666
5667 /**
5668  * @class Roo.data.DataProxy
5669  * @extends Roo.data.Observable
5670  * This class is an abstract base class for implementations which provide retrieval of
5671  * unformatted data objects.<br>
5672  * <p>
5673  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5674  * (of the appropriate type which knows how to parse the data object) to provide a block of
5675  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5676  * <p>
5677  * Custom implementations must implement the load method as described in
5678  * {@link Roo.data.HttpProxy#load}.
5679  */
5680 Roo.data.DataProxy = function(){
5681     this.addEvents({
5682         /**
5683          * @event beforeload
5684          * Fires before a network request is made to retrieve a data object.
5685          * @param {Object} This DataProxy object.
5686          * @param {Object} params The params parameter to the load function.
5687          */
5688         beforeload : true,
5689         /**
5690          * @event load
5691          * Fires before the load method's callback is called.
5692          * @param {Object} This DataProxy object.
5693          * @param {Object} o The data object.
5694          * @param {Object} arg The callback argument object passed to the load function.
5695          */
5696         load : true,
5697         /**
5698          * @event loadexception
5699          * Fires if an Exception occurs during data retrieval.
5700          * @param {Object} This DataProxy object.
5701          * @param {Object} o The data object.
5702          * @param {Object} arg The callback argument object passed to the load function.
5703          * @param {Object} e The Exception.
5704          */
5705         loadexception : true
5706     });
5707     Roo.data.DataProxy.superclass.constructor.call(this);
5708 };
5709
5710 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5711
5712     /**
5713      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5714      */
5715 /*
5716  * Based on:
5717  * Ext JS Library 1.1.1
5718  * Copyright(c) 2006-2007, Ext JS, LLC.
5719  *
5720  * Originally Released Under LGPL - original licence link has changed is not relivant.
5721  *
5722  * Fork - LGPL
5723  * <script type="text/javascript">
5724  */
5725 /**
5726  * @class Roo.data.MemoryProxy
5727  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5728  * to the Reader when its load method is called.
5729  * @constructor
5730  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5731  */
5732 Roo.data.MemoryProxy = function(data){
5733     if (data.data) {
5734         data = data.data;
5735     }
5736     Roo.data.MemoryProxy.superclass.constructor.call(this);
5737     this.data = data;
5738 };
5739
5740 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5741     /**
5742      * Load data from the requested source (in this case an in-memory
5743      * data object passed to the constructor), read the data object into
5744      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5745      * process that block using the passed callback.
5746      * @param {Object} params This parameter is not used by the MemoryProxy class.
5747      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5748      * object into a block of Roo.data.Records.
5749      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5750      * The function must be passed <ul>
5751      * <li>The Record block object</li>
5752      * <li>The "arg" argument from the load function</li>
5753      * <li>A boolean success indicator</li>
5754      * </ul>
5755      * @param {Object} scope The scope in which to call the callback
5756      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5757      */
5758     load : function(params, reader, callback, scope, arg){
5759         params = params || {};
5760         var result;
5761         try {
5762             result = reader.readRecords(this.data);
5763         }catch(e){
5764             this.fireEvent("loadexception", this, arg, null, e);
5765             callback.call(scope, null, arg, false);
5766             return;
5767         }
5768         callback.call(scope, result, arg, true);
5769     },
5770     
5771     // private
5772     update : function(params, records){
5773         
5774     }
5775 });/*
5776  * Based on:
5777  * Ext JS Library 1.1.1
5778  * Copyright(c) 2006-2007, Ext JS, LLC.
5779  *
5780  * Originally Released Under LGPL - original licence link has changed is not relivant.
5781  *
5782  * Fork - LGPL
5783  * <script type="text/javascript">
5784  */
5785 /**
5786  * @class Roo.data.HttpProxy
5787  * @extends Roo.data.DataProxy
5788  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5789  * configured to reference a certain URL.<br><br>
5790  * <p>
5791  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5792  * from which the running page was served.<br><br>
5793  * <p>
5794  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5795  * <p>
5796  * Be aware that to enable the browser to parse an XML document, the server must set
5797  * the Content-Type header in the HTTP response to "text/xml".
5798  * @constructor
5799  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5800  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5801  * will be used to make the request.
5802  */
5803 Roo.data.HttpProxy = function(conn){
5804     Roo.data.HttpProxy.superclass.constructor.call(this);
5805     // is conn a conn config or a real conn?
5806     this.conn = conn;
5807     this.useAjax = !conn || !conn.events;
5808   
5809 };
5810
5811 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5812     // thse are take from connection...
5813     
5814     /**
5815      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5816      */
5817     /**
5818      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5819      * extra parameters to each request made by this object. (defaults to undefined)
5820      */
5821     /**
5822      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5823      *  to each request made by this object. (defaults to undefined)
5824      */
5825     /**
5826      * @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)
5827      */
5828     /**
5829      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5830      */
5831      /**
5832      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5833      * @type Boolean
5834      */
5835   
5836
5837     /**
5838      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5839      * @type Boolean
5840      */
5841     /**
5842      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5843      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5844      * a finer-grained basis than the DataProxy events.
5845      */
5846     getConnection : function(){
5847         return this.useAjax ? Roo.Ajax : this.conn;
5848     },
5849
5850     /**
5851      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5852      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5853      * process that block using the passed callback.
5854      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5855      * for the request to the remote server.
5856      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5857      * object into a block of Roo.data.Records.
5858      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5859      * The function must be passed <ul>
5860      * <li>The Record block object</li>
5861      * <li>The "arg" argument from the load function</li>
5862      * <li>A boolean success indicator</li>
5863      * </ul>
5864      * @param {Object} scope The scope in which to call the callback
5865      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5866      */
5867     load : function(params, reader, callback, scope, arg){
5868         if(this.fireEvent("beforeload", this, params) !== false){
5869             var  o = {
5870                 params : params || {},
5871                 request: {
5872                     callback : callback,
5873                     scope : scope,
5874                     arg : arg
5875                 },
5876                 reader: reader,
5877                 callback : this.loadResponse,
5878                 scope: this
5879             };
5880             if(this.useAjax){
5881                 Roo.applyIf(o, this.conn);
5882                 if(this.activeRequest){
5883                     Roo.Ajax.abort(this.activeRequest);
5884                 }
5885                 this.activeRequest = Roo.Ajax.request(o);
5886             }else{
5887                 this.conn.request(o);
5888             }
5889         }else{
5890             callback.call(scope||this, null, arg, false);
5891         }
5892     },
5893
5894     // private
5895     loadResponse : function(o, success, response){
5896         delete this.activeRequest;
5897         if(!success){
5898             this.fireEvent("loadexception", this, o, response);
5899             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5900             return;
5901         }
5902         var result;
5903         try {
5904             result = o.reader.read(response);
5905         }catch(e){
5906             this.fireEvent("loadexception", this, o, response, e);
5907             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5908             return;
5909         }
5910         
5911         this.fireEvent("load", this, o, o.request.arg);
5912         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5913     },
5914
5915     // private
5916     update : function(dataSet){
5917
5918     },
5919
5920     // private
5921     updateResponse : function(dataSet){
5922
5923     }
5924 });/*
5925  * Based on:
5926  * Ext JS Library 1.1.1
5927  * Copyright(c) 2006-2007, Ext JS, LLC.
5928  *
5929  * Originally Released Under LGPL - original licence link has changed is not relivant.
5930  *
5931  * Fork - LGPL
5932  * <script type="text/javascript">
5933  */
5934
5935 /**
5936  * @class Roo.data.ScriptTagProxy
5937  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5938  * other than the originating domain of the running page.<br><br>
5939  * <p>
5940  * <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
5941  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5942  * <p>
5943  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5944  * source code that is used as the source inside a &lt;script> tag.<br><br>
5945  * <p>
5946  * In order for the browser to process the returned data, the server must wrap the data object
5947  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5948  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5949  * depending on whether the callback name was passed:
5950  * <p>
5951  * <pre><code>
5952 boolean scriptTag = false;
5953 String cb = request.getParameter("callback");
5954 if (cb != null) {
5955     scriptTag = true;
5956     response.setContentType("text/javascript");
5957 } else {
5958     response.setContentType("application/x-json");
5959 }
5960 Writer out = response.getWriter();
5961 if (scriptTag) {
5962     out.write(cb + "(");
5963 }
5964 out.print(dataBlock.toJsonString());
5965 if (scriptTag) {
5966     out.write(");");
5967 }
5968 </pre></code>
5969  *
5970  * @constructor
5971  * @param {Object} config A configuration object.
5972  */
5973 Roo.data.ScriptTagProxy = function(config){
5974     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5975     Roo.apply(this, config);
5976     this.head = document.getElementsByTagName("head")[0];
5977 };
5978
5979 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5980
5981 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5982     /**
5983      * @cfg {String} url The URL from which to request the data object.
5984      */
5985     /**
5986      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5987      */
5988     timeout : 30000,
5989     /**
5990      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5991      * the server the name of the callback function set up by the load call to process the returned data object.
5992      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5993      * javascript output which calls this named function passing the data object as its only parameter.
5994      */
5995     callbackParam : "callback",
5996     /**
5997      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5998      * name to the request.
5999      */
6000     nocache : true,
6001
6002     /**
6003      * Load data from the configured URL, read the data object into
6004      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6005      * process that block using the passed callback.
6006      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6007      * for the request to the remote server.
6008      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6009      * object into a block of Roo.data.Records.
6010      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6011      * The function must be passed <ul>
6012      * <li>The Record block object</li>
6013      * <li>The "arg" argument from the load function</li>
6014      * <li>A boolean success indicator</li>
6015      * </ul>
6016      * @param {Object} scope The scope in which to call the callback
6017      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6018      */
6019     load : function(params, reader, callback, scope, arg){
6020         if(this.fireEvent("beforeload", this, params) !== false){
6021
6022             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6023
6024             var url = this.url;
6025             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6026             if(this.nocache){
6027                 url += "&_dc=" + (new Date().getTime());
6028             }
6029             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6030             var trans = {
6031                 id : transId,
6032                 cb : "stcCallback"+transId,
6033                 scriptId : "stcScript"+transId,
6034                 params : params,
6035                 arg : arg,
6036                 url : url,
6037                 callback : callback,
6038                 scope : scope,
6039                 reader : reader
6040             };
6041             var conn = this;
6042
6043             window[trans.cb] = function(o){
6044                 conn.handleResponse(o, trans);
6045             };
6046
6047             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6048
6049             if(this.autoAbort !== false){
6050                 this.abort();
6051             }
6052
6053             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6054
6055             var script = document.createElement("script");
6056             script.setAttribute("src", url);
6057             script.setAttribute("type", "text/javascript");
6058             script.setAttribute("id", trans.scriptId);
6059             this.head.appendChild(script);
6060
6061             this.trans = trans;
6062         }else{
6063             callback.call(scope||this, null, arg, false);
6064         }
6065     },
6066
6067     // private
6068     isLoading : function(){
6069         return this.trans ? true : false;
6070     },
6071
6072     /**
6073      * Abort the current server request.
6074      */
6075     abort : function(){
6076         if(this.isLoading()){
6077             this.destroyTrans(this.trans);
6078         }
6079     },
6080
6081     // private
6082     destroyTrans : function(trans, isLoaded){
6083         this.head.removeChild(document.getElementById(trans.scriptId));
6084         clearTimeout(trans.timeoutId);
6085         if(isLoaded){
6086             window[trans.cb] = undefined;
6087             try{
6088                 delete window[trans.cb];
6089             }catch(e){}
6090         }else{
6091             // if hasn't been loaded, wait for load to remove it to prevent script error
6092             window[trans.cb] = function(){
6093                 window[trans.cb] = undefined;
6094                 try{
6095                     delete window[trans.cb];
6096                 }catch(e){}
6097             };
6098         }
6099     },
6100
6101     // private
6102     handleResponse : function(o, trans){
6103         this.trans = false;
6104         this.destroyTrans(trans, true);
6105         var result;
6106         try {
6107             result = trans.reader.readRecords(o);
6108         }catch(e){
6109             this.fireEvent("loadexception", this, o, trans.arg, e);
6110             trans.callback.call(trans.scope||window, null, trans.arg, false);
6111             return;
6112         }
6113         this.fireEvent("load", this, o, trans.arg);
6114         trans.callback.call(trans.scope||window, result, trans.arg, true);
6115     },
6116
6117     // private
6118     handleFailure : function(trans){
6119         this.trans = false;
6120         this.destroyTrans(trans, false);
6121         this.fireEvent("loadexception", this, null, trans.arg);
6122         trans.callback.call(trans.scope||window, null, trans.arg, false);
6123     }
6124 });/*
6125  * Based on:
6126  * Ext JS Library 1.1.1
6127  * Copyright(c) 2006-2007, Ext JS, LLC.
6128  *
6129  * Originally Released Under LGPL - original licence link has changed is not relivant.
6130  *
6131  * Fork - LGPL
6132  * <script type="text/javascript">
6133  */
6134
6135 /**
6136  * @class Roo.data.JsonReader
6137  * @extends Roo.data.DataReader
6138  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6139  * based on mappings in a provided Roo.data.Record constructor.
6140  * 
6141  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6142  * in the reply previously. 
6143  * 
6144  * <p>
6145  * Example code:
6146  * <pre><code>
6147 var RecordDef = Roo.data.Record.create([
6148     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6149     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6150 ]);
6151 var myReader = new Roo.data.JsonReader({
6152     totalProperty: "results",    // The property which contains the total dataset size (optional)
6153     root: "rows",                // The property which contains an Array of row objects
6154     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6155 }, RecordDef);
6156 </code></pre>
6157  * <p>
6158  * This would consume a JSON file like this:
6159  * <pre><code>
6160 { 'results': 2, 'rows': [
6161     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6162     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6163 }
6164 </code></pre>
6165  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6166  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6167  * paged from the remote server.
6168  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6169  * @cfg {String} root name of the property which contains the Array of row objects.
6170  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6171  * @constructor
6172  * Create a new JsonReader
6173  * @param {Object} meta Metadata configuration options
6174  * @param {Object} recordType Either an Array of field definition objects,
6175  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6176  */
6177 Roo.data.JsonReader = function(meta, recordType){
6178     
6179     meta = meta || {};
6180     // set some defaults:
6181     Roo.applyIf(meta, {
6182         totalProperty: 'total',
6183         successProperty : 'success',
6184         root : 'data',
6185         id : 'id'
6186     });
6187     
6188     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6189 };
6190 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6191     
6192     /**
6193      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6194      * Used by Store query builder to append _requestMeta to params.
6195      * 
6196      */
6197     metaFromRemote : false,
6198     /**
6199      * This method is only used by a DataProxy which has retrieved data from a remote server.
6200      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6201      * @return {Object} data A data block which is used by an Roo.data.Store object as
6202      * a cache of Roo.data.Records.
6203      */
6204     read : function(response){
6205         var json = response.responseText;
6206        
6207         var o = /* eval:var:o */ eval("("+json+")");
6208         if(!o) {
6209             throw {message: "JsonReader.read: Json object not found"};
6210         }
6211         
6212         if(o.metaData){
6213             
6214             delete this.ef;
6215             this.metaFromRemote = true;
6216             this.meta = o.metaData;
6217             this.recordType = Roo.data.Record.create(o.metaData.fields);
6218             this.onMetaChange(this.meta, this.recordType, o);
6219         }
6220         return this.readRecords(o);
6221     },
6222
6223     // private function a store will implement
6224     onMetaChange : function(meta, recordType, o){
6225
6226     },
6227
6228     /**
6229          * @ignore
6230          */
6231     simpleAccess: function(obj, subsc) {
6232         return obj[subsc];
6233     },
6234
6235         /**
6236          * @ignore
6237          */
6238     getJsonAccessor: function(){
6239         var re = /[\[\.]/;
6240         return function(expr) {
6241             try {
6242                 return(re.test(expr))
6243                     ? new Function("obj", "return obj." + expr)
6244                     : function(obj){
6245                         return obj[expr];
6246                     };
6247             } catch(e){}
6248             return Roo.emptyFn;
6249         };
6250     }(),
6251
6252     /**
6253      * Create a data block containing Roo.data.Records from an XML document.
6254      * @param {Object} o An object which contains an Array of row objects in the property specified
6255      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6256      * which contains the total size of the dataset.
6257      * @return {Object} data A data block which is used by an Roo.data.Store object as
6258      * a cache of Roo.data.Records.
6259      */
6260     readRecords : function(o){
6261         /**
6262          * After any data loads, the raw JSON data is available for further custom processing.
6263          * @type Object
6264          */
6265         this.o = o;
6266         var s = this.meta, Record = this.recordType,
6267             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6268
6269 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6270         if (!this.ef) {
6271             if(s.totalProperty) {
6272                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6273                 }
6274                 if(s.successProperty) {
6275                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6276                 }
6277                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6278                 if (s.id) {
6279                         var g = this.getJsonAccessor(s.id);
6280                         this.getId = function(rec) {
6281                                 var r = g(rec);  
6282                                 return (r === undefined || r === "") ? null : r;
6283                         };
6284                 } else {
6285                         this.getId = function(){return null;};
6286                 }
6287             this.ef = [];
6288             for(var jj = 0; jj < fl; jj++){
6289                 f = fi[jj];
6290                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6291                 this.ef[jj] = this.getJsonAccessor(map);
6292             }
6293         }
6294
6295         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6296         if(s.totalProperty){
6297             var vt = parseInt(this.getTotal(o), 10);
6298             if(!isNaN(vt)){
6299                 totalRecords = vt;
6300             }
6301         }
6302         if(s.successProperty){
6303             var vs = this.getSuccess(o);
6304             if(vs === false || vs === 'false'){
6305                 success = false;
6306             }
6307         }
6308         var records = [];
6309         for(var i = 0; i < c; i++){
6310                 var n = root[i];
6311             var values = {};
6312             var id = this.getId(n);
6313             for(var j = 0; j < fl; j++){
6314                 f = fi[j];
6315             var v = this.ef[j](n);
6316             if (!f.convert) {
6317                 Roo.log('missing convert for ' + f.name);
6318                 Roo.log(f);
6319                 continue;
6320             }
6321             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6322             }
6323             var record = new Record(values, id);
6324             record.json = n;
6325             records[i] = record;
6326         }
6327         return {
6328             raw : o,
6329             success : success,
6330             records : records,
6331             totalRecords : totalRecords
6332         };
6333     }
6334 });/*
6335  * Based on:
6336  * Ext JS Library 1.1.1
6337  * Copyright(c) 2006-2007, Ext JS, LLC.
6338  *
6339  * Originally Released Under LGPL - original licence link has changed is not relivant.
6340  *
6341  * Fork - LGPL
6342  * <script type="text/javascript">
6343  */
6344
6345 /**
6346  * @class Roo.data.XmlReader
6347  * @extends Roo.data.DataReader
6348  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6349  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6350  * <p>
6351  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6352  * header in the HTTP response must be set to "text/xml".</em>
6353  * <p>
6354  * Example code:
6355  * <pre><code>
6356 var RecordDef = Roo.data.Record.create([
6357    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6358    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6359 ]);
6360 var myReader = new Roo.data.XmlReader({
6361    totalRecords: "results", // The element which contains the total dataset size (optional)
6362    record: "row",           // The repeated element which contains row information
6363    id: "id"                 // The element within the row that provides an ID for the record (optional)
6364 }, RecordDef);
6365 </code></pre>
6366  * <p>
6367  * This would consume an XML file like this:
6368  * <pre><code>
6369 &lt;?xml?>
6370 &lt;dataset>
6371  &lt;results>2&lt;/results>
6372  &lt;row>
6373    &lt;id>1&lt;/id>
6374    &lt;name>Bill&lt;/name>
6375    &lt;occupation>Gardener&lt;/occupation>
6376  &lt;/row>
6377  &lt;row>
6378    &lt;id>2&lt;/id>
6379    &lt;name>Ben&lt;/name>
6380    &lt;occupation>Horticulturalist&lt;/occupation>
6381  &lt;/row>
6382 &lt;/dataset>
6383 </code></pre>
6384  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6385  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6386  * paged from the remote server.
6387  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6388  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6389  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6390  * a record identifier value.
6391  * @constructor
6392  * Create a new XmlReader
6393  * @param {Object} meta Metadata configuration options
6394  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6395  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6396  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6397  */
6398 Roo.data.XmlReader = function(meta, recordType){
6399     meta = meta || {};
6400     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6401 };
6402 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6403     /**
6404      * This method is only used by a DataProxy which has retrieved data from a remote server.
6405          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6406          * to contain a method called 'responseXML' that returns an XML document object.
6407      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6408      * a cache of Roo.data.Records.
6409      */
6410     read : function(response){
6411         var doc = response.responseXML;
6412         if(!doc) {
6413             throw {message: "XmlReader.read: XML Document not available"};
6414         }
6415         return this.readRecords(doc);
6416     },
6417
6418     /**
6419      * Create a data block containing Roo.data.Records from an XML document.
6420          * @param {Object} doc A parsed XML document.
6421      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6422      * a cache of Roo.data.Records.
6423      */
6424     readRecords : function(doc){
6425         /**
6426          * After any data loads/reads, the raw XML Document is available for further custom processing.
6427          * @type XMLDocument
6428          */
6429         this.xmlData = doc;
6430         var root = doc.documentElement || doc;
6431         var q = Roo.DomQuery;
6432         var recordType = this.recordType, fields = recordType.prototype.fields;
6433         var sid = this.meta.id;
6434         var totalRecords = 0, success = true;
6435         if(this.meta.totalRecords){
6436             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6437         }
6438         
6439         if(this.meta.success){
6440             var sv = q.selectValue(this.meta.success, root, true);
6441             success = sv !== false && sv !== 'false';
6442         }
6443         var records = [];
6444         var ns = q.select(this.meta.record, root);
6445         for(var i = 0, len = ns.length; i < len; i++) {
6446                 var n = ns[i];
6447                 var values = {};
6448                 var id = sid ? q.selectValue(sid, n) : undefined;
6449                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6450                     var f = fields.items[j];
6451                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6452                     v = f.convert(v);
6453                     values[f.name] = v;
6454                 }
6455                 var record = new recordType(values, id);
6456                 record.node = n;
6457                 records[records.length] = record;
6458             }
6459
6460             return {
6461                 success : success,
6462                 records : records,
6463                 totalRecords : totalRecords || records.length
6464             };
6465     }
6466 });/*
6467  * Based on:
6468  * Ext JS Library 1.1.1
6469  * Copyright(c) 2006-2007, Ext JS, LLC.
6470  *
6471  * Originally Released Under LGPL - original licence link has changed is not relivant.
6472  *
6473  * Fork - LGPL
6474  * <script type="text/javascript">
6475  */
6476
6477 /**
6478  * @class Roo.data.ArrayReader
6479  * @extends Roo.data.DataReader
6480  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6481  * Each element of that Array represents a row of data fields. The
6482  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6483  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6484  * <p>
6485  * Example code:.
6486  * <pre><code>
6487 var RecordDef = Roo.data.Record.create([
6488     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6489     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6490 ]);
6491 var myReader = new Roo.data.ArrayReader({
6492     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6493 }, RecordDef);
6494 </code></pre>
6495  * <p>
6496  * This would consume an Array like this:
6497  * <pre><code>
6498 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6499   </code></pre>
6500  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6501  * @constructor
6502  * Create a new JsonReader
6503  * @param {Object} meta Metadata configuration options.
6504  * @param {Object} recordType Either an Array of field definition objects
6505  * as specified to {@link Roo.data.Record#create},
6506  * or an {@link Roo.data.Record} object
6507  * created using {@link Roo.data.Record#create}.
6508  */
6509 Roo.data.ArrayReader = function(meta, recordType){
6510     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6511 };
6512
6513 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6514     /**
6515      * Create a data block containing Roo.data.Records from an XML document.
6516      * @param {Object} o An Array of row objects which represents the dataset.
6517      * @return {Object} data A data block which is used by an Roo.data.Store object as
6518      * a cache of Roo.data.Records.
6519      */
6520     readRecords : function(o){
6521         var sid = this.meta ? this.meta.id : null;
6522         var recordType = this.recordType, fields = recordType.prototype.fields;
6523         var records = [];
6524         var root = o;
6525             for(var i = 0; i < root.length; i++){
6526                     var n = root[i];
6527                 var values = {};
6528                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6529                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6530                 var f = fields.items[j];
6531                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6532                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6533                 v = f.convert(v);
6534                 values[f.name] = v;
6535             }
6536                 var record = new recordType(values, id);
6537                 record.json = n;
6538                 records[records.length] = record;
6539             }
6540             return {
6541                 records : records,
6542                 totalRecords : records.length
6543             };
6544     }
6545 });/*
6546  * Based on:
6547  * Ext JS Library 1.1.1
6548  * Copyright(c) 2006-2007, Ext JS, LLC.
6549  *
6550  * Originally Released Under LGPL - original licence link has changed is not relivant.
6551  *
6552  * Fork - LGPL
6553  * <script type="text/javascript">
6554  */
6555
6556
6557 /**
6558  * @class Roo.data.Tree
6559  * @extends Roo.util.Observable
6560  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6561  * in the tree have most standard DOM functionality.
6562  * @constructor
6563  * @param {Node} root (optional) The root node
6564  */
6565 Roo.data.Tree = function(root){
6566    this.nodeHash = {};
6567    /**
6568     * The root node for this tree
6569     * @type Node
6570     */
6571    this.root = null;
6572    if(root){
6573        this.setRootNode(root);
6574    }
6575    this.addEvents({
6576        /**
6577         * @event append
6578         * Fires when a new child node is appended to a node in this tree.
6579         * @param {Tree} tree The owner tree
6580         * @param {Node} parent The parent node
6581         * @param {Node} node The newly appended node
6582         * @param {Number} index The index of the newly appended node
6583         */
6584        "append" : true,
6585        /**
6586         * @event remove
6587         * Fires when a child node is removed from a node in this tree.
6588         * @param {Tree} tree The owner tree
6589         * @param {Node} parent The parent node
6590         * @param {Node} node The child node removed
6591         */
6592        "remove" : true,
6593        /**
6594         * @event move
6595         * Fires when a node is moved to a new location in the tree
6596         * @param {Tree} tree The owner tree
6597         * @param {Node} node The node moved
6598         * @param {Node} oldParent The old parent of this node
6599         * @param {Node} newParent The new parent of this node
6600         * @param {Number} index The index it was moved to
6601         */
6602        "move" : true,
6603        /**
6604         * @event insert
6605         * Fires when a new child node is inserted in a node in this tree.
6606         * @param {Tree} tree The owner tree
6607         * @param {Node} parent The parent node
6608         * @param {Node} node The child node inserted
6609         * @param {Node} refNode The child node the node was inserted before
6610         */
6611        "insert" : true,
6612        /**
6613         * @event beforeappend
6614         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6615         * @param {Tree} tree The owner tree
6616         * @param {Node} parent The parent node
6617         * @param {Node} node The child node to be appended
6618         */
6619        "beforeappend" : true,
6620        /**
6621         * @event beforeremove
6622         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6623         * @param {Tree} tree The owner tree
6624         * @param {Node} parent The parent node
6625         * @param {Node} node The child node to be removed
6626         */
6627        "beforeremove" : true,
6628        /**
6629         * @event beforemove
6630         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6631         * @param {Tree} tree The owner tree
6632         * @param {Node} node The node being moved
6633         * @param {Node} oldParent The parent of the node
6634         * @param {Node} newParent The new parent the node is moving to
6635         * @param {Number} index The index it is being moved to
6636         */
6637        "beforemove" : true,
6638        /**
6639         * @event beforeinsert
6640         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6641         * @param {Tree} tree The owner tree
6642         * @param {Node} parent The parent node
6643         * @param {Node} node The child node to be inserted
6644         * @param {Node} refNode The child node the node is being inserted before
6645         */
6646        "beforeinsert" : true
6647    });
6648
6649     Roo.data.Tree.superclass.constructor.call(this);
6650 };
6651
6652 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6653     pathSeparator: "/",
6654
6655     proxyNodeEvent : function(){
6656         return this.fireEvent.apply(this, arguments);
6657     },
6658
6659     /**
6660      * Returns the root node for this tree.
6661      * @return {Node}
6662      */
6663     getRootNode : function(){
6664         return this.root;
6665     },
6666
6667     /**
6668      * Sets the root node for this tree.
6669      * @param {Node} node
6670      * @return {Node}
6671      */
6672     setRootNode : function(node){
6673         this.root = node;
6674         node.ownerTree = this;
6675         node.isRoot = true;
6676         this.registerNode(node);
6677         return node;
6678     },
6679
6680     /**
6681      * Gets a node in this tree by its id.
6682      * @param {String} id
6683      * @return {Node}
6684      */
6685     getNodeById : function(id){
6686         return this.nodeHash[id];
6687     },
6688
6689     registerNode : function(node){
6690         this.nodeHash[node.id] = node;
6691     },
6692
6693     unregisterNode : function(node){
6694         delete this.nodeHash[node.id];
6695     },
6696
6697     toString : function(){
6698         return "[Tree"+(this.id?" "+this.id:"")+"]";
6699     }
6700 });
6701
6702 /**
6703  * @class Roo.data.Node
6704  * @extends Roo.util.Observable
6705  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6706  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6707  * @constructor
6708  * @param {Object} attributes The attributes/config for the node
6709  */
6710 Roo.data.Node = function(attributes){
6711     /**
6712      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6713      * @type {Object}
6714      */
6715     this.attributes = attributes || {};
6716     this.leaf = this.attributes.leaf;
6717     /**
6718      * The node id. @type String
6719      */
6720     this.id = this.attributes.id;
6721     if(!this.id){
6722         this.id = Roo.id(null, "ynode-");
6723         this.attributes.id = this.id;
6724     }
6725      
6726     
6727     /**
6728      * All child nodes of this node. @type Array
6729      */
6730     this.childNodes = [];
6731     if(!this.childNodes.indexOf){ // indexOf is a must
6732         this.childNodes.indexOf = function(o){
6733             for(var i = 0, len = this.length; i < len; i++){
6734                 if(this[i] == o) {
6735                     return i;
6736                 }
6737             }
6738             return -1;
6739         };
6740     }
6741     /**
6742      * The parent node for this node. @type Node
6743      */
6744     this.parentNode = null;
6745     /**
6746      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6747      */
6748     this.firstChild = null;
6749     /**
6750      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6751      */
6752     this.lastChild = null;
6753     /**
6754      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6755      */
6756     this.previousSibling = null;
6757     /**
6758      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6759      */
6760     this.nextSibling = null;
6761
6762     this.addEvents({
6763        /**
6764         * @event append
6765         * Fires when a new child node is appended
6766         * @param {Tree} tree The owner tree
6767         * @param {Node} this This node
6768         * @param {Node} node The newly appended node
6769         * @param {Number} index The index of the newly appended node
6770         */
6771        "append" : true,
6772        /**
6773         * @event remove
6774         * Fires when a child node is removed
6775         * @param {Tree} tree The owner tree
6776         * @param {Node} this This node
6777         * @param {Node} node The removed node
6778         */
6779        "remove" : true,
6780        /**
6781         * @event move
6782         * Fires when this node is moved to a new location in the tree
6783         * @param {Tree} tree The owner tree
6784         * @param {Node} this This node
6785         * @param {Node} oldParent The old parent of this node
6786         * @param {Node} newParent The new parent of this node
6787         * @param {Number} index The index it was moved to
6788         */
6789        "move" : true,
6790        /**
6791         * @event insert
6792         * Fires when a new child node is inserted.
6793         * @param {Tree} tree The owner tree
6794         * @param {Node} this This node
6795         * @param {Node} node The child node inserted
6796         * @param {Node} refNode The child node the node was inserted before
6797         */
6798        "insert" : true,
6799        /**
6800         * @event beforeappend
6801         * Fires before a new child is appended, return false to cancel the append.
6802         * @param {Tree} tree The owner tree
6803         * @param {Node} this This node
6804         * @param {Node} node The child node to be appended
6805         */
6806        "beforeappend" : true,
6807        /**
6808         * @event beforeremove
6809         * Fires before a child is removed, return false to cancel the remove.
6810         * @param {Tree} tree The owner tree
6811         * @param {Node} this This node
6812         * @param {Node} node The child node to be removed
6813         */
6814        "beforeremove" : true,
6815        /**
6816         * @event beforemove
6817         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6818         * @param {Tree} tree The owner tree
6819         * @param {Node} this This node
6820         * @param {Node} oldParent The parent of this node
6821         * @param {Node} newParent The new parent this node is moving to
6822         * @param {Number} index The index it is being moved to
6823         */
6824        "beforemove" : true,
6825        /**
6826         * @event beforeinsert
6827         * Fires before a new child is inserted, return false to cancel the insert.
6828         * @param {Tree} tree The owner tree
6829         * @param {Node} this This node
6830         * @param {Node} node The child node to be inserted
6831         * @param {Node} refNode The child node the node is being inserted before
6832         */
6833        "beforeinsert" : true
6834    });
6835     this.listeners = this.attributes.listeners;
6836     Roo.data.Node.superclass.constructor.call(this);
6837 };
6838
6839 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6840     fireEvent : function(evtName){
6841         // first do standard event for this node
6842         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6843             return false;
6844         }
6845         // then bubble it up to the tree if the event wasn't cancelled
6846         var ot = this.getOwnerTree();
6847         if(ot){
6848             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6849                 return false;
6850             }
6851         }
6852         return true;
6853     },
6854
6855     /**
6856      * Returns true if this node is a leaf
6857      * @return {Boolean}
6858      */
6859     isLeaf : function(){
6860         return this.leaf === true;
6861     },
6862
6863     // private
6864     setFirstChild : function(node){
6865         this.firstChild = node;
6866     },
6867
6868     //private
6869     setLastChild : function(node){
6870         this.lastChild = node;
6871     },
6872
6873
6874     /**
6875      * Returns true if this node is the last child of its parent
6876      * @return {Boolean}
6877      */
6878     isLast : function(){
6879        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6880     },
6881
6882     /**
6883      * Returns true if this node is the first child of its parent
6884      * @return {Boolean}
6885      */
6886     isFirst : function(){
6887        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6888     },
6889
6890     hasChildNodes : function(){
6891         return !this.isLeaf() && this.childNodes.length > 0;
6892     },
6893
6894     /**
6895      * Insert node(s) as the last child node of this node.
6896      * @param {Node/Array} node The node or Array of nodes to append
6897      * @return {Node} The appended node if single append, or null if an array was passed
6898      */
6899     appendChild : function(node){
6900         var multi = false;
6901         if(node instanceof Array){
6902             multi = node;
6903         }else if(arguments.length > 1){
6904             multi = arguments;
6905         }
6906         // if passed an array or multiple args do them one by one
6907         if(multi){
6908             for(var i = 0, len = multi.length; i < len; i++) {
6909                 this.appendChild(multi[i]);
6910             }
6911         }else{
6912             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6913                 return false;
6914             }
6915             var index = this.childNodes.length;
6916             var oldParent = node.parentNode;
6917             // it's a move, make sure we move it cleanly
6918             if(oldParent){
6919                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6920                     return false;
6921                 }
6922                 oldParent.removeChild(node);
6923             }
6924             index = this.childNodes.length;
6925             if(index == 0){
6926                 this.setFirstChild(node);
6927             }
6928             this.childNodes.push(node);
6929             node.parentNode = this;
6930             var ps = this.childNodes[index-1];
6931             if(ps){
6932                 node.previousSibling = ps;
6933                 ps.nextSibling = node;
6934             }else{
6935                 node.previousSibling = null;
6936             }
6937             node.nextSibling = null;
6938             this.setLastChild(node);
6939             node.setOwnerTree(this.getOwnerTree());
6940             this.fireEvent("append", this.ownerTree, this, node, index);
6941             if(oldParent){
6942                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6943             }
6944             return node;
6945         }
6946     },
6947
6948     /**
6949      * Removes a child node from this node.
6950      * @param {Node} node The node to remove
6951      * @return {Node} The removed node
6952      */
6953     removeChild : function(node){
6954         var index = this.childNodes.indexOf(node);
6955         if(index == -1){
6956             return false;
6957         }
6958         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6959             return false;
6960         }
6961
6962         // remove it from childNodes collection
6963         this.childNodes.splice(index, 1);
6964
6965         // update siblings
6966         if(node.previousSibling){
6967             node.previousSibling.nextSibling = node.nextSibling;
6968         }
6969         if(node.nextSibling){
6970             node.nextSibling.previousSibling = node.previousSibling;
6971         }
6972
6973         // update child refs
6974         if(this.firstChild == node){
6975             this.setFirstChild(node.nextSibling);
6976         }
6977         if(this.lastChild == node){
6978             this.setLastChild(node.previousSibling);
6979         }
6980
6981         node.setOwnerTree(null);
6982         // clear any references from the node
6983         node.parentNode = null;
6984         node.previousSibling = null;
6985         node.nextSibling = null;
6986         this.fireEvent("remove", this.ownerTree, this, node);
6987         return node;
6988     },
6989
6990     /**
6991      * Inserts the first node before the second node in this nodes childNodes collection.
6992      * @param {Node} node The node to insert
6993      * @param {Node} refNode The node to insert before (if null the node is appended)
6994      * @return {Node} The inserted node
6995      */
6996     insertBefore : function(node, refNode){
6997         if(!refNode){ // like standard Dom, refNode can be null for append
6998             return this.appendChild(node);
6999         }
7000         // nothing to do
7001         if(node == refNode){
7002             return false;
7003         }
7004
7005         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7006             return false;
7007         }
7008         var index = this.childNodes.indexOf(refNode);
7009         var oldParent = node.parentNode;
7010         var refIndex = index;
7011
7012         // when moving internally, indexes will change after remove
7013         if(oldParent == this && this.childNodes.indexOf(node) < index){
7014             refIndex--;
7015         }
7016
7017         // it's a move, make sure we move it cleanly
7018         if(oldParent){
7019             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7020                 return false;
7021             }
7022             oldParent.removeChild(node);
7023         }
7024         if(refIndex == 0){
7025             this.setFirstChild(node);
7026         }
7027         this.childNodes.splice(refIndex, 0, node);
7028         node.parentNode = this;
7029         var ps = this.childNodes[refIndex-1];
7030         if(ps){
7031             node.previousSibling = ps;
7032             ps.nextSibling = node;
7033         }else{
7034             node.previousSibling = null;
7035         }
7036         node.nextSibling = refNode;
7037         refNode.previousSibling = node;
7038         node.setOwnerTree(this.getOwnerTree());
7039         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7040         if(oldParent){
7041             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7042         }
7043         return node;
7044     },
7045
7046     /**
7047      * Returns the child node at the specified index.
7048      * @param {Number} index
7049      * @return {Node}
7050      */
7051     item : function(index){
7052         return this.childNodes[index];
7053     },
7054
7055     /**
7056      * Replaces one child node in this node with another.
7057      * @param {Node} newChild The replacement node
7058      * @param {Node} oldChild The node to replace
7059      * @return {Node} The replaced node
7060      */
7061     replaceChild : function(newChild, oldChild){
7062         this.insertBefore(newChild, oldChild);
7063         this.removeChild(oldChild);
7064         return oldChild;
7065     },
7066
7067     /**
7068      * Returns the index of a child node
7069      * @param {Node} node
7070      * @return {Number} The index of the node or -1 if it was not found
7071      */
7072     indexOf : function(child){
7073         return this.childNodes.indexOf(child);
7074     },
7075
7076     /**
7077      * Returns the tree this node is in.
7078      * @return {Tree}
7079      */
7080     getOwnerTree : function(){
7081         // if it doesn't have one, look for one
7082         if(!this.ownerTree){
7083             var p = this;
7084             while(p){
7085                 if(p.ownerTree){
7086                     this.ownerTree = p.ownerTree;
7087                     break;
7088                 }
7089                 p = p.parentNode;
7090             }
7091         }
7092         return this.ownerTree;
7093     },
7094
7095     /**
7096      * Returns depth of this node (the root node has a depth of 0)
7097      * @return {Number}
7098      */
7099     getDepth : function(){
7100         var depth = 0;
7101         var p = this;
7102         while(p.parentNode){
7103             ++depth;
7104             p = p.parentNode;
7105         }
7106         return depth;
7107     },
7108
7109     // private
7110     setOwnerTree : function(tree){
7111         // if it's move, we need to update everyone
7112         if(tree != this.ownerTree){
7113             if(this.ownerTree){
7114                 this.ownerTree.unregisterNode(this);
7115             }
7116             this.ownerTree = tree;
7117             var cs = this.childNodes;
7118             for(var i = 0, len = cs.length; i < len; i++) {
7119                 cs[i].setOwnerTree(tree);
7120             }
7121             if(tree){
7122                 tree.registerNode(this);
7123             }
7124         }
7125     },
7126
7127     /**
7128      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7129      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7130      * @return {String} The path
7131      */
7132     getPath : function(attr){
7133         attr = attr || "id";
7134         var p = this.parentNode;
7135         var b = [this.attributes[attr]];
7136         while(p){
7137             b.unshift(p.attributes[attr]);
7138             p = p.parentNode;
7139         }
7140         var sep = this.getOwnerTree().pathSeparator;
7141         return sep + b.join(sep);
7142     },
7143
7144     /**
7145      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7146      * function call will be the scope provided or the current node. The arguments to the function
7147      * will be the args provided or the current node. If the function returns false at any point,
7148      * the bubble is stopped.
7149      * @param {Function} fn The function to call
7150      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7151      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7152      */
7153     bubble : function(fn, scope, args){
7154         var p = this;
7155         while(p){
7156             if(fn.call(scope || p, args || p) === false){
7157                 break;
7158             }
7159             p = p.parentNode;
7160         }
7161     },
7162
7163     /**
7164      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7165      * function call will be the scope provided or the current node. The arguments to the function
7166      * will be the args provided or the current node. If the function returns false at any point,
7167      * the cascade is stopped on that branch.
7168      * @param {Function} fn The function to call
7169      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7170      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7171      */
7172     cascade : function(fn, scope, args){
7173         if(fn.call(scope || this, args || this) !== false){
7174             var cs = this.childNodes;
7175             for(var i = 0, len = cs.length; i < len; i++) {
7176                 cs[i].cascade(fn, scope, args);
7177             }
7178         }
7179     },
7180
7181     /**
7182      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7183      * function call will be the scope provided or the current node. The arguments to the function
7184      * will be the args provided or the current node. If the function returns false at any point,
7185      * the iteration stops.
7186      * @param {Function} fn The function to call
7187      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7188      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7189      */
7190     eachChild : function(fn, scope, args){
7191         var cs = this.childNodes;
7192         for(var i = 0, len = cs.length; i < len; i++) {
7193                 if(fn.call(scope || this, args || cs[i]) === false){
7194                     break;
7195                 }
7196         }
7197     },
7198
7199     /**
7200      * Finds the first child that has the attribute with the specified value.
7201      * @param {String} attribute The attribute name
7202      * @param {Mixed} value The value to search for
7203      * @return {Node} The found child or null if none was found
7204      */
7205     findChild : function(attribute, value){
7206         var cs = this.childNodes;
7207         for(var i = 0, len = cs.length; i < len; i++) {
7208                 if(cs[i].attributes[attribute] == value){
7209                     return cs[i];
7210                 }
7211         }
7212         return null;
7213     },
7214
7215     /**
7216      * Finds the first child by a custom function. The child matches if the function passed
7217      * returns true.
7218      * @param {Function} fn
7219      * @param {Object} scope (optional)
7220      * @return {Node} The found child or null if none was found
7221      */
7222     findChildBy : function(fn, scope){
7223         var cs = this.childNodes;
7224         for(var i = 0, len = cs.length; i < len; i++) {
7225                 if(fn.call(scope||cs[i], cs[i]) === true){
7226                     return cs[i];
7227                 }
7228         }
7229         return null;
7230     },
7231
7232     /**
7233      * Sorts this nodes children using the supplied sort function
7234      * @param {Function} fn
7235      * @param {Object} scope (optional)
7236      */
7237     sort : function(fn, scope){
7238         var cs = this.childNodes;
7239         var len = cs.length;
7240         if(len > 0){
7241             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7242             cs.sort(sortFn);
7243             for(var i = 0; i < len; i++){
7244                 var n = cs[i];
7245                 n.previousSibling = cs[i-1];
7246                 n.nextSibling = cs[i+1];
7247                 if(i == 0){
7248                     this.setFirstChild(n);
7249                 }
7250                 if(i == len-1){
7251                     this.setLastChild(n);
7252                 }
7253             }
7254         }
7255     },
7256
7257     /**
7258      * Returns true if this node is an ancestor (at any point) of the passed node.
7259      * @param {Node} node
7260      * @return {Boolean}
7261      */
7262     contains : function(node){
7263         return node.isAncestor(this);
7264     },
7265
7266     /**
7267      * Returns true if the passed node is an ancestor (at any point) of this node.
7268      * @param {Node} node
7269      * @return {Boolean}
7270      */
7271     isAncestor : function(node){
7272         var p = this.parentNode;
7273         while(p){
7274             if(p == node){
7275                 return true;
7276             }
7277             p = p.parentNode;
7278         }
7279         return false;
7280     },
7281
7282     toString : function(){
7283         return "[Node"+(this.id?" "+this.id:"")+"]";
7284     }
7285 });/*
7286  * Based on:
7287  * Ext JS Library 1.1.1
7288  * Copyright(c) 2006-2007, Ext JS, LLC.
7289  *
7290  * Originally Released Under LGPL - original licence link has changed is not relivant.
7291  *
7292  * Fork - LGPL
7293  * <script type="text/javascript">
7294  */
7295  (function(){ 
7296 /**
7297  * @class Roo.Layer
7298  * @extends Roo.Element
7299  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7300  * automatic maintaining of shadow/shim positions.
7301  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7302  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7303  * you can pass a string with a CSS class name. False turns off the shadow.
7304  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7305  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7306  * @cfg {String} cls CSS class to add to the element
7307  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7308  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7309  * @constructor
7310  * @param {Object} config An object with config options.
7311  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7312  */
7313
7314 Roo.Layer = function(config, existingEl){
7315     config = config || {};
7316     var dh = Roo.DomHelper;
7317     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7318     if(existingEl){
7319         this.dom = Roo.getDom(existingEl);
7320     }
7321     if(!this.dom){
7322         var o = config.dh || {tag: "div", cls: "x-layer"};
7323         this.dom = dh.append(pel, o);
7324     }
7325     if(config.cls){
7326         this.addClass(config.cls);
7327     }
7328     this.constrain = config.constrain !== false;
7329     this.visibilityMode = Roo.Element.VISIBILITY;
7330     if(config.id){
7331         this.id = this.dom.id = config.id;
7332     }else{
7333         this.id = Roo.id(this.dom);
7334     }
7335     this.zindex = config.zindex || this.getZIndex();
7336     this.position("absolute", this.zindex);
7337     if(config.shadow){
7338         this.shadowOffset = config.shadowOffset || 4;
7339         this.shadow = new Roo.Shadow({
7340             offset : this.shadowOffset,
7341             mode : config.shadow
7342         });
7343     }else{
7344         this.shadowOffset = 0;
7345     }
7346     this.useShim = config.shim !== false && Roo.useShims;
7347     this.useDisplay = config.useDisplay;
7348     this.hide();
7349 };
7350
7351 var supr = Roo.Element.prototype;
7352
7353 // shims are shared among layer to keep from having 100 iframes
7354 var shims = [];
7355
7356 Roo.extend(Roo.Layer, Roo.Element, {
7357
7358     getZIndex : function(){
7359         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7360     },
7361
7362     getShim : function(){
7363         if(!this.useShim){
7364             return null;
7365         }
7366         if(this.shim){
7367             return this.shim;
7368         }
7369         var shim = shims.shift();
7370         if(!shim){
7371             shim = this.createShim();
7372             shim.enableDisplayMode('block');
7373             shim.dom.style.display = 'none';
7374             shim.dom.style.visibility = 'visible';
7375         }
7376         var pn = this.dom.parentNode;
7377         if(shim.dom.parentNode != pn){
7378             pn.insertBefore(shim.dom, this.dom);
7379         }
7380         shim.setStyle('z-index', this.getZIndex()-2);
7381         this.shim = shim;
7382         return shim;
7383     },
7384
7385     hideShim : function(){
7386         if(this.shim){
7387             this.shim.setDisplayed(false);
7388             shims.push(this.shim);
7389             delete this.shim;
7390         }
7391     },
7392
7393     disableShadow : function(){
7394         if(this.shadow){
7395             this.shadowDisabled = true;
7396             this.shadow.hide();
7397             this.lastShadowOffset = this.shadowOffset;
7398             this.shadowOffset = 0;
7399         }
7400     },
7401
7402     enableShadow : function(show){
7403         if(this.shadow){
7404             this.shadowDisabled = false;
7405             this.shadowOffset = this.lastShadowOffset;
7406             delete this.lastShadowOffset;
7407             if(show){
7408                 this.sync(true);
7409             }
7410         }
7411     },
7412
7413     // private
7414     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7415     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7416     sync : function(doShow){
7417         var sw = this.shadow;
7418         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7419             var sh = this.getShim();
7420
7421             var w = this.getWidth(),
7422                 h = this.getHeight();
7423
7424             var l = this.getLeft(true),
7425                 t = this.getTop(true);
7426
7427             if(sw && !this.shadowDisabled){
7428                 if(doShow && !sw.isVisible()){
7429                     sw.show(this);
7430                 }else{
7431                     sw.realign(l, t, w, h);
7432                 }
7433                 if(sh){
7434                     if(doShow){
7435                        sh.show();
7436                     }
7437                     // fit the shim behind the shadow, so it is shimmed too
7438                     var a = sw.adjusts, s = sh.dom.style;
7439                     s.left = (Math.min(l, l+a.l))+"px";
7440                     s.top = (Math.min(t, t+a.t))+"px";
7441                     s.width = (w+a.w)+"px";
7442                     s.height = (h+a.h)+"px";
7443                 }
7444             }else if(sh){
7445                 if(doShow){
7446                    sh.show();
7447                 }
7448                 sh.setSize(w, h);
7449                 sh.setLeftTop(l, t);
7450             }
7451             
7452         }
7453     },
7454
7455     // private
7456     destroy : function(){
7457         this.hideShim();
7458         if(this.shadow){
7459             this.shadow.hide();
7460         }
7461         this.removeAllListeners();
7462         var pn = this.dom.parentNode;
7463         if(pn){
7464             pn.removeChild(this.dom);
7465         }
7466         Roo.Element.uncache(this.id);
7467     },
7468
7469     remove : function(){
7470         this.destroy();
7471     },
7472
7473     // private
7474     beginUpdate : function(){
7475         this.updating = true;
7476     },
7477
7478     // private
7479     endUpdate : function(){
7480         this.updating = false;
7481         this.sync(true);
7482     },
7483
7484     // private
7485     hideUnders : function(negOffset){
7486         if(this.shadow){
7487             this.shadow.hide();
7488         }
7489         this.hideShim();
7490     },
7491
7492     // private
7493     constrainXY : function(){
7494         if(this.constrain){
7495             var vw = Roo.lib.Dom.getViewWidth(),
7496                 vh = Roo.lib.Dom.getViewHeight();
7497             var s = Roo.get(document).getScroll();
7498
7499             var xy = this.getXY();
7500             var x = xy[0], y = xy[1];   
7501             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7502             // only move it if it needs it
7503             var moved = false;
7504             // first validate right/bottom
7505             if((x + w) > vw+s.left){
7506                 x = vw - w - this.shadowOffset;
7507                 moved = true;
7508             }
7509             if((y + h) > vh+s.top){
7510                 y = vh - h - this.shadowOffset;
7511                 moved = true;
7512             }
7513             // then make sure top/left isn't negative
7514             if(x < s.left){
7515                 x = s.left;
7516                 moved = true;
7517             }
7518             if(y < s.top){
7519                 y = s.top;
7520                 moved = true;
7521             }
7522             if(moved){
7523                 if(this.avoidY){
7524                     var ay = this.avoidY;
7525                     if(y <= ay && (y+h) >= ay){
7526                         y = ay-h-5;   
7527                     }
7528                 }
7529                 xy = [x, y];
7530                 this.storeXY(xy);
7531                 supr.setXY.call(this, xy);
7532                 this.sync();
7533             }
7534         }
7535     },
7536
7537     isVisible : function(){
7538         return this.visible;    
7539     },
7540
7541     // private
7542     showAction : function(){
7543         this.visible = true; // track visibility to prevent getStyle calls
7544         if(this.useDisplay === true){
7545             this.setDisplayed("");
7546         }else if(this.lastXY){
7547             supr.setXY.call(this, this.lastXY);
7548         }else if(this.lastLT){
7549             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7550         }
7551     },
7552
7553     // private
7554     hideAction : function(){
7555         this.visible = false;
7556         if(this.useDisplay === true){
7557             this.setDisplayed(false);
7558         }else{
7559             this.setLeftTop(-10000,-10000);
7560         }
7561     },
7562
7563     // overridden Element method
7564     setVisible : function(v, a, d, c, e){
7565         if(v){
7566             this.showAction();
7567         }
7568         if(a && v){
7569             var cb = function(){
7570                 this.sync(true);
7571                 if(c){
7572                     c();
7573                 }
7574             }.createDelegate(this);
7575             supr.setVisible.call(this, true, true, d, cb, e);
7576         }else{
7577             if(!v){
7578                 this.hideUnders(true);
7579             }
7580             var cb = c;
7581             if(a){
7582                 cb = function(){
7583                     this.hideAction();
7584                     if(c){
7585                         c();
7586                     }
7587                 }.createDelegate(this);
7588             }
7589             supr.setVisible.call(this, v, a, d, cb, e);
7590             if(v){
7591                 this.sync(true);
7592             }else if(!a){
7593                 this.hideAction();
7594             }
7595         }
7596     },
7597
7598     storeXY : function(xy){
7599         delete this.lastLT;
7600         this.lastXY = xy;
7601     },
7602
7603     storeLeftTop : function(left, top){
7604         delete this.lastXY;
7605         this.lastLT = [left, top];
7606     },
7607
7608     // private
7609     beforeFx : function(){
7610         this.beforeAction();
7611         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7612     },
7613
7614     // private
7615     afterFx : function(){
7616         Roo.Layer.superclass.afterFx.apply(this, arguments);
7617         this.sync(this.isVisible());
7618     },
7619
7620     // private
7621     beforeAction : function(){
7622         if(!this.updating && this.shadow){
7623             this.shadow.hide();
7624         }
7625     },
7626
7627     // overridden Element method
7628     setLeft : function(left){
7629         this.storeLeftTop(left, this.getTop(true));
7630         supr.setLeft.apply(this, arguments);
7631         this.sync();
7632     },
7633
7634     setTop : function(top){
7635         this.storeLeftTop(this.getLeft(true), top);
7636         supr.setTop.apply(this, arguments);
7637         this.sync();
7638     },
7639
7640     setLeftTop : function(left, top){
7641         this.storeLeftTop(left, top);
7642         supr.setLeftTop.apply(this, arguments);
7643         this.sync();
7644     },
7645
7646     setXY : function(xy, a, d, c, e){
7647         this.fixDisplay();
7648         this.beforeAction();
7649         this.storeXY(xy);
7650         var cb = this.createCB(c);
7651         supr.setXY.call(this, xy, a, d, cb, e);
7652         if(!a){
7653             cb();
7654         }
7655     },
7656
7657     // private
7658     createCB : function(c){
7659         var el = this;
7660         return function(){
7661             el.constrainXY();
7662             el.sync(true);
7663             if(c){
7664                 c();
7665             }
7666         };
7667     },
7668
7669     // overridden Element method
7670     setX : function(x, a, d, c, e){
7671         this.setXY([x, this.getY()], a, d, c, e);
7672     },
7673
7674     // overridden Element method
7675     setY : function(y, a, d, c, e){
7676         this.setXY([this.getX(), y], a, d, c, e);
7677     },
7678
7679     // overridden Element method
7680     setSize : function(w, h, a, d, c, e){
7681         this.beforeAction();
7682         var cb = this.createCB(c);
7683         supr.setSize.call(this, w, h, a, d, cb, e);
7684         if(!a){
7685             cb();
7686         }
7687     },
7688
7689     // overridden Element method
7690     setWidth : function(w, a, d, c, e){
7691         this.beforeAction();
7692         var cb = this.createCB(c);
7693         supr.setWidth.call(this, w, a, d, cb, e);
7694         if(!a){
7695             cb();
7696         }
7697     },
7698
7699     // overridden Element method
7700     setHeight : function(h, a, d, c, e){
7701         this.beforeAction();
7702         var cb = this.createCB(c);
7703         supr.setHeight.call(this, h, a, d, cb, e);
7704         if(!a){
7705             cb();
7706         }
7707     },
7708
7709     // overridden Element method
7710     setBounds : function(x, y, w, h, a, d, c, e){
7711         this.beforeAction();
7712         var cb = this.createCB(c);
7713         if(!a){
7714             this.storeXY([x, y]);
7715             supr.setXY.call(this, [x, y]);
7716             supr.setSize.call(this, w, h, a, d, cb, e);
7717             cb();
7718         }else{
7719             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7720         }
7721         return this;
7722     },
7723     
7724     /**
7725      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7726      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7727      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7728      * @param {Number} zindex The new z-index to set
7729      * @return {this} The Layer
7730      */
7731     setZIndex : function(zindex){
7732         this.zindex = zindex;
7733         this.setStyle("z-index", zindex + 2);
7734         if(this.shadow){
7735             this.shadow.setZIndex(zindex + 1);
7736         }
7737         if(this.shim){
7738             this.shim.setStyle("z-index", zindex);
7739         }
7740     }
7741 });
7742 })();/*
7743  * Based on:
7744  * Ext JS Library 1.1.1
7745  * Copyright(c) 2006-2007, Ext JS, LLC.
7746  *
7747  * Originally Released Under LGPL - original licence link has changed is not relivant.
7748  *
7749  * Fork - LGPL
7750  * <script type="text/javascript">
7751  */
7752
7753
7754 /**
7755  * @class Roo.Shadow
7756  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7757  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7758  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7759  * @constructor
7760  * Create a new Shadow
7761  * @param {Object} config The config object
7762  */
7763 Roo.Shadow = function(config){
7764     Roo.apply(this, config);
7765     if(typeof this.mode != "string"){
7766         this.mode = this.defaultMode;
7767     }
7768     var o = this.offset, a = {h: 0};
7769     var rad = Math.floor(this.offset/2);
7770     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7771         case "drop":
7772             a.w = 0;
7773             a.l = a.t = o;
7774             a.t -= 1;
7775             if(Roo.isIE){
7776                 a.l -= this.offset + rad;
7777                 a.t -= this.offset + rad;
7778                 a.w -= rad;
7779                 a.h -= rad;
7780                 a.t += 1;
7781             }
7782         break;
7783         case "sides":
7784             a.w = (o*2);
7785             a.l = -o;
7786             a.t = o-1;
7787             if(Roo.isIE){
7788                 a.l -= (this.offset - rad);
7789                 a.t -= this.offset + rad;
7790                 a.l += 1;
7791                 a.w -= (this.offset - rad)*2;
7792                 a.w -= rad + 1;
7793                 a.h -= 1;
7794             }
7795         break;
7796         case "frame":
7797             a.w = a.h = (o*2);
7798             a.l = a.t = -o;
7799             a.t += 1;
7800             a.h -= 2;
7801             if(Roo.isIE){
7802                 a.l -= (this.offset - rad);
7803                 a.t -= (this.offset - rad);
7804                 a.l += 1;
7805                 a.w -= (this.offset + rad + 1);
7806                 a.h -= (this.offset + rad);
7807                 a.h += 1;
7808             }
7809         break;
7810     };
7811
7812     this.adjusts = a;
7813 };
7814
7815 Roo.Shadow.prototype = {
7816     /**
7817      * @cfg {String} mode
7818      * The shadow display mode.  Supports the following options:<br />
7819      * sides: Shadow displays on both sides and bottom only<br />
7820      * frame: Shadow displays equally on all four sides<br />
7821      * drop: Traditional bottom-right drop shadow (default)
7822      */
7823     /**
7824      * @cfg {String} offset
7825      * The number of pixels to offset the shadow from the element (defaults to 4)
7826      */
7827     offset: 4,
7828
7829     // private
7830     defaultMode: "drop",
7831
7832     /**
7833      * Displays the shadow under the target element
7834      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7835      */
7836     show : function(target){
7837         target = Roo.get(target);
7838         if(!this.el){
7839             this.el = Roo.Shadow.Pool.pull();
7840             if(this.el.dom.nextSibling != target.dom){
7841                 this.el.insertBefore(target);
7842             }
7843         }
7844         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7845         if(Roo.isIE){
7846             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7847         }
7848         this.realign(
7849             target.getLeft(true),
7850             target.getTop(true),
7851             target.getWidth(),
7852             target.getHeight()
7853         );
7854         this.el.dom.style.display = "block";
7855     },
7856
7857     /**
7858      * Returns true if the shadow is visible, else false
7859      */
7860     isVisible : function(){
7861         return this.el ? true : false;  
7862     },
7863
7864     /**
7865      * Direct alignment when values are already available. Show must be called at least once before
7866      * calling this method to ensure it is initialized.
7867      * @param {Number} left The target element left position
7868      * @param {Number} top The target element top position
7869      * @param {Number} width The target element width
7870      * @param {Number} height The target element height
7871      */
7872     realign : function(l, t, w, h){
7873         if(!this.el){
7874             return;
7875         }
7876         var a = this.adjusts, d = this.el.dom, s = d.style;
7877         var iea = 0;
7878         s.left = (l+a.l)+"px";
7879         s.top = (t+a.t)+"px";
7880         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7881  
7882         if(s.width != sws || s.height != shs){
7883             s.width = sws;
7884             s.height = shs;
7885             if(!Roo.isIE){
7886                 var cn = d.childNodes;
7887                 var sww = Math.max(0, (sw-12))+"px";
7888                 cn[0].childNodes[1].style.width = sww;
7889                 cn[1].childNodes[1].style.width = sww;
7890                 cn[2].childNodes[1].style.width = sww;
7891                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7892             }
7893         }
7894     },
7895
7896     /**
7897      * Hides this shadow
7898      */
7899     hide : function(){
7900         if(this.el){
7901             this.el.dom.style.display = "none";
7902             Roo.Shadow.Pool.push(this.el);
7903             delete this.el;
7904         }
7905     },
7906
7907     /**
7908      * Adjust the z-index of this shadow
7909      * @param {Number} zindex The new z-index
7910      */
7911     setZIndex : function(z){
7912         this.zIndex = z;
7913         if(this.el){
7914             this.el.setStyle("z-index", z);
7915         }
7916     }
7917 };
7918
7919 // Private utility class that manages the internal Shadow cache
7920 Roo.Shadow.Pool = function(){
7921     var p = [];
7922     var markup = Roo.isIE ?
7923                  '<div class="x-ie-shadow"></div>' :
7924                  '<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>';
7925     return {
7926         pull : function(){
7927             var sh = p.shift();
7928             if(!sh){
7929                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7930                 sh.autoBoxAdjust = false;
7931             }
7932             return sh;
7933         },
7934
7935         push : function(sh){
7936             p.push(sh);
7937         }
7938     };
7939 }();/*
7940  * Based on:
7941  * Ext JS Library 1.1.1
7942  * Copyright(c) 2006-2007, Ext JS, LLC.
7943  *
7944  * Originally Released Under LGPL - original licence link has changed is not relivant.
7945  *
7946  * Fork - LGPL
7947  * <script type="text/javascript">
7948  */
7949
7950
7951 /**
7952  * @class Roo.SplitBar
7953  * @extends Roo.util.Observable
7954  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7955  * <br><br>
7956  * Usage:
7957  * <pre><code>
7958 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7959                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7960 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7961 split.minSize = 100;
7962 split.maxSize = 600;
7963 split.animate = true;
7964 split.on('moved', splitterMoved);
7965 </code></pre>
7966  * @constructor
7967  * Create a new SplitBar
7968  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7969  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7970  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7971  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7972                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7973                         position of the SplitBar).
7974  */
7975 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7976     
7977     /** @private */
7978     this.el = Roo.get(dragElement, true);
7979     this.el.dom.unselectable = "on";
7980     /** @private */
7981     this.resizingEl = Roo.get(resizingElement, true);
7982
7983     /**
7984      * @private
7985      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7986      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7987      * @type Number
7988      */
7989     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7990     
7991     /**
7992      * The minimum size of the resizing element. (Defaults to 0)
7993      * @type Number
7994      */
7995     this.minSize = 0;
7996     
7997     /**
7998      * The maximum size of the resizing element. (Defaults to 2000)
7999      * @type Number
8000      */
8001     this.maxSize = 2000;
8002     
8003     /**
8004      * Whether to animate the transition to the new size
8005      * @type Boolean
8006      */
8007     this.animate = false;
8008     
8009     /**
8010      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8011      * @type Boolean
8012      */
8013     this.useShim = false;
8014     
8015     /** @private */
8016     this.shim = null;
8017     
8018     if(!existingProxy){
8019         /** @private */
8020         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8021     }else{
8022         this.proxy = Roo.get(existingProxy).dom;
8023     }
8024     /** @private */
8025     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8026     
8027     /** @private */
8028     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8029     
8030     /** @private */
8031     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8032     
8033     /** @private */
8034     this.dragSpecs = {};
8035     
8036     /**
8037      * @private The adapter to use to positon and resize elements
8038      */
8039     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8040     this.adapter.init(this);
8041     
8042     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8043         /** @private */
8044         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8045         this.el.addClass("x-splitbar-h");
8046     }else{
8047         /** @private */
8048         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8049         this.el.addClass("x-splitbar-v");
8050     }
8051     
8052     this.addEvents({
8053         /**
8054          * @event resize
8055          * Fires when the splitter is moved (alias for {@link #event-moved})
8056          * @param {Roo.SplitBar} this
8057          * @param {Number} newSize the new width or height
8058          */
8059         "resize" : true,
8060         /**
8061          * @event moved
8062          * Fires when the splitter is moved
8063          * @param {Roo.SplitBar} this
8064          * @param {Number} newSize the new width or height
8065          */
8066         "moved" : true,
8067         /**
8068          * @event beforeresize
8069          * Fires before the splitter is dragged
8070          * @param {Roo.SplitBar} this
8071          */
8072         "beforeresize" : true,
8073
8074         "beforeapply" : true
8075     });
8076
8077     Roo.util.Observable.call(this);
8078 };
8079
8080 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8081     onStartProxyDrag : function(x, y){
8082         this.fireEvent("beforeresize", this);
8083         if(!this.overlay){
8084             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8085             o.unselectable();
8086             o.enableDisplayMode("block");
8087             // all splitbars share the same overlay
8088             Roo.SplitBar.prototype.overlay = o;
8089         }
8090         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8091         this.overlay.show();
8092         Roo.get(this.proxy).setDisplayed("block");
8093         var size = this.adapter.getElementSize(this);
8094         this.activeMinSize = this.getMinimumSize();;
8095         this.activeMaxSize = this.getMaximumSize();;
8096         var c1 = size - this.activeMinSize;
8097         var c2 = Math.max(this.activeMaxSize - size, 0);
8098         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8099             this.dd.resetConstraints();
8100             this.dd.setXConstraint(
8101                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8102                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8103             );
8104             this.dd.setYConstraint(0, 0);
8105         }else{
8106             this.dd.resetConstraints();
8107             this.dd.setXConstraint(0, 0);
8108             this.dd.setYConstraint(
8109                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8110                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8111             );
8112          }
8113         this.dragSpecs.startSize = size;
8114         this.dragSpecs.startPoint = [x, y];
8115         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8116     },
8117     
8118     /** 
8119      * @private Called after the drag operation by the DDProxy
8120      */
8121     onEndProxyDrag : function(e){
8122         Roo.get(this.proxy).setDisplayed(false);
8123         var endPoint = Roo.lib.Event.getXY(e);
8124         if(this.overlay){
8125             this.overlay.hide();
8126         }
8127         var newSize;
8128         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8129             newSize = this.dragSpecs.startSize + 
8130                 (this.placement == Roo.SplitBar.LEFT ?
8131                     endPoint[0] - this.dragSpecs.startPoint[0] :
8132                     this.dragSpecs.startPoint[0] - endPoint[0]
8133                 );
8134         }else{
8135             newSize = this.dragSpecs.startSize + 
8136                 (this.placement == Roo.SplitBar.TOP ?
8137                     endPoint[1] - this.dragSpecs.startPoint[1] :
8138                     this.dragSpecs.startPoint[1] - endPoint[1]
8139                 );
8140         }
8141         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8142         if(newSize != this.dragSpecs.startSize){
8143             if(this.fireEvent('beforeapply', this, newSize) !== false){
8144                 this.adapter.setElementSize(this, newSize);
8145                 this.fireEvent("moved", this, newSize);
8146                 this.fireEvent("resize", this, newSize);
8147             }
8148         }
8149     },
8150     
8151     /**
8152      * Get the adapter this SplitBar uses
8153      * @return The adapter object
8154      */
8155     getAdapter : function(){
8156         return this.adapter;
8157     },
8158     
8159     /**
8160      * Set the adapter this SplitBar uses
8161      * @param {Object} adapter A SplitBar adapter object
8162      */
8163     setAdapter : function(adapter){
8164         this.adapter = adapter;
8165         this.adapter.init(this);
8166     },
8167     
8168     /**
8169      * Gets the minimum size for the resizing element
8170      * @return {Number} The minimum size
8171      */
8172     getMinimumSize : function(){
8173         return this.minSize;
8174     },
8175     
8176     /**
8177      * Sets the minimum size for the resizing element
8178      * @param {Number} minSize The minimum size
8179      */
8180     setMinimumSize : function(minSize){
8181         this.minSize = minSize;
8182     },
8183     
8184     /**
8185      * Gets the maximum size for the resizing element
8186      * @return {Number} The maximum size
8187      */
8188     getMaximumSize : function(){
8189         return this.maxSize;
8190     },
8191     
8192     /**
8193      * Sets the maximum size for the resizing element
8194      * @param {Number} maxSize The maximum size
8195      */
8196     setMaximumSize : function(maxSize){
8197         this.maxSize = maxSize;
8198     },
8199     
8200     /**
8201      * Sets the initialize size for the resizing element
8202      * @param {Number} size The initial size
8203      */
8204     setCurrentSize : function(size){
8205         var oldAnimate = this.animate;
8206         this.animate = false;
8207         this.adapter.setElementSize(this, size);
8208         this.animate = oldAnimate;
8209     },
8210     
8211     /**
8212      * Destroy this splitbar. 
8213      * @param {Boolean} removeEl True to remove the element
8214      */
8215     destroy : function(removeEl){
8216         if(this.shim){
8217             this.shim.remove();
8218         }
8219         this.dd.unreg();
8220         this.proxy.parentNode.removeChild(this.proxy);
8221         if(removeEl){
8222             this.el.remove();
8223         }
8224     }
8225 });
8226
8227 /**
8228  * @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.
8229  */
8230 Roo.SplitBar.createProxy = function(dir){
8231     var proxy = new Roo.Element(document.createElement("div"));
8232     proxy.unselectable();
8233     var cls = 'x-splitbar-proxy';
8234     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8235     document.body.appendChild(proxy.dom);
8236     return proxy.dom;
8237 };
8238
8239 /** 
8240  * @class Roo.SplitBar.BasicLayoutAdapter
8241  * Default Adapter. It assumes the splitter and resizing element are not positioned
8242  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8243  */
8244 Roo.SplitBar.BasicLayoutAdapter = function(){
8245 };
8246
8247 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8248     // do nothing for now
8249     init : function(s){
8250     
8251     },
8252     /**
8253      * Called before drag operations to get the current size of the resizing element. 
8254      * @param {Roo.SplitBar} s The SplitBar using this adapter
8255      */
8256      getElementSize : function(s){
8257         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8258             return s.resizingEl.getWidth();
8259         }else{
8260             return s.resizingEl.getHeight();
8261         }
8262     },
8263     
8264     /**
8265      * Called after drag operations to set the size of the resizing element.
8266      * @param {Roo.SplitBar} s The SplitBar using this adapter
8267      * @param {Number} newSize The new size to set
8268      * @param {Function} onComplete A function to be invoked when resizing is complete
8269      */
8270     setElementSize : function(s, newSize, onComplete){
8271         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8272             if(!s.animate){
8273                 s.resizingEl.setWidth(newSize);
8274                 if(onComplete){
8275                     onComplete(s, newSize);
8276                 }
8277             }else{
8278                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8279             }
8280         }else{
8281             
8282             if(!s.animate){
8283                 s.resizingEl.setHeight(newSize);
8284                 if(onComplete){
8285                     onComplete(s, newSize);
8286                 }
8287             }else{
8288                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8289             }
8290         }
8291     }
8292 };
8293
8294 /** 
8295  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8296  * @extends Roo.SplitBar.BasicLayoutAdapter
8297  * Adapter that  moves the splitter element to align with the resized sizing element. 
8298  * Used with an absolute positioned SplitBar.
8299  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8300  * document.body, make sure you assign an id to the body element.
8301  */
8302 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8303     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8304     this.container = Roo.get(container);
8305 };
8306
8307 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8308     init : function(s){
8309         this.basic.init(s);
8310     },
8311     
8312     getElementSize : function(s){
8313         return this.basic.getElementSize(s);
8314     },
8315     
8316     setElementSize : function(s, newSize, onComplete){
8317         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8318     },
8319     
8320     moveSplitter : function(s){
8321         var yes = Roo.SplitBar;
8322         switch(s.placement){
8323             case yes.LEFT:
8324                 s.el.setX(s.resizingEl.getRight());
8325                 break;
8326             case yes.RIGHT:
8327                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8328                 break;
8329             case yes.TOP:
8330                 s.el.setY(s.resizingEl.getBottom());
8331                 break;
8332             case yes.BOTTOM:
8333                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8334                 break;
8335         }
8336     }
8337 };
8338
8339 /**
8340  * Orientation constant - Create a vertical SplitBar
8341  * @static
8342  * @type Number
8343  */
8344 Roo.SplitBar.VERTICAL = 1;
8345
8346 /**
8347  * Orientation constant - Create a horizontal SplitBar
8348  * @static
8349  * @type Number
8350  */
8351 Roo.SplitBar.HORIZONTAL = 2;
8352
8353 /**
8354  * Placement constant - The resizing element is to the left of the splitter element
8355  * @static
8356  * @type Number
8357  */
8358 Roo.SplitBar.LEFT = 1;
8359
8360 /**
8361  * Placement constant - The resizing element is to the right of the splitter element
8362  * @static
8363  * @type Number
8364  */
8365 Roo.SplitBar.RIGHT = 2;
8366
8367 /**
8368  * Placement constant - The resizing element is positioned above the splitter element
8369  * @static
8370  * @type Number
8371  */
8372 Roo.SplitBar.TOP = 3;
8373
8374 /**
8375  * Placement constant - The resizing element is positioned under splitter element
8376  * @static
8377  * @type Number
8378  */
8379 Roo.SplitBar.BOTTOM = 4;
8380 /*
8381  * Based on:
8382  * Ext JS Library 1.1.1
8383  * Copyright(c) 2006-2007, Ext JS, LLC.
8384  *
8385  * Originally Released Under LGPL - original licence link has changed is not relivant.
8386  *
8387  * Fork - LGPL
8388  * <script type="text/javascript">
8389  */
8390
8391 /**
8392  * @class Roo.View
8393  * @extends Roo.util.Observable
8394  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8395  * This class also supports single and multi selection modes. <br>
8396  * Create a data model bound view:
8397  <pre><code>
8398  var store = new Roo.data.Store(...);
8399
8400  var view = new Roo.View({
8401     el : "my-element",
8402     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8403  
8404     singleSelect: true,
8405     selectedClass: "ydataview-selected",
8406     store: store
8407  });
8408
8409  // listen for node click?
8410  view.on("click", function(vw, index, node, e){
8411  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8412  });
8413
8414  // load XML data
8415  dataModel.load("foobar.xml");
8416  </code></pre>
8417  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8418  * <br><br>
8419  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8420  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8421  * 
8422  * Note: old style constructor is still suported (container, template, config)
8423  * 
8424  * @constructor
8425  * Create a new View
8426  * @param {Object} config The config object
8427  * 
8428  */
8429 Roo.View = function(config, depreciated_tpl, depreciated_config){
8430     
8431     this.parent = false;
8432     
8433     if (typeof(depreciated_tpl) == 'undefined') {
8434         // new way.. - universal constructor.
8435         Roo.apply(this, config);
8436         this.el  = Roo.get(this.el);
8437     } else {
8438         // old format..
8439         this.el  = Roo.get(config);
8440         this.tpl = depreciated_tpl;
8441         Roo.apply(this, depreciated_config);
8442     }
8443     this.wrapEl  = this.el.wrap().wrap();
8444     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8445     
8446     
8447     if(typeof(this.tpl) == "string"){
8448         this.tpl = new Roo.Template(this.tpl);
8449     } else {
8450         // support xtype ctors..
8451         this.tpl = new Roo.factory(this.tpl, Roo);
8452     }
8453     
8454     
8455     this.tpl.compile();
8456     
8457     /** @private */
8458     this.addEvents({
8459         /**
8460          * @event beforeclick
8461          * Fires before a click is processed. Returns false to cancel the default action.
8462          * @param {Roo.View} this
8463          * @param {Number} index The index of the target node
8464          * @param {HTMLElement} node The target node
8465          * @param {Roo.EventObject} e The raw event object
8466          */
8467             "beforeclick" : true,
8468         /**
8469          * @event click
8470          * Fires when a template node is clicked.
8471          * @param {Roo.View} this
8472          * @param {Number} index The index of the target node
8473          * @param {HTMLElement} node The target node
8474          * @param {Roo.EventObject} e The raw event object
8475          */
8476             "click" : true,
8477         /**
8478          * @event dblclick
8479          * Fires when a template node is double clicked.
8480          * @param {Roo.View} this
8481          * @param {Number} index The index of the target node
8482          * @param {HTMLElement} node The target node
8483          * @param {Roo.EventObject} e The raw event object
8484          */
8485             "dblclick" : true,
8486         /**
8487          * @event contextmenu
8488          * Fires when a template node is right clicked.
8489          * @param {Roo.View} this
8490          * @param {Number} index The index of the target node
8491          * @param {HTMLElement} node The target node
8492          * @param {Roo.EventObject} e The raw event object
8493          */
8494             "contextmenu" : true,
8495         /**
8496          * @event selectionchange
8497          * Fires when the selected nodes change.
8498          * @param {Roo.View} this
8499          * @param {Array} selections Array of the selected nodes
8500          */
8501             "selectionchange" : true,
8502     
8503         /**
8504          * @event beforeselect
8505          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8506          * @param {Roo.View} this
8507          * @param {HTMLElement} node The node to be selected
8508          * @param {Array} selections Array of currently selected nodes
8509          */
8510             "beforeselect" : true,
8511         /**
8512          * @event preparedata
8513          * Fires on every row to render, to allow you to change the data.
8514          * @param {Roo.View} this
8515          * @param {Object} data to be rendered (change this)
8516          */
8517           "preparedata" : true
8518           
8519           
8520         });
8521
8522
8523
8524     this.el.on({
8525         "click": this.onClick,
8526         "dblclick": this.onDblClick,
8527         "contextmenu": this.onContextMenu,
8528         scope:this
8529     });
8530
8531     this.selections = [];
8532     this.nodes = [];
8533     this.cmp = new Roo.CompositeElementLite([]);
8534     if(this.store){
8535         this.store = Roo.factory(this.store, Roo.data);
8536         this.setStore(this.store, true);
8537     }
8538     
8539     if ( this.footer && this.footer.xtype) {
8540            
8541          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8542         
8543         this.footer.dataSource = this.store
8544         this.footer.container = fctr;
8545         this.footer = Roo.factory(this.footer, Roo);
8546         fctr.insertFirst(this.el);
8547         
8548         // this is a bit insane - as the paging toolbar seems to detach the el..
8549 //        dom.parentNode.parentNode.parentNode
8550          // they get detached?
8551     }
8552     
8553     
8554     Roo.View.superclass.constructor.call(this);
8555     
8556     
8557 };
8558
8559 Roo.extend(Roo.View, Roo.util.Observable, {
8560     
8561      /**
8562      * @cfg {Roo.data.Store} store Data store to load data from.
8563      */
8564     store : false,
8565     
8566     /**
8567      * @cfg {String|Roo.Element} el The container element.
8568      */
8569     el : '',
8570     
8571     /**
8572      * @cfg {String|Roo.Template} tpl The template used by this View 
8573      */
8574     tpl : false,
8575     /**
8576      * @cfg {String} dataName the named area of the template to use as the data area
8577      *                          Works with domtemplates roo-name="name"
8578      */
8579     dataName: false,
8580     /**
8581      * @cfg {String} selectedClass The css class to add to selected nodes
8582      */
8583     selectedClass : "x-view-selected",
8584      /**
8585      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8586      */
8587     emptyText : "",
8588     
8589     /**
8590      * @cfg {String} text to display on mask (default Loading)
8591      */
8592     mask : false,
8593     /**
8594      * @cfg {Boolean} multiSelect Allow multiple selection
8595      */
8596     multiSelect : false,
8597     /**
8598      * @cfg {Boolean} singleSelect Allow single selection
8599      */
8600     singleSelect:  false,
8601     
8602     /**
8603      * @cfg {Boolean} toggleSelect - selecting 
8604      */
8605     toggleSelect : false,
8606     
8607     /**
8608      * @cfg {Boolean} tickable - selecting 
8609      */
8610     tickable : false,
8611     
8612     /**
8613      * Returns the element this view is bound to.
8614      * @return {Roo.Element}
8615      */
8616     getEl : function(){
8617         return this.wrapEl;
8618     },
8619     
8620     
8621
8622     /**
8623      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8624      */
8625     refresh : function(){
8626         Roo.log('refresh');
8627         var t = this.tpl;
8628         
8629         // if we are using something like 'domtemplate', then
8630         // the what gets used is:
8631         // t.applySubtemplate(NAME, data, wrapping data..)
8632         // the outer template then get' applied with
8633         //     the store 'extra data'
8634         // and the body get's added to the
8635         //      roo-name="data" node?
8636         //      <span class='roo-tpl-{name}'></span> ?????
8637         
8638         
8639         
8640         this.clearSelections();
8641         this.el.update("");
8642         var html = [];
8643         var records = this.store.getRange();
8644         if(records.length < 1) {
8645             
8646             // is this valid??  = should it render a template??
8647             
8648             this.el.update(this.emptyText);
8649             return;
8650         }
8651         var el = this.el;
8652         if (this.dataName) {
8653             this.el.update(t.apply(this.store.meta)); //????
8654             el = this.el.child('.roo-tpl-' + this.dataName);
8655         }
8656         
8657         for(var i = 0, len = records.length; i < len; i++){
8658             var data = this.prepareData(records[i].data, i, records[i]);
8659             this.fireEvent("preparedata", this, data, i, records[i]);
8660             
8661             var d = Roo.apply({}, data);
8662             
8663             if(this.tickable){
8664                 Roo.apply(d, {'roo-id' : Roo.id()});
8665                 
8666                 var _this = this;
8667             
8668                 Roo.each(this.parent.item, function(item){
8669                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8670                         return;
8671                     }
8672                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8673                 });
8674             }
8675             
8676             html[html.length] = Roo.util.Format.trim(
8677                 this.dataName ?
8678                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8679                     t.apply(d)
8680             );
8681         }
8682         
8683         
8684         
8685         el.update(html.join(""));
8686         this.nodes = el.dom.childNodes;
8687         this.updateIndexes(0);
8688     },
8689     
8690
8691     /**
8692      * Function to override to reformat the data that is sent to
8693      * the template for each node.
8694      * DEPRICATED - use the preparedata event handler.
8695      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8696      * a JSON object for an UpdateManager bound view).
8697      */
8698     prepareData : function(data, index, record)
8699     {
8700         this.fireEvent("preparedata", this, data, index, record);
8701         return data;
8702     },
8703
8704     onUpdate : function(ds, record){
8705          Roo.log('on update');   
8706         this.clearSelections();
8707         var index = this.store.indexOf(record);
8708         var n = this.nodes[index];
8709         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8710         n.parentNode.removeChild(n);
8711         this.updateIndexes(index, index);
8712     },
8713
8714     
8715     
8716 // --------- FIXME     
8717     onAdd : function(ds, records, index)
8718     {
8719         Roo.log(['on Add', ds, records, index] );        
8720         this.clearSelections();
8721         if(this.nodes.length == 0){
8722             this.refresh();
8723             return;
8724         }
8725         var n = this.nodes[index];
8726         for(var i = 0, len = records.length; i < len; i++){
8727             var d = this.prepareData(records[i].data, i, records[i]);
8728             if(n){
8729                 this.tpl.insertBefore(n, d);
8730             }else{
8731                 
8732                 this.tpl.append(this.el, d);
8733             }
8734         }
8735         this.updateIndexes(index);
8736     },
8737
8738     onRemove : function(ds, record, index){
8739         Roo.log('onRemove');
8740         this.clearSelections();
8741         var el = this.dataName  ?
8742             this.el.child('.roo-tpl-' + this.dataName) :
8743             this.el; 
8744         
8745         el.dom.removeChild(this.nodes[index]);
8746         this.updateIndexes(index);
8747     },
8748
8749     /**
8750      * Refresh an individual node.
8751      * @param {Number} index
8752      */
8753     refreshNode : function(index){
8754         this.onUpdate(this.store, this.store.getAt(index));
8755     },
8756
8757     updateIndexes : function(startIndex, endIndex){
8758         var ns = this.nodes;
8759         startIndex = startIndex || 0;
8760         endIndex = endIndex || ns.length - 1;
8761         for(var i = startIndex; i <= endIndex; i++){
8762             ns[i].nodeIndex = i;
8763         }
8764     },
8765
8766     /**
8767      * Changes the data store this view uses and refresh the view.
8768      * @param {Store} store
8769      */
8770     setStore : function(store, initial){
8771         if(!initial && this.store){
8772             this.store.un("datachanged", this.refresh);
8773             this.store.un("add", this.onAdd);
8774             this.store.un("remove", this.onRemove);
8775             this.store.un("update", this.onUpdate);
8776             this.store.un("clear", this.refresh);
8777             this.store.un("beforeload", this.onBeforeLoad);
8778             this.store.un("load", this.onLoad);
8779             this.store.un("loadexception", this.onLoad);
8780         }
8781         if(store){
8782           
8783             store.on("datachanged", this.refresh, this);
8784             store.on("add", this.onAdd, this);
8785             store.on("remove", this.onRemove, this);
8786             store.on("update", this.onUpdate, this);
8787             store.on("clear", this.refresh, this);
8788             store.on("beforeload", this.onBeforeLoad, this);
8789             store.on("load", this.onLoad, this);
8790             store.on("loadexception", this.onLoad, this);
8791         }
8792         
8793         if(store){
8794             this.refresh();
8795         }
8796     },
8797     /**
8798      * onbeforeLoad - masks the loading area.
8799      *
8800      */
8801     onBeforeLoad : function(store,opts)
8802     {
8803          Roo.log('onBeforeLoad');   
8804         if (!opts.add) {
8805             this.el.update("");
8806         }
8807         this.el.mask(this.mask ? this.mask : "Loading" ); 
8808     },
8809     onLoad : function ()
8810     {
8811         this.el.unmask();
8812     },
8813     
8814
8815     /**
8816      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8817      * @param {HTMLElement} node
8818      * @return {HTMLElement} The template node
8819      */
8820     findItemFromChild : function(node){
8821         var el = this.dataName  ?
8822             this.el.child('.roo-tpl-' + this.dataName,true) :
8823             this.el.dom; 
8824         
8825         if(!node || node.parentNode == el){
8826                     return node;
8827             }
8828             var p = node.parentNode;
8829             while(p && p != el){
8830             if(p.parentNode == el){
8831                 return p;
8832             }
8833             p = p.parentNode;
8834         }
8835             return null;
8836     },
8837
8838     /** @ignore */
8839     onClick : function(e){
8840         var item = this.findItemFromChild(e.getTarget());
8841         if(item){
8842             var index = this.indexOf(item);
8843             if(this.onItemClick(item, index, e) !== false){
8844                 this.fireEvent("click", this, index, item, e);
8845             }
8846         }else{
8847             this.clearSelections();
8848         }
8849     },
8850
8851     /** @ignore */
8852     onContextMenu : function(e){
8853         var item = this.findItemFromChild(e.getTarget());
8854         if(item){
8855             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8856         }
8857     },
8858
8859     /** @ignore */
8860     onDblClick : function(e){
8861         var item = this.findItemFromChild(e.getTarget());
8862         if(item){
8863             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8864         }
8865     },
8866
8867     onItemClick : function(item, index, e)
8868     {
8869         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8870             return false;
8871         }
8872         if (this.toggleSelect) {
8873             var m = this.isSelected(item) ? 'unselect' : 'select';
8874             Roo.log(m);
8875             var _t = this;
8876             _t[m](item, true, false);
8877             return true;
8878         }
8879         if(this.multiSelect || this.singleSelect){
8880             if(this.multiSelect && e.shiftKey && this.lastSelection){
8881                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8882             }else{
8883                 this.select(item, this.multiSelect && e.ctrlKey);
8884                 this.lastSelection = item;
8885             }
8886             
8887             if(!this.tickable){
8888                 e.preventDefault();
8889             }
8890             
8891         }
8892         return true;
8893     },
8894
8895     /**
8896      * Get the number of selected nodes.
8897      * @return {Number}
8898      */
8899     getSelectionCount : function(){
8900         return this.selections.length;
8901     },
8902
8903     /**
8904      * Get the currently selected nodes.
8905      * @return {Array} An array of HTMLElements
8906      */
8907     getSelectedNodes : function(){
8908         return this.selections;
8909     },
8910
8911     /**
8912      * Get the indexes of the selected nodes.
8913      * @return {Array}
8914      */
8915     getSelectedIndexes : function(){
8916         var indexes = [], s = this.selections;
8917         for(var i = 0, len = s.length; i < len; i++){
8918             indexes.push(s[i].nodeIndex);
8919         }
8920         return indexes;
8921     },
8922
8923     /**
8924      * Clear all selections
8925      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8926      */
8927     clearSelections : function(suppressEvent){
8928         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8929             this.cmp.elements = this.selections;
8930             this.cmp.removeClass(this.selectedClass);
8931             this.selections = [];
8932             if(!suppressEvent){
8933                 this.fireEvent("selectionchange", this, this.selections);
8934             }
8935         }
8936     },
8937
8938     /**
8939      * Returns true if the passed node is selected
8940      * @param {HTMLElement/Number} node The node or node index
8941      * @return {Boolean}
8942      */
8943     isSelected : function(node){
8944         var s = this.selections;
8945         if(s.length < 1){
8946             return false;
8947         }
8948         node = this.getNode(node);
8949         return s.indexOf(node) !== -1;
8950     },
8951
8952     /**
8953      * Selects nodes.
8954      * @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
8955      * @param {Boolean} keepExisting (optional) true to keep existing selections
8956      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8957      */
8958     select : function(nodeInfo, keepExisting, suppressEvent){
8959         if(nodeInfo instanceof Array){
8960             if(!keepExisting){
8961                 this.clearSelections(true);
8962             }
8963             for(var i = 0, len = nodeInfo.length; i < len; i++){
8964                 this.select(nodeInfo[i], true, true);
8965             }
8966             return;
8967         } 
8968         var node = this.getNode(nodeInfo);
8969         if(!node || this.isSelected(node)){
8970             return; // already selected.
8971         }
8972         if(!keepExisting){
8973             this.clearSelections(true);
8974         }
8975         
8976         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8977             Roo.fly(node).addClass(this.selectedClass);
8978             this.selections.push(node);
8979             if(!suppressEvent){
8980                 this.fireEvent("selectionchange", this, this.selections);
8981             }
8982         }
8983         
8984         
8985     },
8986       /**
8987      * Unselects nodes.
8988      * @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
8989      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8990      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8991      */
8992     unselect : function(nodeInfo, keepExisting, suppressEvent)
8993     {
8994         if(nodeInfo instanceof Array){
8995             Roo.each(this.selections, function(s) {
8996                 this.unselect(s, nodeInfo);
8997             }, this);
8998             return;
8999         }
9000         var node = this.getNode(nodeInfo);
9001         if(!node || !this.isSelected(node)){
9002             Roo.log("not selected");
9003             return; // not selected.
9004         }
9005         // fireevent???
9006         var ns = [];
9007         Roo.each(this.selections, function(s) {
9008             if (s == node ) {
9009                 Roo.fly(node).removeClass(this.selectedClass);
9010
9011                 return;
9012             }
9013             ns.push(s);
9014         },this);
9015         
9016         this.selections= ns;
9017         this.fireEvent("selectionchange", this, this.selections);
9018     },
9019
9020     /**
9021      * Gets a template node.
9022      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9023      * @return {HTMLElement} The node or null if it wasn't found
9024      */
9025     getNode : function(nodeInfo){
9026         if(typeof nodeInfo == "string"){
9027             return document.getElementById(nodeInfo);
9028         }else if(typeof nodeInfo == "number"){
9029             return this.nodes[nodeInfo];
9030         }
9031         return nodeInfo;
9032     },
9033
9034     /**
9035      * Gets a range template nodes.
9036      * @param {Number} startIndex
9037      * @param {Number} endIndex
9038      * @return {Array} An array of nodes
9039      */
9040     getNodes : function(start, end){
9041         var ns = this.nodes;
9042         start = start || 0;
9043         end = typeof end == "undefined" ? ns.length - 1 : end;
9044         var nodes = [];
9045         if(start <= end){
9046             for(var i = start; i <= end; i++){
9047                 nodes.push(ns[i]);
9048             }
9049         } else{
9050             for(var i = start; i >= end; i--){
9051                 nodes.push(ns[i]);
9052             }
9053         }
9054         return nodes;
9055     },
9056
9057     /**
9058      * Finds the index of the passed node
9059      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9060      * @return {Number} The index of the node or -1
9061      */
9062     indexOf : function(node){
9063         node = this.getNode(node);
9064         if(typeof node.nodeIndex == "number"){
9065             return node.nodeIndex;
9066         }
9067         var ns = this.nodes;
9068         for(var i = 0, len = ns.length; i < len; i++){
9069             if(ns[i] == node){
9070                 return i;
9071             }
9072         }
9073         return -1;
9074     }
9075 });
9076 /*
9077  * Based on:
9078  * Ext JS Library 1.1.1
9079  * Copyright(c) 2006-2007, Ext JS, LLC.
9080  *
9081  * Originally Released Under LGPL - original licence link has changed is not relivant.
9082  *
9083  * Fork - LGPL
9084  * <script type="text/javascript">
9085  */
9086
9087 /**
9088  * @class Roo.JsonView
9089  * @extends Roo.View
9090  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9091 <pre><code>
9092 var view = new Roo.JsonView({
9093     container: "my-element",
9094     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9095     multiSelect: true, 
9096     jsonRoot: "data" 
9097 });
9098
9099 // listen for node click?
9100 view.on("click", function(vw, index, node, e){
9101     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9102 });
9103
9104 // direct load of JSON data
9105 view.load("foobar.php");
9106
9107 // Example from my blog list
9108 var tpl = new Roo.Template(
9109     '&lt;div class="entry"&gt;' +
9110     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9111     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9112     "&lt;/div&gt;&lt;hr /&gt;"
9113 );
9114
9115 var moreView = new Roo.JsonView({
9116     container :  "entry-list", 
9117     template : tpl,
9118     jsonRoot: "posts"
9119 });
9120 moreView.on("beforerender", this.sortEntries, this);
9121 moreView.load({
9122     url: "/blog/get-posts.php",
9123     params: "allposts=true",
9124     text: "Loading Blog Entries..."
9125 });
9126 </code></pre>
9127
9128 * Note: old code is supported with arguments : (container, template, config)
9129
9130
9131  * @constructor
9132  * Create a new JsonView
9133  * 
9134  * @param {Object} config The config object
9135  * 
9136  */
9137 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9138     
9139     
9140     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9141
9142     var um = this.el.getUpdateManager();
9143     um.setRenderer(this);
9144     um.on("update", this.onLoad, this);
9145     um.on("failure", this.onLoadException, this);
9146
9147     /**
9148      * @event beforerender
9149      * Fires before rendering of the downloaded JSON data.
9150      * @param {Roo.JsonView} this
9151      * @param {Object} data The JSON data loaded
9152      */
9153     /**
9154      * @event load
9155      * Fires when data is loaded.
9156      * @param {Roo.JsonView} this
9157      * @param {Object} data The JSON data loaded
9158      * @param {Object} response The raw Connect response object
9159      */
9160     /**
9161      * @event loadexception
9162      * Fires when loading fails.
9163      * @param {Roo.JsonView} this
9164      * @param {Object} response The raw Connect response object
9165      */
9166     this.addEvents({
9167         'beforerender' : true,
9168         'load' : true,
9169         'loadexception' : true
9170     });
9171 };
9172 Roo.extend(Roo.JsonView, Roo.View, {
9173     /**
9174      * @type {String} The root property in the loaded JSON object that contains the data
9175      */
9176     jsonRoot : "",
9177
9178     /**
9179      * Refreshes the view.
9180      */
9181     refresh : function(){
9182         this.clearSelections();
9183         this.el.update("");
9184         var html = [];
9185         var o = this.jsonData;
9186         if(o && o.length > 0){
9187             for(var i = 0, len = o.length; i < len; i++){
9188                 var data = this.prepareData(o[i], i, o);
9189                 html[html.length] = this.tpl.apply(data);
9190             }
9191         }else{
9192             html.push(this.emptyText);
9193         }
9194         this.el.update(html.join(""));
9195         this.nodes = this.el.dom.childNodes;
9196         this.updateIndexes(0);
9197     },
9198
9199     /**
9200      * 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.
9201      * @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:
9202      <pre><code>
9203      view.load({
9204          url: "your-url.php",
9205          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9206          callback: yourFunction,
9207          scope: yourObject, //(optional scope)
9208          discardUrl: false,
9209          nocache: false,
9210          text: "Loading...",
9211          timeout: 30,
9212          scripts: false
9213      });
9214      </code></pre>
9215      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9216      * 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.
9217      * @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}
9218      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9219      * @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.
9220      */
9221     load : function(){
9222         var um = this.el.getUpdateManager();
9223         um.update.apply(um, arguments);
9224     },
9225
9226     render : function(el, response){
9227         this.clearSelections();
9228         this.el.update("");
9229         var o;
9230         try{
9231             o = Roo.util.JSON.decode(response.responseText);
9232             if(this.jsonRoot){
9233                 
9234                 o = o[this.jsonRoot];
9235             }
9236         } catch(e){
9237         }
9238         /**
9239          * The current JSON data or null
9240          */
9241         this.jsonData = o;
9242         this.beforeRender();
9243         this.refresh();
9244     },
9245
9246 /**
9247  * Get the number of records in the current JSON dataset
9248  * @return {Number}
9249  */
9250     getCount : function(){
9251         return this.jsonData ? this.jsonData.length : 0;
9252     },
9253
9254 /**
9255  * Returns the JSON object for the specified node(s)
9256  * @param {HTMLElement/Array} node The node or an array of nodes
9257  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9258  * you get the JSON object for the node
9259  */
9260     getNodeData : function(node){
9261         if(node instanceof Array){
9262             var data = [];
9263             for(var i = 0, len = node.length; i < len; i++){
9264                 data.push(this.getNodeData(node[i]));
9265             }
9266             return data;
9267         }
9268         return this.jsonData[this.indexOf(node)] || null;
9269     },
9270
9271     beforeRender : function(){
9272         this.snapshot = this.jsonData;
9273         if(this.sortInfo){
9274             this.sort.apply(this, this.sortInfo);
9275         }
9276         this.fireEvent("beforerender", this, this.jsonData);
9277     },
9278
9279     onLoad : function(el, o){
9280         this.fireEvent("load", this, this.jsonData, o);
9281     },
9282
9283     onLoadException : function(el, o){
9284         this.fireEvent("loadexception", this, o);
9285     },
9286
9287 /**
9288  * Filter the data by a specific property.
9289  * @param {String} property A property on your JSON objects
9290  * @param {String/RegExp} value Either string that the property values
9291  * should start with, or a RegExp to test against the property
9292  */
9293     filter : function(property, value){
9294         if(this.jsonData){
9295             var data = [];
9296             var ss = this.snapshot;
9297             if(typeof value == "string"){
9298                 var vlen = value.length;
9299                 if(vlen == 0){
9300                     this.clearFilter();
9301                     return;
9302                 }
9303                 value = value.toLowerCase();
9304                 for(var i = 0, len = ss.length; i < len; i++){
9305                     var o = ss[i];
9306                     if(o[property].substr(0, vlen).toLowerCase() == value){
9307                         data.push(o);
9308                     }
9309                 }
9310             } else if(value.exec){ // regex?
9311                 for(var i = 0, len = ss.length; i < len; i++){
9312                     var o = ss[i];
9313                     if(value.test(o[property])){
9314                         data.push(o);
9315                     }
9316                 }
9317             } else{
9318                 return;
9319             }
9320             this.jsonData = data;
9321             this.refresh();
9322         }
9323     },
9324
9325 /**
9326  * Filter by a function. The passed function will be called with each
9327  * object in the current dataset. If the function returns true the value is kept,
9328  * otherwise it is filtered.
9329  * @param {Function} fn
9330  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9331  */
9332     filterBy : function(fn, scope){
9333         if(this.jsonData){
9334             var data = [];
9335             var ss = this.snapshot;
9336             for(var i = 0, len = ss.length; i < len; i++){
9337                 var o = ss[i];
9338                 if(fn.call(scope || this, o)){
9339                     data.push(o);
9340                 }
9341             }
9342             this.jsonData = data;
9343             this.refresh();
9344         }
9345     },
9346
9347 /**
9348  * Clears the current filter.
9349  */
9350     clearFilter : function(){
9351         if(this.snapshot && this.jsonData != this.snapshot){
9352             this.jsonData = this.snapshot;
9353             this.refresh();
9354         }
9355     },
9356
9357
9358 /**
9359  * Sorts the data for this view and refreshes it.
9360  * @param {String} property A property on your JSON objects to sort on
9361  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9362  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9363  */
9364     sort : function(property, dir, sortType){
9365         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9366         if(this.jsonData){
9367             var p = property;
9368             var dsc = dir && dir.toLowerCase() == "desc";
9369             var f = function(o1, o2){
9370                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9371                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9372                 ;
9373                 if(v1 < v2){
9374                     return dsc ? +1 : -1;
9375                 } else if(v1 > v2){
9376                     return dsc ? -1 : +1;
9377                 } else{
9378                     return 0;
9379                 }
9380             };
9381             this.jsonData.sort(f);
9382             this.refresh();
9383             if(this.jsonData != this.snapshot){
9384                 this.snapshot.sort(f);
9385             }
9386         }
9387     }
9388 });/*
9389  * Based on:
9390  * Ext JS Library 1.1.1
9391  * Copyright(c) 2006-2007, Ext JS, LLC.
9392  *
9393  * Originally Released Under LGPL - original licence link has changed is not relivant.
9394  *
9395  * Fork - LGPL
9396  * <script type="text/javascript">
9397  */
9398  
9399
9400 /**
9401  * @class Roo.ColorPalette
9402  * @extends Roo.Component
9403  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9404  * Here's an example of typical usage:
9405  * <pre><code>
9406 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9407 cp.render('my-div');
9408
9409 cp.on('select', function(palette, selColor){
9410     // do something with selColor
9411 });
9412 </code></pre>
9413  * @constructor
9414  * Create a new ColorPalette
9415  * @param {Object} config The config object
9416  */
9417 Roo.ColorPalette = function(config){
9418     Roo.ColorPalette.superclass.constructor.call(this, config);
9419     this.addEvents({
9420         /**
9421              * @event select
9422              * Fires when a color is selected
9423              * @param {ColorPalette} this
9424              * @param {String} color The 6-digit color hex code (without the # symbol)
9425              */
9426         select: true
9427     });
9428
9429     if(this.handler){
9430         this.on("select", this.handler, this.scope, true);
9431     }
9432 };
9433 Roo.extend(Roo.ColorPalette, Roo.Component, {
9434     /**
9435      * @cfg {String} itemCls
9436      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9437      */
9438     itemCls : "x-color-palette",
9439     /**
9440      * @cfg {String} value
9441      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9442      * the hex codes are case-sensitive.
9443      */
9444     value : null,
9445     clickEvent:'click',
9446     // private
9447     ctype: "Roo.ColorPalette",
9448
9449     /**
9450      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9451      */
9452     allowReselect : false,
9453
9454     /**
9455      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9456      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9457      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9458      * of colors with the width setting until the box is symmetrical.</p>
9459      * <p>You can override individual colors if needed:</p>
9460      * <pre><code>
9461 var cp = new Roo.ColorPalette();
9462 cp.colors[0] = "FF0000";  // change the first box to red
9463 </code></pre>
9464
9465 Or you can provide a custom array of your own for complete control:
9466 <pre><code>
9467 var cp = new Roo.ColorPalette();
9468 cp.colors = ["000000", "993300", "333300"];
9469 </code></pre>
9470      * @type Array
9471      */
9472     colors : [
9473         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9474         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9475         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9476         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9477         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9478     ],
9479
9480     // private
9481     onRender : function(container, position){
9482         var t = new Roo.MasterTemplate(
9483             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9484         );
9485         var c = this.colors;
9486         for(var i = 0, len = c.length; i < len; i++){
9487             t.add([c[i]]);
9488         }
9489         var el = document.createElement("div");
9490         el.className = this.itemCls;
9491         t.overwrite(el);
9492         container.dom.insertBefore(el, position);
9493         this.el = Roo.get(el);
9494         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9495         if(this.clickEvent != 'click'){
9496             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9497         }
9498     },
9499
9500     // private
9501     afterRender : function(){
9502         Roo.ColorPalette.superclass.afterRender.call(this);
9503         if(this.value){
9504             var s = this.value;
9505             this.value = null;
9506             this.select(s);
9507         }
9508     },
9509
9510     // private
9511     handleClick : function(e, t){
9512         e.preventDefault();
9513         if(!this.disabled){
9514             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9515             this.select(c.toUpperCase());
9516         }
9517     },
9518
9519     /**
9520      * Selects the specified color in the palette (fires the select event)
9521      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9522      */
9523     select : function(color){
9524         color = color.replace("#", "");
9525         if(color != this.value || this.allowReselect){
9526             var el = this.el;
9527             if(this.value){
9528                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9529             }
9530             el.child("a.color-"+color).addClass("x-color-palette-sel");
9531             this.value = color;
9532             this.fireEvent("select", this, color);
9533         }
9534     }
9535 });/*
9536  * Based on:
9537  * Ext JS Library 1.1.1
9538  * Copyright(c) 2006-2007, Ext JS, LLC.
9539  *
9540  * Originally Released Under LGPL - original licence link has changed is not relivant.
9541  *
9542  * Fork - LGPL
9543  * <script type="text/javascript">
9544  */
9545  
9546 /**
9547  * @class Roo.DatePicker
9548  * @extends Roo.Component
9549  * Simple date picker class.
9550  * @constructor
9551  * Create a new DatePicker
9552  * @param {Object} config The config object
9553  */
9554 Roo.DatePicker = function(config){
9555     Roo.DatePicker.superclass.constructor.call(this, config);
9556
9557     this.value = config && config.value ?
9558                  config.value.clearTime() : new Date().clearTime();
9559
9560     this.addEvents({
9561         /**
9562              * @event select
9563              * Fires when a date is selected
9564              * @param {DatePicker} this
9565              * @param {Date} date The selected date
9566              */
9567         'select': true,
9568         /**
9569              * @event monthchange
9570              * Fires when the displayed month changes 
9571              * @param {DatePicker} this
9572              * @param {Date} date The selected month
9573              */
9574         'monthchange': true
9575     });
9576
9577     if(this.handler){
9578         this.on("select", this.handler,  this.scope || this);
9579     }
9580     // build the disabledDatesRE
9581     if(!this.disabledDatesRE && this.disabledDates){
9582         var dd = this.disabledDates;
9583         var re = "(?:";
9584         for(var i = 0; i < dd.length; i++){
9585             re += dd[i];
9586             if(i != dd.length-1) re += "|";
9587         }
9588         this.disabledDatesRE = new RegExp(re + ")");
9589     }
9590 };
9591
9592 Roo.extend(Roo.DatePicker, Roo.Component, {
9593     /**
9594      * @cfg {String} todayText
9595      * The text to display on the button that selects the current date (defaults to "Today")
9596      */
9597     todayText : "Today",
9598     /**
9599      * @cfg {String} okText
9600      * The text to display on the ok button
9601      */
9602     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9603     /**
9604      * @cfg {String} cancelText
9605      * The text to display on the cancel button
9606      */
9607     cancelText : "Cancel",
9608     /**
9609      * @cfg {String} todayTip
9610      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9611      */
9612     todayTip : "{0} (Spacebar)",
9613     /**
9614      * @cfg {Date} minDate
9615      * Minimum allowable date (JavaScript date object, defaults to null)
9616      */
9617     minDate : null,
9618     /**
9619      * @cfg {Date} maxDate
9620      * Maximum allowable date (JavaScript date object, defaults to null)
9621      */
9622     maxDate : null,
9623     /**
9624      * @cfg {String} minText
9625      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9626      */
9627     minText : "This date is before the minimum date",
9628     /**
9629      * @cfg {String} maxText
9630      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9631      */
9632     maxText : "This date is after the maximum date",
9633     /**
9634      * @cfg {String} format
9635      * The default date format string which can be overriden for localization support.  The format must be
9636      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9637      */
9638     format : "m/d/y",
9639     /**
9640      * @cfg {Array} disabledDays
9641      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9642      */
9643     disabledDays : null,
9644     /**
9645      * @cfg {String} disabledDaysText
9646      * The tooltip to display when the date falls on a disabled day (defaults to "")
9647      */
9648     disabledDaysText : "",
9649     /**
9650      * @cfg {RegExp} disabledDatesRE
9651      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9652      */
9653     disabledDatesRE : null,
9654     /**
9655      * @cfg {String} disabledDatesText
9656      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9657      */
9658     disabledDatesText : "",
9659     /**
9660      * @cfg {Boolean} constrainToViewport
9661      * True to constrain the date picker to the viewport (defaults to true)
9662      */
9663     constrainToViewport : true,
9664     /**
9665      * @cfg {Array} monthNames
9666      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9667      */
9668     monthNames : Date.monthNames,
9669     /**
9670      * @cfg {Array} dayNames
9671      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9672      */
9673     dayNames : Date.dayNames,
9674     /**
9675      * @cfg {String} nextText
9676      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9677      */
9678     nextText: 'Next Month (Control+Right)',
9679     /**
9680      * @cfg {String} prevText
9681      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9682      */
9683     prevText: 'Previous Month (Control+Left)',
9684     /**
9685      * @cfg {String} monthYearText
9686      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9687      */
9688     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9689     /**
9690      * @cfg {Number} startDay
9691      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9692      */
9693     startDay : 0,
9694     /**
9695      * @cfg {Bool} showClear
9696      * Show a clear button (usefull for date form elements that can be blank.)
9697      */
9698     
9699     showClear: false,
9700     
9701     /**
9702      * Sets the value of the date field
9703      * @param {Date} value The date to set
9704      */
9705     setValue : function(value){
9706         var old = this.value;
9707         
9708         if (typeof(value) == 'string') {
9709          
9710             value = Date.parseDate(value, this.format);
9711         }
9712         if (!value) {
9713             value = new Date();
9714         }
9715         
9716         this.value = value.clearTime(true);
9717         if(this.el){
9718             this.update(this.value);
9719         }
9720     },
9721
9722     /**
9723      * Gets the current selected value of the date field
9724      * @return {Date} The selected date
9725      */
9726     getValue : function(){
9727         return this.value;
9728     },
9729
9730     // private
9731     focus : function(){
9732         if(this.el){
9733             this.update(this.activeDate);
9734         }
9735     },
9736
9737     // privateval
9738     onRender : function(container, position){
9739         
9740         var m = [
9741              '<table cellspacing="0">',
9742                 '<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>',
9743                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9744         var dn = this.dayNames;
9745         for(var i = 0; i < 7; i++){
9746             var d = this.startDay+i;
9747             if(d > 6){
9748                 d = d-7;
9749             }
9750             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9751         }
9752         m[m.length] = "</tr></thead><tbody><tr>";
9753         for(var i = 0; i < 42; i++) {
9754             if(i % 7 == 0 && i != 0){
9755                 m[m.length] = "</tr><tr>";
9756             }
9757             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9758         }
9759         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9760             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9761
9762         var el = document.createElement("div");
9763         el.className = "x-date-picker";
9764         el.innerHTML = m.join("");
9765
9766         container.dom.insertBefore(el, position);
9767
9768         this.el = Roo.get(el);
9769         this.eventEl = Roo.get(el.firstChild);
9770
9771         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9772             handler: this.showPrevMonth,
9773             scope: this,
9774             preventDefault:true,
9775             stopDefault:true
9776         });
9777
9778         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9779             handler: this.showNextMonth,
9780             scope: this,
9781             preventDefault:true,
9782             stopDefault:true
9783         });
9784
9785         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9786
9787         this.monthPicker = this.el.down('div.x-date-mp');
9788         this.monthPicker.enableDisplayMode('block');
9789         
9790         var kn = new Roo.KeyNav(this.eventEl, {
9791             "left" : function(e){
9792                 e.ctrlKey ?
9793                     this.showPrevMonth() :
9794                     this.update(this.activeDate.add("d", -1));
9795             },
9796
9797             "right" : function(e){
9798                 e.ctrlKey ?
9799                     this.showNextMonth() :
9800                     this.update(this.activeDate.add("d", 1));
9801             },
9802
9803             "up" : function(e){
9804                 e.ctrlKey ?
9805                     this.showNextYear() :
9806                     this.update(this.activeDate.add("d", -7));
9807             },
9808
9809             "down" : function(e){
9810                 e.ctrlKey ?
9811                     this.showPrevYear() :
9812                     this.update(this.activeDate.add("d", 7));
9813             },
9814
9815             "pageUp" : function(e){
9816                 this.showNextMonth();
9817             },
9818
9819             "pageDown" : function(e){
9820                 this.showPrevMonth();
9821             },
9822
9823             "enter" : function(e){
9824                 e.stopPropagation();
9825                 return true;
9826             },
9827
9828             scope : this
9829         });
9830
9831         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9832
9833         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9834
9835         this.el.unselectable();
9836         
9837         this.cells = this.el.select("table.x-date-inner tbody td");
9838         this.textNodes = this.el.query("table.x-date-inner tbody span");
9839
9840         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9841             text: "&#160;",
9842             tooltip: this.monthYearText
9843         });
9844
9845         this.mbtn.on('click', this.showMonthPicker, this);
9846         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9847
9848
9849         var today = (new Date()).dateFormat(this.format);
9850         
9851         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9852         if (this.showClear) {
9853             baseTb.add( new Roo.Toolbar.Fill());
9854         }
9855         baseTb.add({
9856             text: String.format(this.todayText, today),
9857             tooltip: String.format(this.todayTip, today),
9858             handler: this.selectToday,
9859             scope: this
9860         });
9861         
9862         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9863             
9864         //});
9865         if (this.showClear) {
9866             
9867             baseTb.add( new Roo.Toolbar.Fill());
9868             baseTb.add({
9869                 text: '&#160;',
9870                 cls: 'x-btn-icon x-btn-clear',
9871                 handler: function() {
9872                     //this.value = '';
9873                     this.fireEvent("select", this, '');
9874                 },
9875                 scope: this
9876             });
9877         }
9878         
9879         
9880         if(Roo.isIE){
9881             this.el.repaint();
9882         }
9883         this.update(this.value);
9884     },
9885
9886     createMonthPicker : function(){
9887         if(!this.monthPicker.dom.firstChild){
9888             var buf = ['<table border="0" cellspacing="0">'];
9889             for(var i = 0; i < 6; i++){
9890                 buf.push(
9891                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9892                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9893                     i == 0 ?
9894                     '<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>' :
9895                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9896                 );
9897             }
9898             buf.push(
9899                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9900                     this.okText,
9901                     '</button><button type="button" class="x-date-mp-cancel">',
9902                     this.cancelText,
9903                     '</button></td></tr>',
9904                 '</table>'
9905             );
9906             this.monthPicker.update(buf.join(''));
9907             this.monthPicker.on('click', this.onMonthClick, this);
9908             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9909
9910             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9911             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9912
9913             this.mpMonths.each(function(m, a, i){
9914                 i += 1;
9915                 if((i%2) == 0){
9916                     m.dom.xmonth = 5 + Math.round(i * .5);
9917                 }else{
9918                     m.dom.xmonth = Math.round((i-1) * .5);
9919                 }
9920             });
9921         }
9922     },
9923
9924     showMonthPicker : function(){
9925         this.createMonthPicker();
9926         var size = this.el.getSize();
9927         this.monthPicker.setSize(size);
9928         this.monthPicker.child('table').setSize(size);
9929
9930         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9931         this.updateMPMonth(this.mpSelMonth);
9932         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9933         this.updateMPYear(this.mpSelYear);
9934
9935         this.monthPicker.slideIn('t', {duration:.2});
9936     },
9937
9938     updateMPYear : function(y){
9939         this.mpyear = y;
9940         var ys = this.mpYears.elements;
9941         for(var i = 1; i <= 10; i++){
9942             var td = ys[i-1], y2;
9943             if((i%2) == 0){
9944                 y2 = y + Math.round(i * .5);
9945                 td.firstChild.innerHTML = y2;
9946                 td.xyear = y2;
9947             }else{
9948                 y2 = y - (5-Math.round(i * .5));
9949                 td.firstChild.innerHTML = y2;
9950                 td.xyear = y2;
9951             }
9952             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9953         }
9954     },
9955
9956     updateMPMonth : function(sm){
9957         this.mpMonths.each(function(m, a, i){
9958             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9959         });
9960     },
9961
9962     selectMPMonth: function(m){
9963         
9964     },
9965
9966     onMonthClick : function(e, t){
9967         e.stopEvent();
9968         var el = new Roo.Element(t), pn;
9969         if(el.is('button.x-date-mp-cancel')){
9970             this.hideMonthPicker();
9971         }
9972         else if(el.is('button.x-date-mp-ok')){
9973             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9974             this.hideMonthPicker();
9975         }
9976         else if(pn = el.up('td.x-date-mp-month', 2)){
9977             this.mpMonths.removeClass('x-date-mp-sel');
9978             pn.addClass('x-date-mp-sel');
9979             this.mpSelMonth = pn.dom.xmonth;
9980         }
9981         else if(pn = el.up('td.x-date-mp-year', 2)){
9982             this.mpYears.removeClass('x-date-mp-sel');
9983             pn.addClass('x-date-mp-sel');
9984             this.mpSelYear = pn.dom.xyear;
9985         }
9986         else if(el.is('a.x-date-mp-prev')){
9987             this.updateMPYear(this.mpyear-10);
9988         }
9989         else if(el.is('a.x-date-mp-next')){
9990             this.updateMPYear(this.mpyear+10);
9991         }
9992     },
9993
9994     onMonthDblClick : function(e, t){
9995         e.stopEvent();
9996         var el = new Roo.Element(t), pn;
9997         if(pn = el.up('td.x-date-mp-month', 2)){
9998             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
9999             this.hideMonthPicker();
10000         }
10001         else if(pn = el.up('td.x-date-mp-year', 2)){
10002             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10003             this.hideMonthPicker();
10004         }
10005     },
10006
10007     hideMonthPicker : function(disableAnim){
10008         if(this.monthPicker){
10009             if(disableAnim === true){
10010                 this.monthPicker.hide();
10011             }else{
10012                 this.monthPicker.slideOut('t', {duration:.2});
10013             }
10014         }
10015     },
10016
10017     // private
10018     showPrevMonth : function(e){
10019         this.update(this.activeDate.add("mo", -1));
10020     },
10021
10022     // private
10023     showNextMonth : function(e){
10024         this.update(this.activeDate.add("mo", 1));
10025     },
10026
10027     // private
10028     showPrevYear : function(){
10029         this.update(this.activeDate.add("y", -1));
10030     },
10031
10032     // private
10033     showNextYear : function(){
10034         this.update(this.activeDate.add("y", 1));
10035     },
10036
10037     // private
10038     handleMouseWheel : function(e){
10039         var delta = e.getWheelDelta();
10040         if(delta > 0){
10041             this.showPrevMonth();
10042             e.stopEvent();
10043         } else if(delta < 0){
10044             this.showNextMonth();
10045             e.stopEvent();
10046         }
10047     },
10048
10049     // private
10050     handleDateClick : function(e, t){
10051         e.stopEvent();
10052         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10053             this.setValue(new Date(t.dateValue));
10054             this.fireEvent("select", this, this.value);
10055         }
10056     },
10057
10058     // private
10059     selectToday : function(){
10060         this.setValue(new Date().clearTime());
10061         this.fireEvent("select", this, this.value);
10062     },
10063
10064     // private
10065     update : function(date)
10066     {
10067         var vd = this.activeDate;
10068         this.activeDate = date;
10069         if(vd && this.el){
10070             var t = date.getTime();
10071             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10072                 this.cells.removeClass("x-date-selected");
10073                 this.cells.each(function(c){
10074                    if(c.dom.firstChild.dateValue == t){
10075                        c.addClass("x-date-selected");
10076                        setTimeout(function(){
10077                             try{c.dom.firstChild.focus();}catch(e){}
10078                        }, 50);
10079                        return false;
10080                    }
10081                 });
10082                 return;
10083             }
10084         }
10085         
10086         var days = date.getDaysInMonth();
10087         var firstOfMonth = date.getFirstDateOfMonth();
10088         var startingPos = firstOfMonth.getDay()-this.startDay;
10089
10090         if(startingPos <= this.startDay){
10091             startingPos += 7;
10092         }
10093
10094         var pm = date.add("mo", -1);
10095         var prevStart = pm.getDaysInMonth()-startingPos;
10096
10097         var cells = this.cells.elements;
10098         var textEls = this.textNodes;
10099         days += startingPos;
10100
10101         // convert everything to numbers so it's fast
10102         var day = 86400000;
10103         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10104         var today = new Date().clearTime().getTime();
10105         var sel = date.clearTime().getTime();
10106         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10107         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10108         var ddMatch = this.disabledDatesRE;
10109         var ddText = this.disabledDatesText;
10110         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10111         var ddaysText = this.disabledDaysText;
10112         var format = this.format;
10113
10114         var setCellClass = function(cal, cell){
10115             cell.title = "";
10116             var t = d.getTime();
10117             cell.firstChild.dateValue = t;
10118             if(t == today){
10119                 cell.className += " x-date-today";
10120                 cell.title = cal.todayText;
10121             }
10122             if(t == sel){
10123                 cell.className += " x-date-selected";
10124                 setTimeout(function(){
10125                     try{cell.firstChild.focus();}catch(e){}
10126                 }, 50);
10127             }
10128             // disabling
10129             if(t < min) {
10130                 cell.className = " x-date-disabled";
10131                 cell.title = cal.minText;
10132                 return;
10133             }
10134             if(t > max) {
10135                 cell.className = " x-date-disabled";
10136                 cell.title = cal.maxText;
10137                 return;
10138             }
10139             if(ddays){
10140                 if(ddays.indexOf(d.getDay()) != -1){
10141                     cell.title = ddaysText;
10142                     cell.className = " x-date-disabled";
10143                 }
10144             }
10145             if(ddMatch && format){
10146                 var fvalue = d.dateFormat(format);
10147                 if(ddMatch.test(fvalue)){
10148                     cell.title = ddText.replace("%0", fvalue);
10149                     cell.className = " x-date-disabled";
10150                 }
10151             }
10152         };
10153
10154         var i = 0;
10155         for(; i < startingPos; i++) {
10156             textEls[i].innerHTML = (++prevStart);
10157             d.setDate(d.getDate()+1);
10158             cells[i].className = "x-date-prevday";
10159             setCellClass(this, cells[i]);
10160         }
10161         for(; i < days; i++){
10162             intDay = i - startingPos + 1;
10163             textEls[i].innerHTML = (intDay);
10164             d.setDate(d.getDate()+1);
10165             cells[i].className = "x-date-active";
10166             setCellClass(this, cells[i]);
10167         }
10168         var extraDays = 0;
10169         for(; i < 42; i++) {
10170              textEls[i].innerHTML = (++extraDays);
10171              d.setDate(d.getDate()+1);
10172              cells[i].className = "x-date-nextday";
10173              setCellClass(this, cells[i]);
10174         }
10175
10176         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10177         this.fireEvent('monthchange', this, date);
10178         
10179         if(!this.internalRender){
10180             var main = this.el.dom.firstChild;
10181             var w = main.offsetWidth;
10182             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10183             Roo.fly(main).setWidth(w);
10184             this.internalRender = true;
10185             // opera does not respect the auto grow header center column
10186             // then, after it gets a width opera refuses to recalculate
10187             // without a second pass
10188             if(Roo.isOpera && !this.secondPass){
10189                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10190                 this.secondPass = true;
10191                 this.update.defer(10, this, [date]);
10192             }
10193         }
10194         
10195         
10196     }
10197 });        /*
10198  * Based on:
10199  * Ext JS Library 1.1.1
10200  * Copyright(c) 2006-2007, Ext JS, LLC.
10201  *
10202  * Originally Released Under LGPL - original licence link has changed is not relivant.
10203  *
10204  * Fork - LGPL
10205  * <script type="text/javascript">
10206  */
10207 /**
10208  * @class Roo.TabPanel
10209  * @extends Roo.util.Observable
10210  * A lightweight tab container.
10211  * <br><br>
10212  * Usage:
10213  * <pre><code>
10214 // basic tabs 1, built from existing content
10215 var tabs = new Roo.TabPanel("tabs1");
10216 tabs.addTab("script", "View Script");
10217 tabs.addTab("markup", "View Markup");
10218 tabs.activate("script");
10219
10220 // more advanced tabs, built from javascript
10221 var jtabs = new Roo.TabPanel("jtabs");
10222 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10223
10224 // set up the UpdateManager
10225 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10226 var updater = tab2.getUpdateManager();
10227 updater.setDefaultUrl("ajax1.htm");
10228 tab2.on('activate', updater.refresh, updater, true);
10229
10230 // Use setUrl for Ajax loading
10231 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10232 tab3.setUrl("ajax2.htm", null, true);
10233
10234 // Disabled tab
10235 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10236 tab4.disable();
10237
10238 jtabs.activate("jtabs-1");
10239  * </code></pre>
10240  * @constructor
10241  * Create a new TabPanel.
10242  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10243  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10244  */
10245 Roo.TabPanel = function(container, config){
10246     /**
10247     * The container element for this TabPanel.
10248     * @type Roo.Element
10249     */
10250     this.el = Roo.get(container, true);
10251     if(config){
10252         if(typeof config == "boolean"){
10253             this.tabPosition = config ? "bottom" : "top";
10254         }else{
10255             Roo.apply(this, config);
10256         }
10257     }
10258     if(this.tabPosition == "bottom"){
10259         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10260         this.el.addClass("x-tabs-bottom");
10261     }
10262     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10263     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10264     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10265     if(Roo.isIE){
10266         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10267     }
10268     if(this.tabPosition != "bottom"){
10269         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10270          * @type Roo.Element
10271          */
10272         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10273         this.el.addClass("x-tabs-top");
10274     }
10275     this.items = [];
10276
10277     this.bodyEl.setStyle("position", "relative");
10278
10279     this.active = null;
10280     this.activateDelegate = this.activate.createDelegate(this);
10281
10282     this.addEvents({
10283         /**
10284          * @event tabchange
10285          * Fires when the active tab changes
10286          * @param {Roo.TabPanel} this
10287          * @param {Roo.TabPanelItem} activePanel The new active tab
10288          */
10289         "tabchange": true,
10290         /**
10291          * @event beforetabchange
10292          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10293          * @param {Roo.TabPanel} this
10294          * @param {Object} e Set cancel to true on this object to cancel the tab change
10295          * @param {Roo.TabPanelItem} tab The tab being changed to
10296          */
10297         "beforetabchange" : true
10298     });
10299
10300     Roo.EventManager.onWindowResize(this.onResize, this);
10301     this.cpad = this.el.getPadding("lr");
10302     this.hiddenCount = 0;
10303
10304
10305     // toolbar on the tabbar support...
10306     if (this.toolbar) {
10307         var tcfg = this.toolbar;
10308         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10309         this.toolbar = new Roo.Toolbar(tcfg);
10310         if (Roo.isSafari) {
10311             var tbl = tcfg.container.child('table', true);
10312             tbl.setAttribute('width', '100%');
10313         }
10314         
10315     }
10316    
10317
10318
10319     Roo.TabPanel.superclass.constructor.call(this);
10320 };
10321
10322 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10323     /*
10324      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10325      */
10326     tabPosition : "top",
10327     /*
10328      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10329      */
10330     currentTabWidth : 0,
10331     /*
10332      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10333      */
10334     minTabWidth : 40,
10335     /*
10336      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10337      */
10338     maxTabWidth : 250,
10339     /*
10340      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10341      */
10342     preferredTabWidth : 175,
10343     /*
10344      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10345      */
10346     resizeTabs : false,
10347     /*
10348      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10349      */
10350     monitorResize : true,
10351     /*
10352      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10353      */
10354     toolbar : false,
10355
10356     /**
10357      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10358      * @param {String} id The id of the div to use <b>or create</b>
10359      * @param {String} text The text for the tab
10360      * @param {String} content (optional) Content to put in the TabPanelItem body
10361      * @param {Boolean} closable (optional) True to create a close icon on the tab
10362      * @return {Roo.TabPanelItem} The created TabPanelItem
10363      */
10364     addTab : function(id, text, content, closable){
10365         var item = new Roo.TabPanelItem(this, id, text, closable);
10366         this.addTabItem(item);
10367         if(content){
10368             item.setContent(content);
10369         }
10370         return item;
10371     },
10372
10373     /**
10374      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10375      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10376      * @return {Roo.TabPanelItem}
10377      */
10378     getTab : function(id){
10379         return this.items[id];
10380     },
10381
10382     /**
10383      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10384      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10385      */
10386     hideTab : function(id){
10387         var t = this.items[id];
10388         if(!t.isHidden()){
10389            t.setHidden(true);
10390            this.hiddenCount++;
10391            this.autoSizeTabs();
10392         }
10393     },
10394
10395     /**
10396      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10397      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10398      */
10399     unhideTab : function(id){
10400         var t = this.items[id];
10401         if(t.isHidden()){
10402            t.setHidden(false);
10403            this.hiddenCount--;
10404            this.autoSizeTabs();
10405         }
10406     },
10407
10408     /**
10409      * Adds an existing {@link Roo.TabPanelItem}.
10410      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10411      */
10412     addTabItem : function(item){
10413         this.items[item.id] = item;
10414         this.items.push(item);
10415         if(this.resizeTabs){
10416            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10417            this.autoSizeTabs();
10418         }else{
10419             item.autoSize();
10420         }
10421     },
10422
10423     /**
10424      * Removes a {@link Roo.TabPanelItem}.
10425      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10426      */
10427     removeTab : function(id){
10428         var items = this.items;
10429         var tab = items[id];
10430         if(!tab) { return; }
10431         var index = items.indexOf(tab);
10432         if(this.active == tab && items.length > 1){
10433             var newTab = this.getNextAvailable(index);
10434             if(newTab) {
10435                 newTab.activate();
10436             }
10437         }
10438         this.stripEl.dom.removeChild(tab.pnode.dom);
10439         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10440             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10441         }
10442         items.splice(index, 1);
10443         delete this.items[tab.id];
10444         tab.fireEvent("close", tab);
10445         tab.purgeListeners();
10446         this.autoSizeTabs();
10447     },
10448
10449     getNextAvailable : function(start){
10450         var items = this.items;
10451         var index = start;
10452         // look for a next tab that will slide over to
10453         // replace the one being removed
10454         while(index < items.length){
10455             var item = items[++index];
10456             if(item && !item.isHidden()){
10457                 return item;
10458             }
10459         }
10460         // if one isn't found select the previous tab (on the left)
10461         index = start;
10462         while(index >= 0){
10463             var item = items[--index];
10464             if(item && !item.isHidden()){
10465                 return item;
10466             }
10467         }
10468         return null;
10469     },
10470
10471     /**
10472      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10473      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10474      */
10475     disableTab : function(id){
10476         var tab = this.items[id];
10477         if(tab && this.active != tab){
10478             tab.disable();
10479         }
10480     },
10481
10482     /**
10483      * Enables a {@link Roo.TabPanelItem} that is disabled.
10484      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10485      */
10486     enableTab : function(id){
10487         var tab = this.items[id];
10488         tab.enable();
10489     },
10490
10491     /**
10492      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10493      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10494      * @return {Roo.TabPanelItem} The TabPanelItem.
10495      */
10496     activate : function(id){
10497         var tab = this.items[id];
10498         if(!tab){
10499             return null;
10500         }
10501         if(tab == this.active || tab.disabled){
10502             return tab;
10503         }
10504         var e = {};
10505         this.fireEvent("beforetabchange", this, e, tab);
10506         if(e.cancel !== true && !tab.disabled){
10507             if(this.active){
10508                 this.active.hide();
10509             }
10510             this.active = this.items[id];
10511             this.active.show();
10512             this.fireEvent("tabchange", this, this.active);
10513         }
10514         return tab;
10515     },
10516
10517     /**
10518      * Gets the active {@link Roo.TabPanelItem}.
10519      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10520      */
10521     getActiveTab : function(){
10522         return this.active;
10523     },
10524
10525     /**
10526      * Updates the tab body element to fit the height of the container element
10527      * for overflow scrolling
10528      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10529      */
10530     syncHeight : function(targetHeight){
10531         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10532         var bm = this.bodyEl.getMargins();
10533         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10534         this.bodyEl.setHeight(newHeight);
10535         return newHeight;
10536     },
10537
10538     onResize : function(){
10539         if(this.monitorResize){
10540             this.autoSizeTabs();
10541         }
10542     },
10543
10544     /**
10545      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10546      */
10547     beginUpdate : function(){
10548         this.updating = true;
10549     },
10550
10551     /**
10552      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10553      */
10554     endUpdate : function(){
10555         this.updating = false;
10556         this.autoSizeTabs();
10557     },
10558
10559     /**
10560      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10561      */
10562     autoSizeTabs : function(){
10563         var count = this.items.length;
10564         var vcount = count - this.hiddenCount;
10565         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10566         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10567         var availWidth = Math.floor(w / vcount);
10568         var b = this.stripBody;
10569         if(b.getWidth() > w){
10570             var tabs = this.items;
10571             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10572             if(availWidth < this.minTabWidth){
10573                 /*if(!this.sleft){    // incomplete scrolling code
10574                     this.createScrollButtons();
10575                 }
10576                 this.showScroll();
10577                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10578             }
10579         }else{
10580             if(this.currentTabWidth < this.preferredTabWidth){
10581                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10582             }
10583         }
10584     },
10585
10586     /**
10587      * Returns the number of tabs in this TabPanel.
10588      * @return {Number}
10589      */
10590      getCount : function(){
10591          return this.items.length;
10592      },
10593
10594     /**
10595      * Resizes all the tabs to the passed width
10596      * @param {Number} The new width
10597      */
10598     setTabWidth : function(width){
10599         this.currentTabWidth = width;
10600         for(var i = 0, len = this.items.length; i < len; i++) {
10601                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10602         }
10603     },
10604
10605     /**
10606      * Destroys this TabPanel
10607      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10608      */
10609     destroy : function(removeEl){
10610         Roo.EventManager.removeResizeListener(this.onResize, this);
10611         for(var i = 0, len = this.items.length; i < len; i++){
10612             this.items[i].purgeListeners();
10613         }
10614         if(removeEl === true){
10615             this.el.update("");
10616             this.el.remove();
10617         }
10618     }
10619 });
10620
10621 /**
10622  * @class Roo.TabPanelItem
10623  * @extends Roo.util.Observable
10624  * Represents an individual item (tab plus body) in a TabPanel.
10625  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10626  * @param {String} id The id of this TabPanelItem
10627  * @param {String} text The text for the tab of this TabPanelItem
10628  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10629  */
10630 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10631     /**
10632      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10633      * @type Roo.TabPanel
10634      */
10635     this.tabPanel = tabPanel;
10636     /**
10637      * The id for this TabPanelItem
10638      * @type String
10639      */
10640     this.id = id;
10641     /** @private */
10642     this.disabled = false;
10643     /** @private */
10644     this.text = text;
10645     /** @private */
10646     this.loaded = false;
10647     this.closable = closable;
10648
10649     /**
10650      * The body element for this TabPanelItem.
10651      * @type Roo.Element
10652      */
10653     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10654     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10655     this.bodyEl.setStyle("display", "block");
10656     this.bodyEl.setStyle("zoom", "1");
10657     this.hideAction();
10658
10659     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10660     /** @private */
10661     this.el = Roo.get(els.el, true);
10662     this.inner = Roo.get(els.inner, true);
10663     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10664     this.pnode = Roo.get(els.el.parentNode, true);
10665     this.el.on("mousedown", this.onTabMouseDown, this);
10666     this.el.on("click", this.onTabClick, this);
10667     /** @private */
10668     if(closable){
10669         var c = Roo.get(els.close, true);
10670         c.dom.title = this.closeText;
10671         c.addClassOnOver("close-over");
10672         c.on("click", this.closeClick, this);
10673      }
10674
10675     this.addEvents({
10676          /**
10677          * @event activate
10678          * Fires when this tab becomes the active tab.
10679          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10680          * @param {Roo.TabPanelItem} this
10681          */
10682         "activate": true,
10683         /**
10684          * @event beforeclose
10685          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10686          * @param {Roo.TabPanelItem} this
10687          * @param {Object} e Set cancel to true on this object to cancel the close.
10688          */
10689         "beforeclose": true,
10690         /**
10691          * @event close
10692          * Fires when this tab is closed.
10693          * @param {Roo.TabPanelItem} this
10694          */
10695          "close": true,
10696         /**
10697          * @event deactivate
10698          * Fires when this tab is no longer the active tab.
10699          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10700          * @param {Roo.TabPanelItem} this
10701          */
10702          "deactivate" : true
10703     });
10704     this.hidden = false;
10705
10706     Roo.TabPanelItem.superclass.constructor.call(this);
10707 };
10708
10709 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10710     purgeListeners : function(){
10711        Roo.util.Observable.prototype.purgeListeners.call(this);
10712        this.el.removeAllListeners();
10713     },
10714     /**
10715      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10716      */
10717     show : function(){
10718         this.pnode.addClass("on");
10719         this.showAction();
10720         if(Roo.isOpera){
10721             this.tabPanel.stripWrap.repaint();
10722         }
10723         this.fireEvent("activate", this.tabPanel, this);
10724     },
10725
10726     /**
10727      * Returns true if this tab is the active tab.
10728      * @return {Boolean}
10729      */
10730     isActive : function(){
10731         return this.tabPanel.getActiveTab() == this;
10732     },
10733
10734     /**
10735      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10736      */
10737     hide : function(){
10738         this.pnode.removeClass("on");
10739         this.hideAction();
10740         this.fireEvent("deactivate", this.tabPanel, this);
10741     },
10742
10743     hideAction : function(){
10744         this.bodyEl.hide();
10745         this.bodyEl.setStyle("position", "absolute");
10746         this.bodyEl.setLeft("-20000px");
10747         this.bodyEl.setTop("-20000px");
10748     },
10749
10750     showAction : function(){
10751         this.bodyEl.setStyle("position", "relative");
10752         this.bodyEl.setTop("");
10753         this.bodyEl.setLeft("");
10754         this.bodyEl.show();
10755     },
10756
10757     /**
10758      * Set the tooltip for the tab.
10759      * @param {String} tooltip The tab's tooltip
10760      */
10761     setTooltip : function(text){
10762         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10763             this.textEl.dom.qtip = text;
10764             this.textEl.dom.removeAttribute('title');
10765         }else{
10766             this.textEl.dom.title = text;
10767         }
10768     },
10769
10770     onTabClick : function(e){
10771         e.preventDefault();
10772         this.tabPanel.activate(this.id);
10773     },
10774
10775     onTabMouseDown : function(e){
10776         e.preventDefault();
10777         this.tabPanel.activate(this.id);
10778     },
10779
10780     getWidth : function(){
10781         return this.inner.getWidth();
10782     },
10783
10784     setWidth : function(width){
10785         var iwidth = width - this.pnode.getPadding("lr");
10786         this.inner.setWidth(iwidth);
10787         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10788         this.pnode.setWidth(width);
10789     },
10790
10791     /**
10792      * Show or hide the tab
10793      * @param {Boolean} hidden True to hide or false to show.
10794      */
10795     setHidden : function(hidden){
10796         this.hidden = hidden;
10797         this.pnode.setStyle("display", hidden ? "none" : "");
10798     },
10799
10800     /**
10801      * Returns true if this tab is "hidden"
10802      * @return {Boolean}
10803      */
10804     isHidden : function(){
10805         return this.hidden;
10806     },
10807
10808     /**
10809      * Returns the text for this tab
10810      * @return {String}
10811      */
10812     getText : function(){
10813         return this.text;
10814     },
10815
10816     autoSize : function(){
10817         //this.el.beginMeasure();
10818         this.textEl.setWidth(1);
10819         /*
10820          *  #2804 [new] Tabs in Roojs
10821          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10822          */
10823         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10824         //this.el.endMeasure();
10825     },
10826
10827     /**
10828      * Sets the text for the tab (Note: this also sets the tooltip text)
10829      * @param {String} text The tab's text and tooltip
10830      */
10831     setText : function(text){
10832         this.text = text;
10833         this.textEl.update(text);
10834         this.setTooltip(text);
10835         if(!this.tabPanel.resizeTabs){
10836             this.autoSize();
10837         }
10838     },
10839     /**
10840      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10841      */
10842     activate : function(){
10843         this.tabPanel.activate(this.id);
10844     },
10845
10846     /**
10847      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10848      */
10849     disable : function(){
10850         if(this.tabPanel.active != this){
10851             this.disabled = true;
10852             this.pnode.addClass("disabled");
10853         }
10854     },
10855
10856     /**
10857      * Enables this TabPanelItem if it was previously disabled.
10858      */
10859     enable : function(){
10860         this.disabled = false;
10861         this.pnode.removeClass("disabled");
10862     },
10863
10864     /**
10865      * Sets the content for this TabPanelItem.
10866      * @param {String} content The content
10867      * @param {Boolean} loadScripts true to look for and load scripts
10868      */
10869     setContent : function(content, loadScripts){
10870         this.bodyEl.update(content, loadScripts);
10871     },
10872
10873     /**
10874      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10875      * @return {Roo.UpdateManager} The UpdateManager
10876      */
10877     getUpdateManager : function(){
10878         return this.bodyEl.getUpdateManager();
10879     },
10880
10881     /**
10882      * Set a URL to be used to load the content for this TabPanelItem.
10883      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10884      * @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)
10885      * @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)
10886      * @return {Roo.UpdateManager} The UpdateManager
10887      */
10888     setUrl : function(url, params, loadOnce){
10889         if(this.refreshDelegate){
10890             this.un('activate', this.refreshDelegate);
10891         }
10892         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10893         this.on("activate", this.refreshDelegate);
10894         return this.bodyEl.getUpdateManager();
10895     },
10896
10897     /** @private */
10898     _handleRefresh : function(url, params, loadOnce){
10899         if(!loadOnce || !this.loaded){
10900             var updater = this.bodyEl.getUpdateManager();
10901             updater.update(url, params, this._setLoaded.createDelegate(this));
10902         }
10903     },
10904
10905     /**
10906      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10907      *   Will fail silently if the setUrl method has not been called.
10908      *   This does not activate the panel, just updates its content.
10909      */
10910     refresh : function(){
10911         if(this.refreshDelegate){
10912            this.loaded = false;
10913            this.refreshDelegate();
10914         }
10915     },
10916
10917     /** @private */
10918     _setLoaded : function(){
10919         this.loaded = true;
10920     },
10921
10922     /** @private */
10923     closeClick : function(e){
10924         var o = {};
10925         e.stopEvent();
10926         this.fireEvent("beforeclose", this, o);
10927         if(o.cancel !== true){
10928             this.tabPanel.removeTab(this.id);
10929         }
10930     },
10931     /**
10932      * The text displayed in the tooltip for the close icon.
10933      * @type String
10934      */
10935     closeText : "Close this tab"
10936 });
10937
10938 /** @private */
10939 Roo.TabPanel.prototype.createStrip = function(container){
10940     var strip = document.createElement("div");
10941     strip.className = "x-tabs-wrap";
10942     container.appendChild(strip);
10943     return strip;
10944 };
10945 /** @private */
10946 Roo.TabPanel.prototype.createStripList = function(strip){
10947     // div wrapper for retard IE
10948     // returns the "tr" element.
10949     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10950         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10951         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10952     return strip.firstChild.firstChild.firstChild.firstChild;
10953 };
10954 /** @private */
10955 Roo.TabPanel.prototype.createBody = function(container){
10956     var body = document.createElement("div");
10957     Roo.id(body, "tab-body");
10958     Roo.fly(body).addClass("x-tabs-body");
10959     container.appendChild(body);
10960     return body;
10961 };
10962 /** @private */
10963 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10964     var body = Roo.getDom(id);
10965     if(!body){
10966         body = document.createElement("div");
10967         body.id = id;
10968     }
10969     Roo.fly(body).addClass("x-tabs-item-body");
10970     bodyEl.insertBefore(body, bodyEl.firstChild);
10971     return body;
10972 };
10973 /** @private */
10974 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10975     var td = document.createElement("td");
10976     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10977     //stripEl.appendChild(td);
10978     if(closable){
10979         td.className = "x-tabs-closable";
10980         if(!this.closeTpl){
10981             this.closeTpl = new Roo.Template(
10982                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10983                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10984                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10985             );
10986         }
10987         var el = this.closeTpl.overwrite(td, {"text": text});
10988         var close = el.getElementsByTagName("div")[0];
10989         var inner = el.getElementsByTagName("em")[0];
10990         return {"el": el, "close": close, "inner": inner};
10991     } else {
10992         if(!this.tabTpl){
10993             this.tabTpl = new Roo.Template(
10994                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10995                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
10996             );
10997         }
10998         var el = this.tabTpl.overwrite(td, {"text": text});
10999         var inner = el.getElementsByTagName("em")[0];
11000         return {"el": el, "inner": inner};
11001     }
11002 };/*
11003  * Based on:
11004  * Ext JS Library 1.1.1
11005  * Copyright(c) 2006-2007, Ext JS, LLC.
11006  *
11007  * Originally Released Under LGPL - original licence link has changed is not relivant.
11008  *
11009  * Fork - LGPL
11010  * <script type="text/javascript">
11011  */
11012
11013 /**
11014  * @class Roo.Button
11015  * @extends Roo.util.Observable
11016  * Simple Button class
11017  * @cfg {String} text The button text
11018  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11019  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11020  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11021  * @cfg {Object} scope The scope of the handler
11022  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11023  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11024  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11025  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11026  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11027  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11028    applies if enableToggle = true)
11029  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11030  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11031   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11032  * @constructor
11033  * Create a new button
11034  * @param {Object} config The config object
11035  */
11036 Roo.Button = function(renderTo, config)
11037 {
11038     if (!config) {
11039         config = renderTo;
11040         renderTo = config.renderTo || false;
11041     }
11042     
11043     Roo.apply(this, config);
11044     this.addEvents({
11045         /**
11046              * @event click
11047              * Fires when this button is clicked
11048              * @param {Button} this
11049              * @param {EventObject} e The click event
11050              */
11051             "click" : true,
11052         /**
11053              * @event toggle
11054              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11055              * @param {Button} this
11056              * @param {Boolean} pressed
11057              */
11058             "toggle" : true,
11059         /**
11060              * @event mouseover
11061              * Fires when the mouse hovers over the button
11062              * @param {Button} this
11063              * @param {Event} e The event object
11064              */
11065         'mouseover' : true,
11066         /**
11067              * @event mouseout
11068              * Fires when the mouse exits the button
11069              * @param {Button} this
11070              * @param {Event} e The event object
11071              */
11072         'mouseout': true,
11073          /**
11074              * @event render
11075              * Fires when the button is rendered
11076              * @param {Button} this
11077              */
11078         'render': true
11079     });
11080     if(this.menu){
11081         this.menu = Roo.menu.MenuMgr.get(this.menu);
11082     }
11083     // register listeners first!!  - so render can be captured..
11084     Roo.util.Observable.call(this);
11085     if(renderTo){
11086         this.render(renderTo);
11087     }
11088     
11089   
11090 };
11091
11092 Roo.extend(Roo.Button, Roo.util.Observable, {
11093     /**
11094      * 
11095      */
11096     
11097     /**
11098      * Read-only. True if this button is hidden
11099      * @type Boolean
11100      */
11101     hidden : false,
11102     /**
11103      * Read-only. True if this button is disabled
11104      * @type Boolean
11105      */
11106     disabled : false,
11107     /**
11108      * Read-only. True if this button is pressed (only if enableToggle = true)
11109      * @type Boolean
11110      */
11111     pressed : false,
11112
11113     /**
11114      * @cfg {Number} tabIndex 
11115      * The DOM tabIndex for this button (defaults to undefined)
11116      */
11117     tabIndex : undefined,
11118
11119     /**
11120      * @cfg {Boolean} enableToggle
11121      * True to enable pressed/not pressed toggling (defaults to false)
11122      */
11123     enableToggle: false,
11124     /**
11125      * @cfg {Mixed} menu
11126      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11127      */
11128     menu : undefined,
11129     /**
11130      * @cfg {String} menuAlign
11131      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11132      */
11133     menuAlign : "tl-bl?",
11134
11135     /**
11136      * @cfg {String} iconCls
11137      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11138      */
11139     iconCls : undefined,
11140     /**
11141      * @cfg {String} type
11142      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11143      */
11144     type : 'button',
11145
11146     // private
11147     menuClassTarget: 'tr',
11148
11149     /**
11150      * @cfg {String} clickEvent
11151      * The type of event to map to the button's event handler (defaults to 'click')
11152      */
11153     clickEvent : 'click',
11154
11155     /**
11156      * @cfg {Boolean} handleMouseEvents
11157      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11158      */
11159     handleMouseEvents : true,
11160
11161     /**
11162      * @cfg {String} tooltipType
11163      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11164      */
11165     tooltipType : 'qtip',
11166
11167     /**
11168      * @cfg {String} cls
11169      * A CSS class to apply to the button's main element.
11170      */
11171     
11172     /**
11173      * @cfg {Roo.Template} template (Optional)
11174      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11175      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11176      * require code modifications if required elements (e.g. a button) aren't present.
11177      */
11178
11179     // private
11180     render : function(renderTo){
11181         var btn;
11182         if(this.hideParent){
11183             this.parentEl = Roo.get(renderTo);
11184         }
11185         if(!this.dhconfig){
11186             if(!this.template){
11187                 if(!Roo.Button.buttonTemplate){
11188                     // hideous table template
11189                     Roo.Button.buttonTemplate = new Roo.Template(
11190                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11191                         '<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>',
11192                         "</tr></tbody></table>");
11193                 }
11194                 this.template = Roo.Button.buttonTemplate;
11195             }
11196             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11197             var btnEl = btn.child("button:first");
11198             btnEl.on('focus', this.onFocus, this);
11199             btnEl.on('blur', this.onBlur, this);
11200             if(this.cls){
11201                 btn.addClass(this.cls);
11202             }
11203             if(this.icon){
11204                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11205             }
11206             if(this.iconCls){
11207                 btnEl.addClass(this.iconCls);
11208                 if(!this.cls){
11209                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11210                 }
11211             }
11212             if(this.tabIndex !== undefined){
11213                 btnEl.dom.tabIndex = this.tabIndex;
11214             }
11215             if(this.tooltip){
11216                 if(typeof this.tooltip == 'object'){
11217                     Roo.QuickTips.tips(Roo.apply({
11218                           target: btnEl.id
11219                     }, this.tooltip));
11220                 } else {
11221                     btnEl.dom[this.tooltipType] = this.tooltip;
11222                 }
11223             }
11224         }else{
11225             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11226         }
11227         this.el = btn;
11228         if(this.id){
11229             this.el.dom.id = this.el.id = this.id;
11230         }
11231         if(this.menu){
11232             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11233             this.menu.on("show", this.onMenuShow, this);
11234             this.menu.on("hide", this.onMenuHide, this);
11235         }
11236         btn.addClass("x-btn");
11237         if(Roo.isIE && !Roo.isIE7){
11238             this.autoWidth.defer(1, this);
11239         }else{
11240             this.autoWidth();
11241         }
11242         if(this.handleMouseEvents){
11243             btn.on("mouseover", this.onMouseOver, this);
11244             btn.on("mouseout", this.onMouseOut, this);
11245             btn.on("mousedown", this.onMouseDown, this);
11246         }
11247         btn.on(this.clickEvent, this.onClick, this);
11248         //btn.on("mouseup", this.onMouseUp, this);
11249         if(this.hidden){
11250             this.hide();
11251         }
11252         if(this.disabled){
11253             this.disable();
11254         }
11255         Roo.ButtonToggleMgr.register(this);
11256         if(this.pressed){
11257             this.el.addClass("x-btn-pressed");
11258         }
11259         if(this.repeat){
11260             var repeater = new Roo.util.ClickRepeater(btn,
11261                 typeof this.repeat == "object" ? this.repeat : {}
11262             );
11263             repeater.on("click", this.onClick,  this);
11264         }
11265         
11266         this.fireEvent('render', this);
11267         
11268     },
11269     /**
11270      * Returns the button's underlying element
11271      * @return {Roo.Element} The element
11272      */
11273     getEl : function(){
11274         return this.el;  
11275     },
11276     
11277     /**
11278      * Destroys this Button and removes any listeners.
11279      */
11280     destroy : function(){
11281         Roo.ButtonToggleMgr.unregister(this);
11282         this.el.removeAllListeners();
11283         this.purgeListeners();
11284         this.el.remove();
11285     },
11286
11287     // private
11288     autoWidth : function(){
11289         if(this.el){
11290             this.el.setWidth("auto");
11291             if(Roo.isIE7 && Roo.isStrict){
11292                 var ib = this.el.child('button');
11293                 if(ib && ib.getWidth() > 20){
11294                     ib.clip();
11295                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11296                 }
11297             }
11298             if(this.minWidth){
11299                 if(this.hidden){
11300                     this.el.beginMeasure();
11301                 }
11302                 if(this.el.getWidth() < this.minWidth){
11303                     this.el.setWidth(this.minWidth);
11304                 }
11305                 if(this.hidden){
11306                     this.el.endMeasure();
11307                 }
11308             }
11309         }
11310     },
11311
11312     /**
11313      * Assigns this button's click handler
11314      * @param {Function} handler The function to call when the button is clicked
11315      * @param {Object} scope (optional) Scope for the function passed in
11316      */
11317     setHandler : function(handler, scope){
11318         this.handler = handler;
11319         this.scope = scope;  
11320     },
11321     
11322     /**
11323      * Sets this button's text
11324      * @param {String} text The button text
11325      */
11326     setText : function(text){
11327         this.text = text;
11328         if(this.el){
11329             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11330         }
11331         this.autoWidth();
11332     },
11333     
11334     /**
11335      * Gets the text for this button
11336      * @return {String} The button text
11337      */
11338     getText : function(){
11339         return this.text;  
11340     },
11341     
11342     /**
11343      * Show this button
11344      */
11345     show: function(){
11346         this.hidden = false;
11347         if(this.el){
11348             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11349         }
11350     },
11351     
11352     /**
11353      * Hide this button
11354      */
11355     hide: function(){
11356         this.hidden = true;
11357         if(this.el){
11358             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11359         }
11360     },
11361     
11362     /**
11363      * Convenience function for boolean show/hide
11364      * @param {Boolean} visible True to show, false to hide
11365      */
11366     setVisible: function(visible){
11367         if(visible) {
11368             this.show();
11369         }else{
11370             this.hide();
11371         }
11372     },
11373     
11374     /**
11375      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11376      * @param {Boolean} state (optional) Force a particular state
11377      */
11378     toggle : function(state){
11379         state = state === undefined ? !this.pressed : state;
11380         if(state != this.pressed){
11381             if(state){
11382                 this.el.addClass("x-btn-pressed");
11383                 this.pressed = true;
11384                 this.fireEvent("toggle", this, true);
11385             }else{
11386                 this.el.removeClass("x-btn-pressed");
11387                 this.pressed = false;
11388                 this.fireEvent("toggle", this, false);
11389             }
11390             if(this.toggleHandler){
11391                 this.toggleHandler.call(this.scope || this, this, state);
11392             }
11393         }
11394     },
11395     
11396     /**
11397      * Focus the button
11398      */
11399     focus : function(){
11400         this.el.child('button:first').focus();
11401     },
11402     
11403     /**
11404      * Disable this button
11405      */
11406     disable : function(){
11407         if(this.el){
11408             this.el.addClass("x-btn-disabled");
11409         }
11410         this.disabled = true;
11411     },
11412     
11413     /**
11414      * Enable this button
11415      */
11416     enable : function(){
11417         if(this.el){
11418             this.el.removeClass("x-btn-disabled");
11419         }
11420         this.disabled = false;
11421     },
11422
11423     /**
11424      * Convenience function for boolean enable/disable
11425      * @param {Boolean} enabled True to enable, false to disable
11426      */
11427     setDisabled : function(v){
11428         this[v !== true ? "enable" : "disable"]();
11429     },
11430
11431     // private
11432     onClick : function(e){
11433         if(e){
11434             e.preventDefault();
11435         }
11436         if(e.button != 0){
11437             return;
11438         }
11439         if(!this.disabled){
11440             if(this.enableToggle){
11441                 this.toggle();
11442             }
11443             if(this.menu && !this.menu.isVisible()){
11444                 this.menu.show(this.el, this.menuAlign);
11445             }
11446             this.fireEvent("click", this, e);
11447             if(this.handler){
11448                 this.el.removeClass("x-btn-over");
11449                 this.handler.call(this.scope || this, this, e);
11450             }
11451         }
11452     },
11453     // private
11454     onMouseOver : function(e){
11455         if(!this.disabled){
11456             this.el.addClass("x-btn-over");
11457             this.fireEvent('mouseover', this, e);
11458         }
11459     },
11460     // private
11461     onMouseOut : function(e){
11462         if(!e.within(this.el,  true)){
11463             this.el.removeClass("x-btn-over");
11464             this.fireEvent('mouseout', this, e);
11465         }
11466     },
11467     // private
11468     onFocus : function(e){
11469         if(!this.disabled){
11470             this.el.addClass("x-btn-focus");
11471         }
11472     },
11473     // private
11474     onBlur : function(e){
11475         this.el.removeClass("x-btn-focus");
11476     },
11477     // private
11478     onMouseDown : function(e){
11479         if(!this.disabled && e.button == 0){
11480             this.el.addClass("x-btn-click");
11481             Roo.get(document).on('mouseup', this.onMouseUp, this);
11482         }
11483     },
11484     // private
11485     onMouseUp : function(e){
11486         if(e.button == 0){
11487             this.el.removeClass("x-btn-click");
11488             Roo.get(document).un('mouseup', this.onMouseUp, this);
11489         }
11490     },
11491     // private
11492     onMenuShow : function(e){
11493         this.el.addClass("x-btn-menu-active");
11494     },
11495     // private
11496     onMenuHide : function(e){
11497         this.el.removeClass("x-btn-menu-active");
11498     }   
11499 });
11500
11501 // Private utility class used by Button
11502 Roo.ButtonToggleMgr = function(){
11503    var groups = {};
11504    
11505    function toggleGroup(btn, state){
11506        if(state){
11507            var g = groups[btn.toggleGroup];
11508            for(var i = 0, l = g.length; i < l; i++){
11509                if(g[i] != btn){
11510                    g[i].toggle(false);
11511                }
11512            }
11513        }
11514    }
11515    
11516    return {
11517        register : function(btn){
11518            if(!btn.toggleGroup){
11519                return;
11520            }
11521            var g = groups[btn.toggleGroup];
11522            if(!g){
11523                g = groups[btn.toggleGroup] = [];
11524            }
11525            g.push(btn);
11526            btn.on("toggle", toggleGroup);
11527        },
11528        
11529        unregister : function(btn){
11530            if(!btn.toggleGroup){
11531                return;
11532            }
11533            var g = groups[btn.toggleGroup];
11534            if(g){
11535                g.remove(btn);
11536                btn.un("toggle", toggleGroup);
11537            }
11538        }
11539    };
11540 }();/*
11541  * Based on:
11542  * Ext JS Library 1.1.1
11543  * Copyright(c) 2006-2007, Ext JS, LLC.
11544  *
11545  * Originally Released Under LGPL - original licence link has changed is not relivant.
11546  *
11547  * Fork - LGPL
11548  * <script type="text/javascript">
11549  */
11550  
11551 /**
11552  * @class Roo.SplitButton
11553  * @extends Roo.Button
11554  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11555  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11556  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11557  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11558  * @cfg {String} arrowTooltip The title attribute of the arrow
11559  * @constructor
11560  * Create a new menu button
11561  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11562  * @param {Object} config The config object
11563  */
11564 Roo.SplitButton = function(renderTo, config){
11565     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11566     /**
11567      * @event arrowclick
11568      * Fires when this button's arrow is clicked
11569      * @param {SplitButton} this
11570      * @param {EventObject} e The click event
11571      */
11572     this.addEvents({"arrowclick":true});
11573 };
11574
11575 Roo.extend(Roo.SplitButton, Roo.Button, {
11576     render : function(renderTo){
11577         // this is one sweet looking template!
11578         var tpl = new Roo.Template(
11579             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11580             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11581             '<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>',
11582             "</tbody></table></td><td>",
11583             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11584             '<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>',
11585             "</tbody></table></td></tr></table>"
11586         );
11587         var btn = tpl.append(renderTo, [this.text, this.type], true);
11588         var btnEl = btn.child("button");
11589         if(this.cls){
11590             btn.addClass(this.cls);
11591         }
11592         if(this.icon){
11593             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11594         }
11595         if(this.iconCls){
11596             btnEl.addClass(this.iconCls);
11597             if(!this.cls){
11598                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11599             }
11600         }
11601         this.el = btn;
11602         if(this.handleMouseEvents){
11603             btn.on("mouseover", this.onMouseOver, this);
11604             btn.on("mouseout", this.onMouseOut, this);
11605             btn.on("mousedown", this.onMouseDown, this);
11606             btn.on("mouseup", this.onMouseUp, this);
11607         }
11608         btn.on(this.clickEvent, this.onClick, this);
11609         if(this.tooltip){
11610             if(typeof this.tooltip == 'object'){
11611                 Roo.QuickTips.tips(Roo.apply({
11612                       target: btnEl.id
11613                 }, this.tooltip));
11614             } else {
11615                 btnEl.dom[this.tooltipType] = this.tooltip;
11616             }
11617         }
11618         if(this.arrowTooltip){
11619             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11620         }
11621         if(this.hidden){
11622             this.hide();
11623         }
11624         if(this.disabled){
11625             this.disable();
11626         }
11627         if(this.pressed){
11628             this.el.addClass("x-btn-pressed");
11629         }
11630         if(Roo.isIE && !Roo.isIE7){
11631             this.autoWidth.defer(1, this);
11632         }else{
11633             this.autoWidth();
11634         }
11635         if(this.menu){
11636             this.menu.on("show", this.onMenuShow, this);
11637             this.menu.on("hide", this.onMenuHide, this);
11638         }
11639         this.fireEvent('render', this);
11640     },
11641
11642     // private
11643     autoWidth : function(){
11644         if(this.el){
11645             var tbl = this.el.child("table:first");
11646             var tbl2 = this.el.child("table:last");
11647             this.el.setWidth("auto");
11648             tbl.setWidth("auto");
11649             if(Roo.isIE7 && Roo.isStrict){
11650                 var ib = this.el.child('button:first');
11651                 if(ib && ib.getWidth() > 20){
11652                     ib.clip();
11653                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11654                 }
11655             }
11656             if(this.minWidth){
11657                 if(this.hidden){
11658                     this.el.beginMeasure();
11659                 }
11660                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11661                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11662                 }
11663                 if(this.hidden){
11664                     this.el.endMeasure();
11665                 }
11666             }
11667             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11668         } 
11669     },
11670     /**
11671      * Sets this button's click handler
11672      * @param {Function} handler The function to call when the button is clicked
11673      * @param {Object} scope (optional) Scope for the function passed above
11674      */
11675     setHandler : function(handler, scope){
11676         this.handler = handler;
11677         this.scope = scope;  
11678     },
11679     
11680     /**
11681      * Sets this button's arrow click handler
11682      * @param {Function} handler The function to call when the arrow is clicked
11683      * @param {Object} scope (optional) Scope for the function passed above
11684      */
11685     setArrowHandler : function(handler, scope){
11686         this.arrowHandler = handler;
11687         this.scope = scope;  
11688     },
11689     
11690     /**
11691      * Focus the button
11692      */
11693     focus : function(){
11694         if(this.el){
11695             this.el.child("button:first").focus();
11696         }
11697     },
11698
11699     // private
11700     onClick : function(e){
11701         e.preventDefault();
11702         if(!this.disabled){
11703             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11704                 if(this.menu && !this.menu.isVisible()){
11705                     this.menu.show(this.el, this.menuAlign);
11706                 }
11707                 this.fireEvent("arrowclick", this, e);
11708                 if(this.arrowHandler){
11709                     this.arrowHandler.call(this.scope || this, this, e);
11710                 }
11711             }else{
11712                 this.fireEvent("click", this, e);
11713                 if(this.handler){
11714                     this.handler.call(this.scope || this, this, e);
11715                 }
11716             }
11717         }
11718     },
11719     // private
11720     onMouseDown : function(e){
11721         if(!this.disabled){
11722             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11723         }
11724     },
11725     // private
11726     onMouseUp : function(e){
11727         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11728     }   
11729 });
11730
11731
11732 // backwards compat
11733 Roo.MenuButton = Roo.SplitButton;/*
11734  * Based on:
11735  * Ext JS Library 1.1.1
11736  * Copyright(c) 2006-2007, Ext JS, LLC.
11737  *
11738  * Originally Released Under LGPL - original licence link has changed is not relivant.
11739  *
11740  * Fork - LGPL
11741  * <script type="text/javascript">
11742  */
11743
11744 /**
11745  * @class Roo.Toolbar
11746  * Basic Toolbar class.
11747  * @constructor
11748  * Creates a new Toolbar
11749  * @param {Object} container The config object
11750  */ 
11751 Roo.Toolbar = function(container, buttons, config)
11752 {
11753     /// old consturctor format still supported..
11754     if(container instanceof Array){ // omit the container for later rendering
11755         buttons = container;
11756         config = buttons;
11757         container = null;
11758     }
11759     if (typeof(container) == 'object' && container.xtype) {
11760         config = container;
11761         container = config.container;
11762         buttons = config.buttons || []; // not really - use items!!
11763     }
11764     var xitems = [];
11765     if (config && config.items) {
11766         xitems = config.items;
11767         delete config.items;
11768     }
11769     Roo.apply(this, config);
11770     this.buttons = buttons;
11771     
11772     if(container){
11773         this.render(container);
11774     }
11775     this.xitems = xitems;
11776     Roo.each(xitems, function(b) {
11777         this.add(b);
11778     }, this);
11779     
11780 };
11781
11782 Roo.Toolbar.prototype = {
11783     /**
11784      * @cfg {Array} items
11785      * array of button configs or elements to add (will be converted to a MixedCollection)
11786      */
11787     
11788     /**
11789      * @cfg {String/HTMLElement/Element} container
11790      * The id or element that will contain the toolbar
11791      */
11792     // private
11793     render : function(ct){
11794         this.el = Roo.get(ct);
11795         if(this.cls){
11796             this.el.addClass(this.cls);
11797         }
11798         // using a table allows for vertical alignment
11799         // 100% width is needed by Safari...
11800         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11801         this.tr = this.el.child("tr", true);
11802         var autoId = 0;
11803         this.items = new Roo.util.MixedCollection(false, function(o){
11804             return o.id || ("item" + (++autoId));
11805         });
11806         if(this.buttons){
11807             this.add.apply(this, this.buttons);
11808             delete this.buttons;
11809         }
11810     },
11811
11812     /**
11813      * Adds element(s) to the toolbar -- this function takes a variable number of 
11814      * arguments of mixed type and adds them to the toolbar.
11815      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11816      * <ul>
11817      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11818      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11819      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11820      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11821      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11822      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11823      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11824      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11825      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11826      * </ul>
11827      * @param {Mixed} arg2
11828      * @param {Mixed} etc.
11829      */
11830     add : function(){
11831         var a = arguments, l = a.length;
11832         for(var i = 0; i < l; i++){
11833             this._add(a[i]);
11834         }
11835     },
11836     // private..
11837     _add : function(el) {
11838         
11839         if (el.xtype) {
11840             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11841         }
11842         
11843         if (el.applyTo){ // some kind of form field
11844             return this.addField(el);
11845         } 
11846         if (el.render){ // some kind of Toolbar.Item
11847             return this.addItem(el);
11848         }
11849         if (typeof el == "string"){ // string
11850             if(el == "separator" || el == "-"){
11851                 return this.addSeparator();
11852             }
11853             if (el == " "){
11854                 return this.addSpacer();
11855             }
11856             if(el == "->"){
11857                 return this.addFill();
11858             }
11859             return this.addText(el);
11860             
11861         }
11862         if(el.tagName){ // element
11863             return this.addElement(el);
11864         }
11865         if(typeof el == "object"){ // must be button config?
11866             return this.addButton(el);
11867         }
11868         // and now what?!?!
11869         return false;
11870         
11871     },
11872     
11873     /**
11874      * Add an Xtype element
11875      * @param {Object} xtype Xtype Object
11876      * @return {Object} created Object
11877      */
11878     addxtype : function(e){
11879         return this.add(e);  
11880     },
11881     
11882     /**
11883      * Returns the Element for this toolbar.
11884      * @return {Roo.Element}
11885      */
11886     getEl : function(){
11887         return this.el;  
11888     },
11889     
11890     /**
11891      * Adds a separator
11892      * @return {Roo.Toolbar.Item} The separator item
11893      */
11894     addSeparator : function(){
11895         return this.addItem(new Roo.Toolbar.Separator());
11896     },
11897
11898     /**
11899      * Adds a spacer element
11900      * @return {Roo.Toolbar.Spacer} The spacer item
11901      */
11902     addSpacer : function(){
11903         return this.addItem(new Roo.Toolbar.Spacer());
11904     },
11905
11906     /**
11907      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11908      * @return {Roo.Toolbar.Fill} The fill item
11909      */
11910     addFill : function(){
11911         return this.addItem(new Roo.Toolbar.Fill());
11912     },
11913
11914     /**
11915      * Adds any standard HTML element to the toolbar
11916      * @param {String/HTMLElement/Element} el The element or id of the element to add
11917      * @return {Roo.Toolbar.Item} The element's item
11918      */
11919     addElement : function(el){
11920         return this.addItem(new Roo.Toolbar.Item(el));
11921     },
11922     /**
11923      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11924      * @type Roo.util.MixedCollection  
11925      */
11926     items : false,
11927      
11928     /**
11929      * Adds any Toolbar.Item or subclass
11930      * @param {Roo.Toolbar.Item} item
11931      * @return {Roo.Toolbar.Item} The item
11932      */
11933     addItem : function(item){
11934         var td = this.nextBlock();
11935         item.render(td);
11936         this.items.add(item);
11937         return item;
11938     },
11939     
11940     /**
11941      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11942      * @param {Object/Array} config A button config or array of configs
11943      * @return {Roo.Toolbar.Button/Array}
11944      */
11945     addButton : function(config){
11946         if(config instanceof Array){
11947             var buttons = [];
11948             for(var i = 0, len = config.length; i < len; i++) {
11949                 buttons.push(this.addButton(config[i]));
11950             }
11951             return buttons;
11952         }
11953         var b = config;
11954         if(!(config instanceof Roo.Toolbar.Button)){
11955             b = config.split ?
11956                 new Roo.Toolbar.SplitButton(config) :
11957                 new Roo.Toolbar.Button(config);
11958         }
11959         var td = this.nextBlock();
11960         b.render(td);
11961         this.items.add(b);
11962         return b;
11963     },
11964     
11965     /**
11966      * Adds text to the toolbar
11967      * @param {String} text The text to add
11968      * @return {Roo.Toolbar.Item} The element's item
11969      */
11970     addText : function(text){
11971         return this.addItem(new Roo.Toolbar.TextItem(text));
11972     },
11973     
11974     /**
11975      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11976      * @param {Number} index The index where the item is to be inserted
11977      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11978      * @return {Roo.Toolbar.Button/Item}
11979      */
11980     insertButton : function(index, item){
11981         if(item instanceof Array){
11982             var buttons = [];
11983             for(var i = 0, len = item.length; i < len; i++) {
11984                buttons.push(this.insertButton(index + i, item[i]));
11985             }
11986             return buttons;
11987         }
11988         if (!(item instanceof Roo.Toolbar.Button)){
11989            item = new Roo.Toolbar.Button(item);
11990         }
11991         var td = document.createElement("td");
11992         this.tr.insertBefore(td, this.tr.childNodes[index]);
11993         item.render(td);
11994         this.items.insert(index, item);
11995         return item;
11996     },
11997     
11998     /**
11999      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12000      * @param {Object} config
12001      * @return {Roo.Toolbar.Item} The element's item
12002      */
12003     addDom : function(config, returnEl){
12004         var td = this.nextBlock();
12005         Roo.DomHelper.overwrite(td, config);
12006         var ti = new Roo.Toolbar.Item(td.firstChild);
12007         ti.render(td);
12008         this.items.add(ti);
12009         return ti;
12010     },
12011
12012     /**
12013      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12014      * @type Roo.util.MixedCollection  
12015      */
12016     fields : false,
12017     
12018     /**
12019      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12020      * Note: the field should not have been rendered yet. For a field that has already been
12021      * rendered, use {@link #addElement}.
12022      * @param {Roo.form.Field} field
12023      * @return {Roo.ToolbarItem}
12024      */
12025      
12026       
12027     addField : function(field) {
12028         if (!this.fields) {
12029             var autoId = 0;
12030             this.fields = new Roo.util.MixedCollection(false, function(o){
12031                 return o.id || ("item" + (++autoId));
12032             });
12033
12034         }
12035         
12036         var td = this.nextBlock();
12037         field.render(td);
12038         var ti = new Roo.Toolbar.Item(td.firstChild);
12039         ti.render(td);
12040         this.items.add(ti);
12041         this.fields.add(field);
12042         return ti;
12043     },
12044     /**
12045      * Hide the toolbar
12046      * @method hide
12047      */
12048      
12049       
12050     hide : function()
12051     {
12052         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12053         this.el.child('div').hide();
12054     },
12055     /**
12056      * Show the toolbar
12057      * @method show
12058      */
12059     show : function()
12060     {
12061         this.el.child('div').show();
12062     },
12063       
12064     // private
12065     nextBlock : function(){
12066         var td = document.createElement("td");
12067         this.tr.appendChild(td);
12068         return td;
12069     },
12070
12071     // private
12072     destroy : function(){
12073         if(this.items){ // rendered?
12074             Roo.destroy.apply(Roo, this.items.items);
12075         }
12076         if(this.fields){ // rendered?
12077             Roo.destroy.apply(Roo, this.fields.items);
12078         }
12079         Roo.Element.uncache(this.el, this.tr);
12080     }
12081 };
12082
12083 /**
12084  * @class Roo.Toolbar.Item
12085  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12086  * @constructor
12087  * Creates a new Item
12088  * @param {HTMLElement} el 
12089  */
12090 Roo.Toolbar.Item = function(el){
12091     this.el = Roo.getDom(el);
12092     this.id = Roo.id(this.el);
12093     this.hidden = false;
12094     
12095     this.addEvents({
12096          /**
12097              * @event render
12098              * Fires when the button is rendered
12099              * @param {Button} this
12100              */
12101         'render': true
12102     });
12103 };
12104
12105 Roo.Toolbar.Item.prototype = {
12106     
12107     /**
12108      * Get this item's HTML Element
12109      * @return {HTMLElement}
12110      */
12111     getEl : function(){
12112        return this.el;  
12113     },
12114
12115     // private
12116     render : function(td){
12117         this.td = td;
12118         td.appendChild(this.el);
12119         this.fireEvent('render', this);
12120     },
12121     
12122     /**
12123      * Removes and destroys this item.
12124      */
12125     destroy : function(){
12126         this.td.parentNode.removeChild(this.td);
12127     },
12128     
12129     /**
12130      * Shows this item.
12131      */
12132     show: function(){
12133         this.hidden = false;
12134         this.td.style.display = "";
12135     },
12136     
12137     /**
12138      * Hides this item.
12139      */
12140     hide: function(){
12141         this.hidden = true;
12142         this.td.style.display = "none";
12143     },
12144     
12145     /**
12146      * Convenience function for boolean show/hide.
12147      * @param {Boolean} visible true to show/false to hide
12148      */
12149     setVisible: function(visible){
12150         if(visible) {
12151             this.show();
12152         }else{
12153             this.hide();
12154         }
12155     },
12156     
12157     /**
12158      * Try to focus this item.
12159      */
12160     focus : function(){
12161         Roo.fly(this.el).focus();
12162     },
12163     
12164     /**
12165      * Disables this item.
12166      */
12167     disable : function(){
12168         Roo.fly(this.td).addClass("x-item-disabled");
12169         this.disabled = true;
12170         this.el.disabled = true;
12171     },
12172     
12173     /**
12174      * Enables this item.
12175      */
12176     enable : function(){
12177         Roo.fly(this.td).removeClass("x-item-disabled");
12178         this.disabled = false;
12179         this.el.disabled = false;
12180     }
12181 };
12182
12183
12184 /**
12185  * @class Roo.Toolbar.Separator
12186  * @extends Roo.Toolbar.Item
12187  * A simple toolbar separator class
12188  * @constructor
12189  * Creates a new Separator
12190  */
12191 Roo.Toolbar.Separator = function(){
12192     var s = document.createElement("span");
12193     s.className = "ytb-sep";
12194     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12195 };
12196 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12197     enable:Roo.emptyFn,
12198     disable:Roo.emptyFn,
12199     focus:Roo.emptyFn
12200 });
12201
12202 /**
12203  * @class Roo.Toolbar.Spacer
12204  * @extends Roo.Toolbar.Item
12205  * A simple element that adds extra horizontal space to a toolbar.
12206  * @constructor
12207  * Creates a new Spacer
12208  */
12209 Roo.Toolbar.Spacer = function(){
12210     var s = document.createElement("div");
12211     s.className = "ytb-spacer";
12212     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12213 };
12214 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12215     enable:Roo.emptyFn,
12216     disable:Roo.emptyFn,
12217     focus:Roo.emptyFn
12218 });
12219
12220 /**
12221  * @class Roo.Toolbar.Fill
12222  * @extends Roo.Toolbar.Spacer
12223  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12224  * @constructor
12225  * Creates a new Spacer
12226  */
12227 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12228     // private
12229     render : function(td){
12230         td.style.width = '100%';
12231         Roo.Toolbar.Fill.superclass.render.call(this, td);
12232     }
12233 });
12234
12235 /**
12236  * @class Roo.Toolbar.TextItem
12237  * @extends Roo.Toolbar.Item
12238  * A simple class that renders text directly into a toolbar.
12239  * @constructor
12240  * Creates a new TextItem
12241  * @param {String} text
12242  */
12243 Roo.Toolbar.TextItem = function(text){
12244     if (typeof(text) == 'object') {
12245         text = text.text;
12246     }
12247     var s = document.createElement("span");
12248     s.className = "ytb-text";
12249     s.innerHTML = text;
12250     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12251 };
12252 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12253     enable:Roo.emptyFn,
12254     disable:Roo.emptyFn,
12255     focus:Roo.emptyFn
12256 });
12257
12258 /**
12259  * @class Roo.Toolbar.Button
12260  * @extends Roo.Button
12261  * A button that renders into a toolbar.
12262  * @constructor
12263  * Creates a new Button
12264  * @param {Object} config A standard {@link Roo.Button} config object
12265  */
12266 Roo.Toolbar.Button = function(config){
12267     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12268 };
12269 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12270     render : function(td){
12271         this.td = td;
12272         Roo.Toolbar.Button.superclass.render.call(this, td);
12273     },
12274     
12275     /**
12276      * Removes and destroys this button
12277      */
12278     destroy : function(){
12279         Roo.Toolbar.Button.superclass.destroy.call(this);
12280         this.td.parentNode.removeChild(this.td);
12281     },
12282     
12283     /**
12284      * Shows this button
12285      */
12286     show: function(){
12287         this.hidden = false;
12288         this.td.style.display = "";
12289     },
12290     
12291     /**
12292      * Hides this button
12293      */
12294     hide: function(){
12295         this.hidden = true;
12296         this.td.style.display = "none";
12297     },
12298
12299     /**
12300      * Disables this item
12301      */
12302     disable : function(){
12303         Roo.fly(this.td).addClass("x-item-disabled");
12304         this.disabled = true;
12305     },
12306
12307     /**
12308      * Enables this item
12309      */
12310     enable : function(){
12311         Roo.fly(this.td).removeClass("x-item-disabled");
12312         this.disabled = false;
12313     }
12314 });
12315 // backwards compat
12316 Roo.ToolbarButton = Roo.Toolbar.Button;
12317
12318 /**
12319  * @class Roo.Toolbar.SplitButton
12320  * @extends Roo.SplitButton
12321  * A menu button that renders into a toolbar.
12322  * @constructor
12323  * Creates a new SplitButton
12324  * @param {Object} config A standard {@link Roo.SplitButton} config object
12325  */
12326 Roo.Toolbar.SplitButton = function(config){
12327     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12328 };
12329 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12330     render : function(td){
12331         this.td = td;
12332         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12333     },
12334     
12335     /**
12336      * Removes and destroys this button
12337      */
12338     destroy : function(){
12339         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12340         this.td.parentNode.removeChild(this.td);
12341     },
12342     
12343     /**
12344      * Shows this button
12345      */
12346     show: function(){
12347         this.hidden = false;
12348         this.td.style.display = "";
12349     },
12350     
12351     /**
12352      * Hides this button
12353      */
12354     hide: function(){
12355         this.hidden = true;
12356         this.td.style.display = "none";
12357     }
12358 });
12359
12360 // backwards compat
12361 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12362  * Based on:
12363  * Ext JS Library 1.1.1
12364  * Copyright(c) 2006-2007, Ext JS, LLC.
12365  *
12366  * Originally Released Under LGPL - original licence link has changed is not relivant.
12367  *
12368  * Fork - LGPL
12369  * <script type="text/javascript">
12370  */
12371  
12372 /**
12373  * @class Roo.PagingToolbar
12374  * @extends Roo.Toolbar
12375  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12376  * @constructor
12377  * Create a new PagingToolbar
12378  * @param {Object} config The config object
12379  */
12380 Roo.PagingToolbar = function(el, ds, config)
12381 {
12382     // old args format still supported... - xtype is prefered..
12383     if (typeof(el) == 'object' && el.xtype) {
12384         // created from xtype...
12385         config = el;
12386         ds = el.dataSource;
12387         el = config.container;
12388     }
12389     var items = [];
12390     if (config.items) {
12391         items = config.items;
12392         config.items = [];
12393     }
12394     
12395     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12396     this.ds = ds;
12397     this.cursor = 0;
12398     this.renderButtons(this.el);
12399     this.bind(ds);
12400     
12401     // supprot items array.
12402    
12403     Roo.each(items, function(e) {
12404         this.add(Roo.factory(e));
12405     },this);
12406     
12407 };
12408
12409 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12410     /**
12411      * @cfg {Roo.data.Store} dataSource
12412      * The underlying data store providing the paged data
12413      */
12414     /**
12415      * @cfg {String/HTMLElement/Element} container
12416      * container The id or element that will contain the toolbar
12417      */
12418     /**
12419      * @cfg {Boolean} displayInfo
12420      * True to display the displayMsg (defaults to false)
12421      */
12422     /**
12423      * @cfg {Number} pageSize
12424      * The number of records to display per page (defaults to 20)
12425      */
12426     pageSize: 20,
12427     /**
12428      * @cfg {String} displayMsg
12429      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12430      */
12431     displayMsg : 'Displaying {0} - {1} of {2}',
12432     /**
12433      * @cfg {String} emptyMsg
12434      * The message to display when no records are found (defaults to "No data to display")
12435      */
12436     emptyMsg : 'No data to display',
12437     /**
12438      * Customizable piece of the default paging text (defaults to "Page")
12439      * @type String
12440      */
12441     beforePageText : "Page",
12442     /**
12443      * Customizable piece of the default paging text (defaults to "of %0")
12444      * @type String
12445      */
12446     afterPageText : "of {0}",
12447     /**
12448      * Customizable piece of the default paging text (defaults to "First Page")
12449      * @type String
12450      */
12451     firstText : "First Page",
12452     /**
12453      * Customizable piece of the default paging text (defaults to "Previous Page")
12454      * @type String
12455      */
12456     prevText : "Previous Page",
12457     /**
12458      * Customizable piece of the default paging text (defaults to "Next Page")
12459      * @type String
12460      */
12461     nextText : "Next Page",
12462     /**
12463      * Customizable piece of the default paging text (defaults to "Last Page")
12464      * @type String
12465      */
12466     lastText : "Last Page",
12467     /**
12468      * Customizable piece of the default paging text (defaults to "Refresh")
12469      * @type String
12470      */
12471     refreshText : "Refresh",
12472
12473     // private
12474     renderButtons : function(el){
12475         Roo.PagingToolbar.superclass.render.call(this, el);
12476         this.first = this.addButton({
12477             tooltip: this.firstText,
12478             cls: "x-btn-icon x-grid-page-first",
12479             disabled: true,
12480             handler: this.onClick.createDelegate(this, ["first"])
12481         });
12482         this.prev = this.addButton({
12483             tooltip: this.prevText,
12484             cls: "x-btn-icon x-grid-page-prev",
12485             disabled: true,
12486             handler: this.onClick.createDelegate(this, ["prev"])
12487         });
12488         //this.addSeparator();
12489         this.add(this.beforePageText);
12490         this.field = Roo.get(this.addDom({
12491            tag: "input",
12492            type: "text",
12493            size: "3",
12494            value: "1",
12495            cls: "x-grid-page-number"
12496         }).el);
12497         this.field.on("keydown", this.onPagingKeydown, this);
12498         this.field.on("focus", function(){this.dom.select();});
12499         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12500         this.field.setHeight(18);
12501         //this.addSeparator();
12502         this.next = this.addButton({
12503             tooltip: this.nextText,
12504             cls: "x-btn-icon x-grid-page-next",
12505             disabled: true,
12506             handler: this.onClick.createDelegate(this, ["next"])
12507         });
12508         this.last = this.addButton({
12509             tooltip: this.lastText,
12510             cls: "x-btn-icon x-grid-page-last",
12511             disabled: true,
12512             handler: this.onClick.createDelegate(this, ["last"])
12513         });
12514         //this.addSeparator();
12515         this.loading = this.addButton({
12516             tooltip: this.refreshText,
12517             cls: "x-btn-icon x-grid-loading",
12518             handler: this.onClick.createDelegate(this, ["refresh"])
12519         });
12520
12521         if(this.displayInfo){
12522             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12523         }
12524     },
12525
12526     // private
12527     updateInfo : function(){
12528         if(this.displayEl){
12529             var count = this.ds.getCount();
12530             var msg = count == 0 ?
12531                 this.emptyMsg :
12532                 String.format(
12533                     this.displayMsg,
12534                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12535                 );
12536             this.displayEl.update(msg);
12537         }
12538     },
12539
12540     // private
12541     onLoad : function(ds, r, o){
12542        this.cursor = o.params ? o.params.start : 0;
12543        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12544
12545        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12546        this.field.dom.value = ap;
12547        this.first.setDisabled(ap == 1);
12548        this.prev.setDisabled(ap == 1);
12549        this.next.setDisabled(ap == ps);
12550        this.last.setDisabled(ap == ps);
12551        this.loading.enable();
12552        this.updateInfo();
12553     },
12554
12555     // private
12556     getPageData : function(){
12557         var total = this.ds.getTotalCount();
12558         return {
12559             total : total,
12560             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12561             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12562         };
12563     },
12564
12565     // private
12566     onLoadError : function(){
12567         this.loading.enable();
12568     },
12569
12570     // private
12571     onPagingKeydown : function(e){
12572         var k = e.getKey();
12573         var d = this.getPageData();
12574         if(k == e.RETURN){
12575             var v = this.field.dom.value, pageNum;
12576             if(!v || isNaN(pageNum = parseInt(v, 10))){
12577                 this.field.dom.value = d.activePage;
12578                 return;
12579             }
12580             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12581             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12582             e.stopEvent();
12583         }
12584         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))
12585         {
12586           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12587           this.field.dom.value = pageNum;
12588           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12589           e.stopEvent();
12590         }
12591         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12592         {
12593           var v = this.field.dom.value, pageNum; 
12594           var increment = (e.shiftKey) ? 10 : 1;
12595           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12596             increment *= -1;
12597           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12598             this.field.dom.value = d.activePage;
12599             return;
12600           }
12601           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12602           {
12603             this.field.dom.value = parseInt(v, 10) + increment;
12604             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12605             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12606           }
12607           e.stopEvent();
12608         }
12609     },
12610
12611     // private
12612     beforeLoad : function(){
12613         if(this.loading){
12614             this.loading.disable();
12615         }
12616     },
12617
12618     // private
12619     onClick : function(which){
12620         var ds = this.ds;
12621         switch(which){
12622             case "first":
12623                 ds.load({params:{start: 0, limit: this.pageSize}});
12624             break;
12625             case "prev":
12626                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12627             break;
12628             case "next":
12629                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12630             break;
12631             case "last":
12632                 var total = ds.getTotalCount();
12633                 var extra = total % this.pageSize;
12634                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12635                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12636             break;
12637             case "refresh":
12638                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12639             break;
12640         }
12641     },
12642
12643     /**
12644      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12645      * @param {Roo.data.Store} store The data store to unbind
12646      */
12647     unbind : function(ds){
12648         ds.un("beforeload", this.beforeLoad, this);
12649         ds.un("load", this.onLoad, this);
12650         ds.un("loadexception", this.onLoadError, this);
12651         ds.un("remove", this.updateInfo, this);
12652         ds.un("add", this.updateInfo, this);
12653         this.ds = undefined;
12654     },
12655
12656     /**
12657      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12658      * @param {Roo.data.Store} store The data store to bind
12659      */
12660     bind : function(ds){
12661         ds.on("beforeload", this.beforeLoad, this);
12662         ds.on("load", this.onLoad, this);
12663         ds.on("loadexception", this.onLoadError, this);
12664         ds.on("remove", this.updateInfo, this);
12665         ds.on("add", this.updateInfo, this);
12666         this.ds = ds;
12667     }
12668 });/*
12669  * Based on:
12670  * Ext JS Library 1.1.1
12671  * Copyright(c) 2006-2007, Ext JS, LLC.
12672  *
12673  * Originally Released Under LGPL - original licence link has changed is not relivant.
12674  *
12675  * Fork - LGPL
12676  * <script type="text/javascript">
12677  */
12678
12679 /**
12680  * @class Roo.Resizable
12681  * @extends Roo.util.Observable
12682  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12683  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12684  * 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
12685  * the element will be wrapped for you automatically.</p>
12686  * <p>Here is the list of valid resize handles:</p>
12687  * <pre>
12688 Value   Description
12689 ------  -------------------
12690  'n'     north
12691  's'     south
12692  'e'     east
12693  'w'     west
12694  'nw'    northwest
12695  'sw'    southwest
12696  'se'    southeast
12697  'ne'    northeast
12698  'hd'    horizontal drag
12699  'all'   all
12700 </pre>
12701  * <p>Here's an example showing the creation of a typical Resizable:</p>
12702  * <pre><code>
12703 var resizer = new Roo.Resizable("element-id", {
12704     handles: 'all',
12705     minWidth: 200,
12706     minHeight: 100,
12707     maxWidth: 500,
12708     maxHeight: 400,
12709     pinned: true
12710 });
12711 resizer.on("resize", myHandler);
12712 </code></pre>
12713  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12714  * resizer.east.setDisplayed(false);</p>
12715  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12716  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12717  * resize operation's new size (defaults to [0, 0])
12718  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12719  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12720  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12721  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12722  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12723  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12724  * @cfg {Number} width The width of the element in pixels (defaults to null)
12725  * @cfg {Number} height The height of the element in pixels (defaults to null)
12726  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12727  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12728  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12729  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12730  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12731  * in favor of the handles config option (defaults to false)
12732  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12733  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12734  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12735  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12736  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12737  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12738  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12739  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12740  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12741  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12742  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12743  * @constructor
12744  * Create a new resizable component
12745  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12746  * @param {Object} config configuration options
12747   */
12748 Roo.Resizable = function(el, config)
12749 {
12750     this.el = Roo.get(el);
12751
12752     if(config && config.wrap){
12753         config.resizeChild = this.el;
12754         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12755         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12756         this.el.setStyle("overflow", "hidden");
12757         this.el.setPositioning(config.resizeChild.getPositioning());
12758         config.resizeChild.clearPositioning();
12759         if(!config.width || !config.height){
12760             var csize = config.resizeChild.getSize();
12761             this.el.setSize(csize.width, csize.height);
12762         }
12763         if(config.pinned && !config.adjustments){
12764             config.adjustments = "auto";
12765         }
12766     }
12767
12768     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12769     this.proxy.unselectable();
12770     this.proxy.enableDisplayMode('block');
12771
12772     Roo.apply(this, config);
12773
12774     if(this.pinned){
12775         this.disableTrackOver = true;
12776         this.el.addClass("x-resizable-pinned");
12777     }
12778     // if the element isn't positioned, make it relative
12779     var position = this.el.getStyle("position");
12780     if(position != "absolute" && position != "fixed"){
12781         this.el.setStyle("position", "relative");
12782     }
12783     if(!this.handles){ // no handles passed, must be legacy style
12784         this.handles = 's,e,se';
12785         if(this.multiDirectional){
12786             this.handles += ',n,w';
12787         }
12788     }
12789     if(this.handles == "all"){
12790         this.handles = "n s e w ne nw se sw";
12791     }
12792     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12793     var ps = Roo.Resizable.positions;
12794     for(var i = 0, len = hs.length; i < len; i++){
12795         if(hs[i] && ps[hs[i]]){
12796             var pos = ps[hs[i]];
12797             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12798         }
12799     }
12800     // legacy
12801     this.corner = this.southeast;
12802     
12803     // updateBox = the box can move..
12804     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12805         this.updateBox = true;
12806     }
12807
12808     this.activeHandle = null;
12809
12810     if(this.resizeChild){
12811         if(typeof this.resizeChild == "boolean"){
12812             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12813         }else{
12814             this.resizeChild = Roo.get(this.resizeChild, true);
12815         }
12816     }
12817     
12818     if(this.adjustments == "auto"){
12819         var rc = this.resizeChild;
12820         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12821         if(rc && (hw || hn)){
12822             rc.position("relative");
12823             rc.setLeft(hw ? hw.el.getWidth() : 0);
12824             rc.setTop(hn ? hn.el.getHeight() : 0);
12825         }
12826         this.adjustments = [
12827             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12828             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12829         ];
12830     }
12831
12832     if(this.draggable){
12833         this.dd = this.dynamic ?
12834             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12835         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12836     }
12837
12838     // public events
12839     this.addEvents({
12840         /**
12841          * @event beforeresize
12842          * Fired before resize is allowed. Set enabled to false to cancel resize.
12843          * @param {Roo.Resizable} this
12844          * @param {Roo.EventObject} e The mousedown event
12845          */
12846         "beforeresize" : true,
12847         /**
12848          * @event resizing
12849          * Fired a resizing.
12850          * @param {Roo.Resizable} this
12851          * @param {Number} x The new x position
12852          * @param {Number} y The new y position
12853          * @param {Number} w The new w width
12854          * @param {Number} h The new h hight
12855          * @param {Roo.EventObject} e The mouseup event
12856          */
12857         "resizing" : true,
12858         /**
12859          * @event resize
12860          * Fired after a resize.
12861          * @param {Roo.Resizable} this
12862          * @param {Number} width The new width
12863          * @param {Number} height The new height
12864          * @param {Roo.EventObject} e The mouseup event
12865          */
12866         "resize" : true
12867     });
12868
12869     if(this.width !== null && this.height !== null){
12870         this.resizeTo(this.width, this.height);
12871     }else{
12872         this.updateChildSize();
12873     }
12874     if(Roo.isIE){
12875         this.el.dom.style.zoom = 1;
12876     }
12877     Roo.Resizable.superclass.constructor.call(this);
12878 };
12879
12880 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12881         resizeChild : false,
12882         adjustments : [0, 0],
12883         minWidth : 5,
12884         minHeight : 5,
12885         maxWidth : 10000,
12886         maxHeight : 10000,
12887         enabled : true,
12888         animate : false,
12889         duration : .35,
12890         dynamic : false,
12891         handles : false,
12892         multiDirectional : false,
12893         disableTrackOver : false,
12894         easing : 'easeOutStrong',
12895         widthIncrement : 0,
12896         heightIncrement : 0,
12897         pinned : false,
12898         width : null,
12899         height : null,
12900         preserveRatio : false,
12901         transparent: false,
12902         minX: 0,
12903         minY: 0,
12904         draggable: false,
12905
12906         /**
12907          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12908          */
12909         constrainTo: undefined,
12910         /**
12911          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12912          */
12913         resizeRegion: undefined,
12914
12915
12916     /**
12917      * Perform a manual resize
12918      * @param {Number} width
12919      * @param {Number} height
12920      */
12921     resizeTo : function(width, height){
12922         this.el.setSize(width, height);
12923         this.updateChildSize();
12924         this.fireEvent("resize", this, width, height, null);
12925     },
12926
12927     // private
12928     startSizing : function(e, handle){
12929         this.fireEvent("beforeresize", this, e);
12930         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12931
12932             if(!this.overlay){
12933                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12934                 this.overlay.unselectable();
12935                 this.overlay.enableDisplayMode("block");
12936                 this.overlay.on("mousemove", this.onMouseMove, this);
12937                 this.overlay.on("mouseup", this.onMouseUp, this);
12938             }
12939             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12940
12941             this.resizing = true;
12942             this.startBox = this.el.getBox();
12943             this.startPoint = e.getXY();
12944             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12945                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12946
12947             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12948             this.overlay.show();
12949
12950             if(this.constrainTo) {
12951                 var ct = Roo.get(this.constrainTo);
12952                 this.resizeRegion = ct.getRegion().adjust(
12953                     ct.getFrameWidth('t'),
12954                     ct.getFrameWidth('l'),
12955                     -ct.getFrameWidth('b'),
12956                     -ct.getFrameWidth('r')
12957                 );
12958             }
12959
12960             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12961             this.proxy.show();
12962             this.proxy.setBox(this.startBox);
12963             if(!this.dynamic){
12964                 this.proxy.setStyle('visibility', 'visible');
12965             }
12966         }
12967     },
12968
12969     // private
12970     onMouseDown : function(handle, e){
12971         if(this.enabled){
12972             e.stopEvent();
12973             this.activeHandle = handle;
12974             this.startSizing(e, handle);
12975         }
12976     },
12977
12978     // private
12979     onMouseUp : function(e){
12980         var size = this.resizeElement();
12981         this.resizing = false;
12982         this.handleOut();
12983         this.overlay.hide();
12984         this.proxy.hide();
12985         this.fireEvent("resize", this, size.width, size.height, e);
12986     },
12987
12988     // private
12989     updateChildSize : function(){
12990         
12991         if(this.resizeChild){
12992             var el = this.el;
12993             var child = this.resizeChild;
12994             var adj = this.adjustments;
12995             if(el.dom.offsetWidth){
12996                 var b = el.getSize(true);
12997                 child.setSize(b.width+adj[0], b.height+adj[1]);
12998             }
12999             // Second call here for IE
13000             // The first call enables instant resizing and
13001             // the second call corrects scroll bars if they
13002             // exist
13003             if(Roo.isIE){
13004                 setTimeout(function(){
13005                     if(el.dom.offsetWidth){
13006                         var b = el.getSize(true);
13007                         child.setSize(b.width+adj[0], b.height+adj[1]);
13008                     }
13009                 }, 10);
13010             }
13011         }
13012     },
13013
13014     // private
13015     snap : function(value, inc, min){
13016         if(!inc || !value) return value;
13017         var newValue = value;
13018         var m = value % inc;
13019         if(m > 0){
13020             if(m > (inc/2)){
13021                 newValue = value + (inc-m);
13022             }else{
13023                 newValue = value - m;
13024             }
13025         }
13026         return Math.max(min, newValue);
13027     },
13028
13029     // private
13030     resizeElement : function(){
13031         var box = this.proxy.getBox();
13032         if(this.updateBox){
13033             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13034         }else{
13035             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13036         }
13037         this.updateChildSize();
13038         if(!this.dynamic){
13039             this.proxy.hide();
13040         }
13041         return box;
13042     },
13043
13044     // private
13045     constrain : function(v, diff, m, mx){
13046         if(v - diff < m){
13047             diff = v - m;
13048         }else if(v - diff > mx){
13049             diff = mx - v;
13050         }
13051         return diff;
13052     },
13053
13054     // private
13055     onMouseMove : function(e){
13056         
13057         if(this.enabled){
13058             try{// try catch so if something goes wrong the user doesn't get hung
13059
13060             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13061                 return;
13062             }
13063
13064             //var curXY = this.startPoint;
13065             var curSize = this.curSize || this.startBox;
13066             var x = this.startBox.x, y = this.startBox.y;
13067             var ox = x, oy = y;
13068             var w = curSize.width, h = curSize.height;
13069             var ow = w, oh = h;
13070             var mw = this.minWidth, mh = this.minHeight;
13071             var mxw = this.maxWidth, mxh = this.maxHeight;
13072             var wi = this.widthIncrement;
13073             var hi = this.heightIncrement;
13074
13075             var eventXY = e.getXY();
13076             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13077             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13078
13079             var pos = this.activeHandle.position;
13080
13081             switch(pos){
13082                 case "east":
13083                     w += diffX;
13084                     w = Math.min(Math.max(mw, w), mxw);
13085                     break;
13086              
13087                 case "south":
13088                     h += diffY;
13089                     h = Math.min(Math.max(mh, h), mxh);
13090                     break;
13091                 case "southeast":
13092                     w += diffX;
13093                     h += diffY;
13094                     w = Math.min(Math.max(mw, w), mxw);
13095                     h = Math.min(Math.max(mh, h), mxh);
13096                     break;
13097                 case "north":
13098                     diffY = this.constrain(h, diffY, mh, mxh);
13099                     y += diffY;
13100                     h -= diffY;
13101                     break;
13102                 case "hdrag":
13103                     
13104                     if (wi) {
13105                         var adiffX = Math.abs(diffX);
13106                         var sub = (adiffX % wi); // how much 
13107                         if (sub > (wi/2)) { // far enough to snap
13108                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13109                         } else {
13110                             // remove difference.. 
13111                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13112                         }
13113                     }
13114                     x += diffX;
13115                     x = Math.max(this.minX, x);
13116                     break;
13117                 case "west":
13118                     diffX = this.constrain(w, diffX, mw, mxw);
13119                     x += diffX;
13120                     w -= diffX;
13121                     break;
13122                 case "northeast":
13123                     w += diffX;
13124                     w = Math.min(Math.max(mw, w), mxw);
13125                     diffY = this.constrain(h, diffY, mh, mxh);
13126                     y += diffY;
13127                     h -= diffY;
13128                     break;
13129                 case "northwest":
13130                     diffX = this.constrain(w, diffX, mw, mxw);
13131                     diffY = this.constrain(h, diffY, mh, mxh);
13132                     y += diffY;
13133                     h -= diffY;
13134                     x += diffX;
13135                     w -= diffX;
13136                     break;
13137                case "southwest":
13138                     diffX = this.constrain(w, diffX, mw, mxw);
13139                     h += diffY;
13140                     h = Math.min(Math.max(mh, h), mxh);
13141                     x += diffX;
13142                     w -= diffX;
13143                     break;
13144             }
13145
13146             var sw = this.snap(w, wi, mw);
13147             var sh = this.snap(h, hi, mh);
13148             if(sw != w || sh != h){
13149                 switch(pos){
13150                     case "northeast":
13151                         y -= sh - h;
13152                     break;
13153                     case "north":
13154                         y -= sh - h;
13155                         break;
13156                     case "southwest":
13157                         x -= sw - w;
13158                     break;
13159                     case "west":
13160                         x -= sw - w;
13161                         break;
13162                     case "northwest":
13163                         x -= sw - w;
13164                         y -= sh - h;
13165                     break;
13166                 }
13167                 w = sw;
13168                 h = sh;
13169             }
13170
13171             if(this.preserveRatio){
13172                 switch(pos){
13173                     case "southeast":
13174                     case "east":
13175                         h = oh * (w/ow);
13176                         h = Math.min(Math.max(mh, h), mxh);
13177                         w = ow * (h/oh);
13178                        break;
13179                     case "south":
13180                         w = ow * (h/oh);
13181                         w = Math.min(Math.max(mw, w), mxw);
13182                         h = oh * (w/ow);
13183                         break;
13184                     case "northeast":
13185                         w = ow * (h/oh);
13186                         w = Math.min(Math.max(mw, w), mxw);
13187                         h = oh * (w/ow);
13188                     break;
13189                     case "north":
13190                         var tw = w;
13191                         w = ow * (h/oh);
13192                         w = Math.min(Math.max(mw, w), mxw);
13193                         h = oh * (w/ow);
13194                         x += (tw - w) / 2;
13195                         break;
13196                     case "southwest":
13197                         h = oh * (w/ow);
13198                         h = Math.min(Math.max(mh, h), mxh);
13199                         var tw = w;
13200                         w = ow * (h/oh);
13201                         x += tw - w;
13202                         break;
13203                     case "west":
13204                         var th = h;
13205                         h = oh * (w/ow);
13206                         h = Math.min(Math.max(mh, h), mxh);
13207                         y += (th - h) / 2;
13208                         var tw = w;
13209                         w = ow * (h/oh);
13210                         x += tw - w;
13211                        break;
13212                     case "northwest":
13213                         var tw = w;
13214                         var th = h;
13215                         h = oh * (w/ow);
13216                         h = Math.min(Math.max(mh, h), mxh);
13217                         w = ow * (h/oh);
13218                         y += th - h;
13219                         x += tw - w;
13220                        break;
13221
13222                 }
13223             }
13224             if (pos == 'hdrag') {
13225                 w = ow;
13226             }
13227             this.proxy.setBounds(x, y, w, h);
13228             if(this.dynamic){
13229                 this.resizeElement();
13230             }
13231             }catch(e){}
13232         }
13233         this.fireEvent("resizing", this, x, y, w, h, e);
13234     },
13235
13236     // private
13237     handleOver : function(){
13238         if(this.enabled){
13239             this.el.addClass("x-resizable-over");
13240         }
13241     },
13242
13243     // private
13244     handleOut : function(){
13245         if(!this.resizing){
13246             this.el.removeClass("x-resizable-over");
13247         }
13248     },
13249
13250     /**
13251      * Returns the element this component is bound to.
13252      * @return {Roo.Element}
13253      */
13254     getEl : function(){
13255         return this.el;
13256     },
13257
13258     /**
13259      * Returns the resizeChild element (or null).
13260      * @return {Roo.Element}
13261      */
13262     getResizeChild : function(){
13263         return this.resizeChild;
13264     },
13265     groupHandler : function()
13266     {
13267         
13268     },
13269     /**
13270      * Destroys this resizable. If the element was wrapped and
13271      * removeEl is not true then the element remains.
13272      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13273      */
13274     destroy : function(removeEl){
13275         this.proxy.remove();
13276         if(this.overlay){
13277             this.overlay.removeAllListeners();
13278             this.overlay.remove();
13279         }
13280         var ps = Roo.Resizable.positions;
13281         for(var k in ps){
13282             if(typeof ps[k] != "function" && this[ps[k]]){
13283                 var h = this[ps[k]];
13284                 h.el.removeAllListeners();
13285                 h.el.remove();
13286             }
13287         }
13288         if(removeEl){
13289             this.el.update("");
13290             this.el.remove();
13291         }
13292     }
13293 });
13294
13295 // private
13296 // hash to map config positions to true positions
13297 Roo.Resizable.positions = {
13298     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13299     hd: "hdrag"
13300 };
13301
13302 // private
13303 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13304     if(!this.tpl){
13305         // only initialize the template if resizable is used
13306         var tpl = Roo.DomHelper.createTemplate(
13307             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13308         );
13309         tpl.compile();
13310         Roo.Resizable.Handle.prototype.tpl = tpl;
13311     }
13312     this.position = pos;
13313     this.rz = rz;
13314     // show north drag fro topdra
13315     var handlepos = pos == 'hdrag' ? 'north' : pos;
13316     
13317     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13318     if (pos == 'hdrag') {
13319         this.el.setStyle('cursor', 'pointer');
13320     }
13321     this.el.unselectable();
13322     if(transparent){
13323         this.el.setOpacity(0);
13324     }
13325     this.el.on("mousedown", this.onMouseDown, this);
13326     if(!disableTrackOver){
13327         this.el.on("mouseover", this.onMouseOver, this);
13328         this.el.on("mouseout", this.onMouseOut, this);
13329     }
13330 };
13331
13332 // private
13333 Roo.Resizable.Handle.prototype = {
13334     afterResize : function(rz){
13335         Roo.log('after?');
13336         // do nothing
13337     },
13338     // private
13339     onMouseDown : function(e){
13340         this.rz.onMouseDown(this, e);
13341     },
13342     // private
13343     onMouseOver : function(e){
13344         this.rz.handleOver(this, e);
13345     },
13346     // private
13347     onMouseOut : function(e){
13348         this.rz.handleOut(this, e);
13349     }
13350 };/*
13351  * Based on:
13352  * Ext JS Library 1.1.1
13353  * Copyright(c) 2006-2007, Ext JS, LLC.
13354  *
13355  * Originally Released Under LGPL - original licence link has changed is not relivant.
13356  *
13357  * Fork - LGPL
13358  * <script type="text/javascript">
13359  */
13360
13361 /**
13362  * @class Roo.Editor
13363  * @extends Roo.Component
13364  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13365  * @constructor
13366  * Create a new Editor
13367  * @param {Roo.form.Field} field The Field object (or descendant)
13368  * @param {Object} config The config object
13369  */
13370 Roo.Editor = function(field, config){
13371     Roo.Editor.superclass.constructor.call(this, config);
13372     this.field = field;
13373     this.addEvents({
13374         /**
13375              * @event beforestartedit
13376              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13377              * false from the handler of this event.
13378              * @param {Editor} this
13379              * @param {Roo.Element} boundEl The underlying element bound to this editor
13380              * @param {Mixed} value The field value being set
13381              */
13382         "beforestartedit" : true,
13383         /**
13384              * @event startedit
13385              * Fires when this editor is displayed
13386              * @param {Roo.Element} boundEl The underlying element bound to this editor
13387              * @param {Mixed} value The starting field value
13388              */
13389         "startedit" : true,
13390         /**
13391              * @event beforecomplete
13392              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13393              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13394              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13395              * event will not fire since no edit actually occurred.
13396              * @param {Editor} this
13397              * @param {Mixed} value The current field value
13398              * @param {Mixed} startValue The original field value
13399              */
13400         "beforecomplete" : true,
13401         /**
13402              * @event complete
13403              * Fires after editing is complete and any changed value has been written to the underlying field.
13404              * @param {Editor} this
13405              * @param {Mixed} value The current field value
13406              * @param {Mixed} startValue The original field value
13407              */
13408         "complete" : true,
13409         /**
13410          * @event specialkey
13411          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13412          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13413          * @param {Roo.form.Field} this
13414          * @param {Roo.EventObject} e The event object
13415          */
13416         "specialkey" : true
13417     });
13418 };
13419
13420 Roo.extend(Roo.Editor, Roo.Component, {
13421     /**
13422      * @cfg {Boolean/String} autosize
13423      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13424      * or "height" to adopt the height only (defaults to false)
13425      */
13426     /**
13427      * @cfg {Boolean} revertInvalid
13428      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13429      * validation fails (defaults to true)
13430      */
13431     /**
13432      * @cfg {Boolean} ignoreNoChange
13433      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13434      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13435      * will never be ignored.
13436      */
13437     /**
13438      * @cfg {Boolean} hideEl
13439      * False to keep the bound element visible while the editor is displayed (defaults to true)
13440      */
13441     /**
13442      * @cfg {Mixed} value
13443      * The data value of the underlying field (defaults to "")
13444      */
13445     value : "",
13446     /**
13447      * @cfg {String} alignment
13448      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13449      */
13450     alignment: "c-c?",
13451     /**
13452      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13453      * for bottom-right shadow (defaults to "frame")
13454      */
13455     shadow : "frame",
13456     /**
13457      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13458      */
13459     constrain : false,
13460     /**
13461      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13462      */
13463     completeOnEnter : false,
13464     /**
13465      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13466      */
13467     cancelOnEsc : false,
13468     /**
13469      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13470      */
13471     updateEl : false,
13472
13473     // private
13474     onRender : function(ct, position){
13475         this.el = new Roo.Layer({
13476             shadow: this.shadow,
13477             cls: "x-editor",
13478             parentEl : ct,
13479             shim : this.shim,
13480             shadowOffset:4,
13481             id: this.id,
13482             constrain: this.constrain
13483         });
13484         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13485         if(this.field.msgTarget != 'title'){
13486             this.field.msgTarget = 'qtip';
13487         }
13488         this.field.render(this.el);
13489         if(Roo.isGecko){
13490             this.field.el.dom.setAttribute('autocomplete', 'off');
13491         }
13492         this.field.on("specialkey", this.onSpecialKey, this);
13493         if(this.swallowKeys){
13494             this.field.el.swallowEvent(['keydown','keypress']);
13495         }
13496         this.field.show();
13497         this.field.on("blur", this.onBlur, this);
13498         if(this.field.grow){
13499             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13500         }
13501     },
13502
13503     onSpecialKey : function(field, e)
13504     {
13505         //Roo.log('editor onSpecialKey');
13506         if(this.completeOnEnter && e.getKey() == e.ENTER){
13507             e.stopEvent();
13508             this.completeEdit();
13509             return;
13510         }
13511         // do not fire special key otherwise it might hide close the editor...
13512         if(e.getKey() == e.ENTER){    
13513             return;
13514         }
13515         if(this.cancelOnEsc && e.getKey() == e.ESC){
13516             this.cancelEdit();
13517             return;
13518         } 
13519         this.fireEvent('specialkey', field, e);
13520     
13521     },
13522
13523     /**
13524      * Starts the editing process and shows the editor.
13525      * @param {String/HTMLElement/Element} el The element to edit
13526      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13527       * to the innerHTML of el.
13528      */
13529     startEdit : function(el, value){
13530         if(this.editing){
13531             this.completeEdit();
13532         }
13533         this.boundEl = Roo.get(el);
13534         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13535         if(!this.rendered){
13536             this.render(this.parentEl || document.body);
13537         }
13538         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13539             return;
13540         }
13541         this.startValue = v;
13542         this.field.setValue(v);
13543         if(this.autoSize){
13544             var sz = this.boundEl.getSize();
13545             switch(this.autoSize){
13546                 case "width":
13547                 this.setSize(sz.width,  "");
13548                 break;
13549                 case "height":
13550                 this.setSize("",  sz.height);
13551                 break;
13552                 default:
13553                 this.setSize(sz.width,  sz.height);
13554             }
13555         }
13556         this.el.alignTo(this.boundEl, this.alignment);
13557         this.editing = true;
13558         if(Roo.QuickTips){
13559             Roo.QuickTips.disable();
13560         }
13561         this.show();
13562     },
13563
13564     /**
13565      * Sets the height and width of this editor.
13566      * @param {Number} width The new width
13567      * @param {Number} height The new height
13568      */
13569     setSize : function(w, h){
13570         this.field.setSize(w, h);
13571         if(this.el){
13572             this.el.sync();
13573         }
13574     },
13575
13576     /**
13577      * Realigns the editor to the bound field based on the current alignment config value.
13578      */
13579     realign : function(){
13580         this.el.alignTo(this.boundEl, this.alignment);
13581     },
13582
13583     /**
13584      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13585      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13586      */
13587     completeEdit : function(remainVisible){
13588         if(!this.editing){
13589             return;
13590         }
13591         var v = this.getValue();
13592         if(this.revertInvalid !== false && !this.field.isValid()){
13593             v = this.startValue;
13594             this.cancelEdit(true);
13595         }
13596         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13597             this.editing = false;
13598             this.hide();
13599             return;
13600         }
13601         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13602             this.editing = false;
13603             if(this.updateEl && this.boundEl){
13604                 this.boundEl.update(v);
13605             }
13606             if(remainVisible !== true){
13607                 this.hide();
13608             }
13609             this.fireEvent("complete", this, v, this.startValue);
13610         }
13611     },
13612
13613     // private
13614     onShow : function(){
13615         this.el.show();
13616         if(this.hideEl !== false){
13617             this.boundEl.hide();
13618         }
13619         this.field.show();
13620         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13621             this.fixIEFocus = true;
13622             this.deferredFocus.defer(50, this);
13623         }else{
13624             this.field.focus();
13625         }
13626         this.fireEvent("startedit", this.boundEl, this.startValue);
13627     },
13628
13629     deferredFocus : function(){
13630         if(this.editing){
13631             this.field.focus();
13632         }
13633     },
13634
13635     /**
13636      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13637      * reverted to the original starting value.
13638      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13639      * cancel (defaults to false)
13640      */
13641     cancelEdit : function(remainVisible){
13642         if(this.editing){
13643             this.setValue(this.startValue);
13644             if(remainVisible !== true){
13645                 this.hide();
13646             }
13647         }
13648     },
13649
13650     // private
13651     onBlur : function(){
13652         if(this.allowBlur !== true && this.editing){
13653             this.completeEdit();
13654         }
13655     },
13656
13657     // private
13658     onHide : function(){
13659         if(this.editing){
13660             this.completeEdit();
13661             return;
13662         }
13663         this.field.blur();
13664         if(this.field.collapse){
13665             this.field.collapse();
13666         }
13667         this.el.hide();
13668         if(this.hideEl !== false){
13669             this.boundEl.show();
13670         }
13671         if(Roo.QuickTips){
13672             Roo.QuickTips.enable();
13673         }
13674     },
13675
13676     /**
13677      * Sets the data value of the editor
13678      * @param {Mixed} value Any valid value supported by the underlying field
13679      */
13680     setValue : function(v){
13681         this.field.setValue(v);
13682     },
13683
13684     /**
13685      * Gets the data value of the editor
13686      * @return {Mixed} The data value
13687      */
13688     getValue : function(){
13689         return this.field.getValue();
13690     }
13691 });/*
13692  * Based on:
13693  * Ext JS Library 1.1.1
13694  * Copyright(c) 2006-2007, Ext JS, LLC.
13695  *
13696  * Originally Released Under LGPL - original licence link has changed is not relivant.
13697  *
13698  * Fork - LGPL
13699  * <script type="text/javascript">
13700  */
13701  
13702 /**
13703  * @class Roo.BasicDialog
13704  * @extends Roo.util.Observable
13705  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13706  * <pre><code>
13707 var dlg = new Roo.BasicDialog("my-dlg", {
13708     height: 200,
13709     width: 300,
13710     minHeight: 100,
13711     minWidth: 150,
13712     modal: true,
13713     proxyDrag: true,
13714     shadow: true
13715 });
13716 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13717 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13718 dlg.addButton('Cancel', dlg.hide, dlg);
13719 dlg.show();
13720 </code></pre>
13721   <b>A Dialog should always be a direct child of the body element.</b>
13722  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13723  * @cfg {String} title Default text to display in the title bar (defaults to null)
13724  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13725  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13726  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13727  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13728  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13729  * (defaults to null with no animation)
13730  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13731  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13732  * property for valid values (defaults to 'all')
13733  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13734  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13735  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13736  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13737  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13738  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13739  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13740  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13741  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13742  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13743  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13744  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13745  * draggable = true (defaults to false)
13746  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13747  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13748  * shadow (defaults to false)
13749  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13750  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13751  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13752  * @cfg {Array} buttons Array of buttons
13753  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13754  * @constructor
13755  * Create a new BasicDialog.
13756  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13757  * @param {Object} config Configuration options
13758  */
13759 Roo.BasicDialog = function(el, config){
13760     this.el = Roo.get(el);
13761     var dh = Roo.DomHelper;
13762     if(!this.el && config && config.autoCreate){
13763         if(typeof config.autoCreate == "object"){
13764             if(!config.autoCreate.id){
13765                 config.autoCreate.id = el;
13766             }
13767             this.el = dh.append(document.body,
13768                         config.autoCreate, true);
13769         }else{
13770             this.el = dh.append(document.body,
13771                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13772         }
13773     }
13774     el = this.el;
13775     el.setDisplayed(true);
13776     el.hide = this.hideAction;
13777     this.id = el.id;
13778     el.addClass("x-dlg");
13779
13780     Roo.apply(this, config);
13781
13782     this.proxy = el.createProxy("x-dlg-proxy");
13783     this.proxy.hide = this.hideAction;
13784     this.proxy.setOpacity(.5);
13785     this.proxy.hide();
13786
13787     if(config.width){
13788         el.setWidth(config.width);
13789     }
13790     if(config.height){
13791         el.setHeight(config.height);
13792     }
13793     this.size = el.getSize();
13794     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13795         this.xy = [config.x,config.y];
13796     }else{
13797         this.xy = el.getCenterXY(true);
13798     }
13799     /** The header element @type Roo.Element */
13800     this.header = el.child("> .x-dlg-hd");
13801     /** The body element @type Roo.Element */
13802     this.body = el.child("> .x-dlg-bd");
13803     /** The footer element @type Roo.Element */
13804     this.footer = el.child("> .x-dlg-ft");
13805
13806     if(!this.header){
13807         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13808     }
13809     if(!this.body){
13810         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13811     }
13812
13813     this.header.unselectable();
13814     if(this.title){
13815         this.header.update(this.title);
13816     }
13817     // this element allows the dialog to be focused for keyboard event
13818     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13819     this.focusEl.swallowEvent("click", true);
13820
13821     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13822
13823     // wrap the body and footer for special rendering
13824     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13825     if(this.footer){
13826         this.bwrap.dom.appendChild(this.footer.dom);
13827     }
13828
13829     this.bg = this.el.createChild({
13830         tag: "div", cls:"x-dlg-bg",
13831         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13832     });
13833     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13834
13835
13836     if(this.autoScroll !== false && !this.autoTabs){
13837         this.body.setStyle("overflow", "auto");
13838     }
13839
13840     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13841
13842     if(this.closable !== false){
13843         this.el.addClass("x-dlg-closable");
13844         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13845         this.close.on("click", this.closeClick, this);
13846         this.close.addClassOnOver("x-dlg-close-over");
13847     }
13848     if(this.collapsible !== false){
13849         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13850         this.collapseBtn.on("click", this.collapseClick, this);
13851         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13852         this.header.on("dblclick", this.collapseClick, this);
13853     }
13854     if(this.resizable !== false){
13855         this.el.addClass("x-dlg-resizable");
13856         this.resizer = new Roo.Resizable(el, {
13857             minWidth: this.minWidth || 80,
13858             minHeight:this.minHeight || 80,
13859             handles: this.resizeHandles || "all",
13860             pinned: true
13861         });
13862         this.resizer.on("beforeresize", this.beforeResize, this);
13863         this.resizer.on("resize", this.onResize, this);
13864     }
13865     if(this.draggable !== false){
13866         el.addClass("x-dlg-draggable");
13867         if (!this.proxyDrag) {
13868             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13869         }
13870         else {
13871             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13872         }
13873         dd.setHandleElId(this.header.id);
13874         dd.endDrag = this.endMove.createDelegate(this);
13875         dd.startDrag = this.startMove.createDelegate(this);
13876         dd.onDrag = this.onDrag.createDelegate(this);
13877         dd.scroll = false;
13878         this.dd = dd;
13879     }
13880     if(this.modal){
13881         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13882         this.mask.enableDisplayMode("block");
13883         this.mask.hide();
13884         this.el.addClass("x-dlg-modal");
13885     }
13886     if(this.shadow){
13887         this.shadow = new Roo.Shadow({
13888             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13889             offset : this.shadowOffset
13890         });
13891     }else{
13892         this.shadowOffset = 0;
13893     }
13894     if(Roo.useShims && this.shim !== false){
13895         this.shim = this.el.createShim();
13896         this.shim.hide = this.hideAction;
13897         this.shim.hide();
13898     }else{
13899         this.shim = false;
13900     }
13901     if(this.autoTabs){
13902         this.initTabs();
13903     }
13904     if (this.buttons) { 
13905         var bts= this.buttons;
13906         this.buttons = [];
13907         Roo.each(bts, function(b) {
13908             this.addButton(b);
13909         }, this);
13910     }
13911     
13912     
13913     this.addEvents({
13914         /**
13915          * @event keydown
13916          * Fires when a key is pressed
13917          * @param {Roo.BasicDialog} this
13918          * @param {Roo.EventObject} e
13919          */
13920         "keydown" : true,
13921         /**
13922          * @event move
13923          * Fires when this dialog is moved by the user.
13924          * @param {Roo.BasicDialog} this
13925          * @param {Number} x The new page X
13926          * @param {Number} y The new page Y
13927          */
13928         "move" : true,
13929         /**
13930          * @event resize
13931          * Fires when this dialog is resized by the user.
13932          * @param {Roo.BasicDialog} this
13933          * @param {Number} width The new width
13934          * @param {Number} height The new height
13935          */
13936         "resize" : true,
13937         /**
13938          * @event beforehide
13939          * Fires before this dialog is hidden.
13940          * @param {Roo.BasicDialog} this
13941          */
13942         "beforehide" : true,
13943         /**
13944          * @event hide
13945          * Fires when this dialog is hidden.
13946          * @param {Roo.BasicDialog} this
13947          */
13948         "hide" : true,
13949         /**
13950          * @event beforeshow
13951          * Fires before this dialog is shown.
13952          * @param {Roo.BasicDialog} this
13953          */
13954         "beforeshow" : true,
13955         /**
13956          * @event show
13957          * Fires when this dialog is shown.
13958          * @param {Roo.BasicDialog} this
13959          */
13960         "show" : true
13961     });
13962     el.on("keydown", this.onKeyDown, this);
13963     el.on("mousedown", this.toFront, this);
13964     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13965     this.el.hide();
13966     Roo.DialogManager.register(this);
13967     Roo.BasicDialog.superclass.constructor.call(this);
13968 };
13969
13970 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
13971     shadowOffset: Roo.isIE ? 6 : 5,
13972     minHeight: 80,
13973     minWidth: 200,
13974     minButtonWidth: 75,
13975     defaultButton: null,
13976     buttonAlign: "right",
13977     tabTag: 'div',
13978     firstShow: true,
13979
13980     /**
13981      * Sets the dialog title text
13982      * @param {String} text The title text to display
13983      * @return {Roo.BasicDialog} this
13984      */
13985     setTitle : function(text){
13986         this.header.update(text);
13987         return this;
13988     },
13989
13990     // private
13991     closeClick : function(){
13992         this.hide();
13993     },
13994
13995     // private
13996     collapseClick : function(){
13997         this[this.collapsed ? "expand" : "collapse"]();
13998     },
13999
14000     /**
14001      * Collapses the dialog to its minimized state (only the title bar is visible).
14002      * Equivalent to the user clicking the collapse dialog button.
14003      */
14004     collapse : function(){
14005         if(!this.collapsed){
14006             this.collapsed = true;
14007             this.el.addClass("x-dlg-collapsed");
14008             this.restoreHeight = this.el.getHeight();
14009             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14010         }
14011     },
14012
14013     /**
14014      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14015      * clicking the expand dialog button.
14016      */
14017     expand : function(){
14018         if(this.collapsed){
14019             this.collapsed = false;
14020             this.el.removeClass("x-dlg-collapsed");
14021             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14022         }
14023     },
14024
14025     /**
14026      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14027      * @return {Roo.TabPanel} The tabs component
14028      */
14029     initTabs : function(){
14030         var tabs = this.getTabs();
14031         while(tabs.getTab(0)){
14032             tabs.removeTab(0);
14033         }
14034         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14035             var dom = el.dom;
14036             tabs.addTab(Roo.id(dom), dom.title);
14037             dom.title = "";
14038         });
14039         tabs.activate(0);
14040         return tabs;
14041     },
14042
14043     // private
14044     beforeResize : function(){
14045         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14046     },
14047
14048     // private
14049     onResize : function(){
14050         this.refreshSize();
14051         this.syncBodyHeight();
14052         this.adjustAssets();
14053         this.focus();
14054         this.fireEvent("resize", this, this.size.width, this.size.height);
14055     },
14056
14057     // private
14058     onKeyDown : function(e){
14059         if(this.isVisible()){
14060             this.fireEvent("keydown", this, e);
14061         }
14062     },
14063
14064     /**
14065      * Resizes the dialog.
14066      * @param {Number} width
14067      * @param {Number} height
14068      * @return {Roo.BasicDialog} this
14069      */
14070     resizeTo : function(width, height){
14071         this.el.setSize(width, height);
14072         this.size = {width: width, height: height};
14073         this.syncBodyHeight();
14074         if(this.fixedcenter){
14075             this.center();
14076         }
14077         if(this.isVisible()){
14078             this.constrainXY();
14079             this.adjustAssets();
14080         }
14081         this.fireEvent("resize", this, width, height);
14082         return this;
14083     },
14084
14085
14086     /**
14087      * Resizes the dialog to fit the specified content size.
14088      * @param {Number} width
14089      * @param {Number} height
14090      * @return {Roo.BasicDialog} this
14091      */
14092     setContentSize : function(w, h){
14093         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14094         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14095         //if(!this.el.isBorderBox()){
14096             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14097             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14098         //}
14099         if(this.tabs){
14100             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14101             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14102         }
14103         this.resizeTo(w, h);
14104         return this;
14105     },
14106
14107     /**
14108      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14109      * executed in response to a particular key being pressed while the dialog is active.
14110      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14111      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14112      * @param {Function} fn The function to call
14113      * @param {Object} scope (optional) The scope of the function
14114      * @return {Roo.BasicDialog} this
14115      */
14116     addKeyListener : function(key, fn, scope){
14117         var keyCode, shift, ctrl, alt;
14118         if(typeof key == "object" && !(key instanceof Array)){
14119             keyCode = key["key"];
14120             shift = key["shift"];
14121             ctrl = key["ctrl"];
14122             alt = key["alt"];
14123         }else{
14124             keyCode = key;
14125         }
14126         var handler = function(dlg, e){
14127             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14128                 var k = e.getKey();
14129                 if(keyCode instanceof Array){
14130                     for(var i = 0, len = keyCode.length; i < len; i++){
14131                         if(keyCode[i] == k){
14132                           fn.call(scope || window, dlg, k, e);
14133                           return;
14134                         }
14135                     }
14136                 }else{
14137                     if(k == keyCode){
14138                         fn.call(scope || window, dlg, k, e);
14139                     }
14140                 }
14141             }
14142         };
14143         this.on("keydown", handler);
14144         return this;
14145     },
14146
14147     /**
14148      * Returns the TabPanel component (creates it if it doesn't exist).
14149      * Note: If you wish to simply check for the existence of tabs without creating them,
14150      * check for a null 'tabs' property.
14151      * @return {Roo.TabPanel} The tabs component
14152      */
14153     getTabs : function(){
14154         if(!this.tabs){
14155             this.el.addClass("x-dlg-auto-tabs");
14156             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14157             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14158         }
14159         return this.tabs;
14160     },
14161
14162     /**
14163      * Adds a button to the footer section of the dialog.
14164      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14165      * object or a valid Roo.DomHelper element config
14166      * @param {Function} handler The function called when the button is clicked
14167      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14168      * @return {Roo.Button} The new button
14169      */
14170     addButton : function(config, handler, scope){
14171         var dh = Roo.DomHelper;
14172         if(!this.footer){
14173             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14174         }
14175         if(!this.btnContainer){
14176             var tb = this.footer.createChild({
14177
14178                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14179                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14180             }, null, true);
14181             this.btnContainer = tb.firstChild.firstChild.firstChild;
14182         }
14183         var bconfig = {
14184             handler: handler,
14185             scope: scope,
14186             minWidth: this.minButtonWidth,
14187             hideParent:true
14188         };
14189         if(typeof config == "string"){
14190             bconfig.text = config;
14191         }else{
14192             if(config.tag){
14193                 bconfig.dhconfig = config;
14194             }else{
14195                 Roo.apply(bconfig, config);
14196             }
14197         }
14198         var fc = false;
14199         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14200             bconfig.position = Math.max(0, bconfig.position);
14201             fc = this.btnContainer.childNodes[bconfig.position];
14202         }
14203          
14204         var btn = new Roo.Button(
14205             fc ? 
14206                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14207                 : this.btnContainer.appendChild(document.createElement("td")),
14208             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14209             bconfig
14210         );
14211         this.syncBodyHeight();
14212         if(!this.buttons){
14213             /**
14214              * Array of all the buttons that have been added to this dialog via addButton
14215              * @type Array
14216              */
14217             this.buttons = [];
14218         }
14219         this.buttons.push(btn);
14220         return btn;
14221     },
14222
14223     /**
14224      * Sets the default button to be focused when the dialog is displayed.
14225      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14226      * @return {Roo.BasicDialog} this
14227      */
14228     setDefaultButton : function(btn){
14229         this.defaultButton = btn;
14230         return this;
14231     },
14232
14233     // private
14234     getHeaderFooterHeight : function(safe){
14235         var height = 0;
14236         if(this.header){
14237            height += this.header.getHeight();
14238         }
14239         if(this.footer){
14240            var fm = this.footer.getMargins();
14241             height += (this.footer.getHeight()+fm.top+fm.bottom);
14242         }
14243         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14244         height += this.centerBg.getPadding("tb");
14245         return height;
14246     },
14247
14248     // private
14249     syncBodyHeight : function()
14250     {
14251         var bd = this.body, // the text
14252             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14253             bw = this.bwrap;
14254         var height = this.size.height - this.getHeaderFooterHeight(false);
14255         bd.setHeight(height-bd.getMargins("tb"));
14256         var hh = this.header.getHeight();
14257         var h = this.size.height-hh;
14258         cb.setHeight(h);
14259         
14260         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14261         bw.setHeight(h-cb.getPadding("tb"));
14262         
14263         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14264         bd.setWidth(bw.getWidth(true));
14265         if(this.tabs){
14266             this.tabs.syncHeight();
14267             if(Roo.isIE){
14268                 this.tabs.el.repaint();
14269             }
14270         }
14271     },
14272
14273     /**
14274      * Restores the previous state of the dialog if Roo.state is configured.
14275      * @return {Roo.BasicDialog} this
14276      */
14277     restoreState : function(){
14278         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14279         if(box && box.width){
14280             this.xy = [box.x, box.y];
14281             this.resizeTo(box.width, box.height);
14282         }
14283         return this;
14284     },
14285
14286     // private
14287     beforeShow : function(){
14288         this.expand();
14289         if(this.fixedcenter){
14290             this.xy = this.el.getCenterXY(true);
14291         }
14292         if(this.modal){
14293             Roo.get(document.body).addClass("x-body-masked");
14294             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14295             this.mask.show();
14296         }
14297         this.constrainXY();
14298     },
14299
14300     // private
14301     animShow : function(){
14302         var b = Roo.get(this.animateTarget).getBox();
14303         this.proxy.setSize(b.width, b.height);
14304         this.proxy.setLocation(b.x, b.y);
14305         this.proxy.show();
14306         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14307                     true, .35, this.showEl.createDelegate(this));
14308     },
14309
14310     /**
14311      * Shows the dialog.
14312      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14313      * @return {Roo.BasicDialog} this
14314      */
14315     show : function(animateTarget){
14316         if (this.fireEvent("beforeshow", this) === false){
14317             return;
14318         }
14319         if(this.syncHeightBeforeShow){
14320             this.syncBodyHeight();
14321         }else if(this.firstShow){
14322             this.firstShow = false;
14323             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14324         }
14325         this.animateTarget = animateTarget || this.animateTarget;
14326         if(!this.el.isVisible()){
14327             this.beforeShow();
14328             if(this.animateTarget && Roo.get(this.animateTarget)){
14329                 this.animShow();
14330             }else{
14331                 this.showEl();
14332             }
14333         }
14334         return this;
14335     },
14336
14337     // private
14338     showEl : function(){
14339         this.proxy.hide();
14340         this.el.setXY(this.xy);
14341         this.el.show();
14342         this.adjustAssets(true);
14343         this.toFront();
14344         this.focus();
14345         // IE peekaboo bug - fix found by Dave Fenwick
14346         if(Roo.isIE){
14347             this.el.repaint();
14348         }
14349         this.fireEvent("show", this);
14350     },
14351
14352     /**
14353      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14354      * dialog itself will receive focus.
14355      */
14356     focus : function(){
14357         if(this.defaultButton){
14358             this.defaultButton.focus();
14359         }else{
14360             this.focusEl.focus();
14361         }
14362     },
14363
14364     // private
14365     constrainXY : function(){
14366         if(this.constraintoviewport !== false){
14367             if(!this.viewSize){
14368                 if(this.container){
14369                     var s = this.container.getSize();
14370                     this.viewSize = [s.width, s.height];
14371                 }else{
14372                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14373                 }
14374             }
14375             var s = Roo.get(this.container||document).getScroll();
14376
14377             var x = this.xy[0], y = this.xy[1];
14378             var w = this.size.width, h = this.size.height;
14379             var vw = this.viewSize[0], vh = this.viewSize[1];
14380             // only move it if it needs it
14381             var moved = false;
14382             // first validate right/bottom
14383             if(x + w > vw+s.left){
14384                 x = vw - w;
14385                 moved = true;
14386             }
14387             if(y + h > vh+s.top){
14388                 y = vh - h;
14389                 moved = true;
14390             }
14391             // then make sure top/left isn't negative
14392             if(x < s.left){
14393                 x = s.left;
14394                 moved = true;
14395             }
14396             if(y < s.top){
14397                 y = s.top;
14398                 moved = true;
14399             }
14400             if(moved){
14401                 // cache xy
14402                 this.xy = [x, y];
14403                 if(this.isVisible()){
14404                     this.el.setLocation(x, y);
14405                     this.adjustAssets();
14406                 }
14407             }
14408         }
14409     },
14410
14411     // private
14412     onDrag : function(){
14413         if(!this.proxyDrag){
14414             this.xy = this.el.getXY();
14415             this.adjustAssets();
14416         }
14417     },
14418
14419     // private
14420     adjustAssets : function(doShow){
14421         var x = this.xy[0], y = this.xy[1];
14422         var w = this.size.width, h = this.size.height;
14423         if(doShow === true){
14424             if(this.shadow){
14425                 this.shadow.show(this.el);
14426             }
14427             if(this.shim){
14428                 this.shim.show();
14429             }
14430         }
14431         if(this.shadow && this.shadow.isVisible()){
14432             this.shadow.show(this.el);
14433         }
14434         if(this.shim && this.shim.isVisible()){
14435             this.shim.setBounds(x, y, w, h);
14436         }
14437     },
14438
14439     // private
14440     adjustViewport : function(w, h){
14441         if(!w || !h){
14442             w = Roo.lib.Dom.getViewWidth();
14443             h = Roo.lib.Dom.getViewHeight();
14444         }
14445         // cache the size
14446         this.viewSize = [w, h];
14447         if(this.modal && this.mask.isVisible()){
14448             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14449             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14450         }
14451         if(this.isVisible()){
14452             this.constrainXY();
14453         }
14454     },
14455
14456     /**
14457      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14458      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14459      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14460      */
14461     destroy : function(removeEl){
14462         if(this.isVisible()){
14463             this.animateTarget = null;
14464             this.hide();
14465         }
14466         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14467         if(this.tabs){
14468             this.tabs.destroy(removeEl);
14469         }
14470         Roo.destroy(
14471              this.shim,
14472              this.proxy,
14473              this.resizer,
14474              this.close,
14475              this.mask
14476         );
14477         if(this.dd){
14478             this.dd.unreg();
14479         }
14480         if(this.buttons){
14481            for(var i = 0, len = this.buttons.length; i < len; i++){
14482                this.buttons[i].destroy();
14483            }
14484         }
14485         this.el.removeAllListeners();
14486         if(removeEl === true){
14487             this.el.update("");
14488             this.el.remove();
14489         }
14490         Roo.DialogManager.unregister(this);
14491     },
14492
14493     // private
14494     startMove : function(){
14495         if(this.proxyDrag){
14496             this.proxy.show();
14497         }
14498         if(this.constraintoviewport !== false){
14499             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14500         }
14501     },
14502
14503     // private
14504     endMove : function(){
14505         if(!this.proxyDrag){
14506             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14507         }else{
14508             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14509             this.proxy.hide();
14510         }
14511         this.refreshSize();
14512         this.adjustAssets();
14513         this.focus();
14514         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14515     },
14516
14517     /**
14518      * Brings this dialog to the front of any other visible dialogs
14519      * @return {Roo.BasicDialog} this
14520      */
14521     toFront : function(){
14522         Roo.DialogManager.bringToFront(this);
14523         return this;
14524     },
14525
14526     /**
14527      * Sends this dialog to the back (under) of any other visible dialogs
14528      * @return {Roo.BasicDialog} this
14529      */
14530     toBack : function(){
14531         Roo.DialogManager.sendToBack(this);
14532         return this;
14533     },
14534
14535     /**
14536      * Centers this dialog in the viewport
14537      * @return {Roo.BasicDialog} this
14538      */
14539     center : function(){
14540         var xy = this.el.getCenterXY(true);
14541         this.moveTo(xy[0], xy[1]);
14542         return this;
14543     },
14544
14545     /**
14546      * Moves the dialog's top-left corner to the specified point
14547      * @param {Number} x
14548      * @param {Number} y
14549      * @return {Roo.BasicDialog} this
14550      */
14551     moveTo : function(x, y){
14552         this.xy = [x,y];
14553         if(this.isVisible()){
14554             this.el.setXY(this.xy);
14555             this.adjustAssets();
14556         }
14557         return this;
14558     },
14559
14560     /**
14561      * Aligns the dialog to the specified element
14562      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14563      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14564      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14565      * @return {Roo.BasicDialog} this
14566      */
14567     alignTo : function(element, position, offsets){
14568         this.xy = this.el.getAlignToXY(element, position, offsets);
14569         if(this.isVisible()){
14570             this.el.setXY(this.xy);
14571             this.adjustAssets();
14572         }
14573         return this;
14574     },
14575
14576     /**
14577      * Anchors an element to another element and realigns it when the window is resized.
14578      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14579      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14580      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14581      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14582      * is a number, it is used as the buffer delay (defaults to 50ms).
14583      * @return {Roo.BasicDialog} this
14584      */
14585     anchorTo : function(el, alignment, offsets, monitorScroll){
14586         var action = function(){
14587             this.alignTo(el, alignment, offsets);
14588         };
14589         Roo.EventManager.onWindowResize(action, this);
14590         var tm = typeof monitorScroll;
14591         if(tm != 'undefined'){
14592             Roo.EventManager.on(window, 'scroll', action, this,
14593                 {buffer: tm == 'number' ? monitorScroll : 50});
14594         }
14595         action.call(this);
14596         return this;
14597     },
14598
14599     /**
14600      * Returns true if the dialog is visible
14601      * @return {Boolean}
14602      */
14603     isVisible : function(){
14604         return this.el.isVisible();
14605     },
14606
14607     // private
14608     animHide : function(callback){
14609         var b = Roo.get(this.animateTarget).getBox();
14610         this.proxy.show();
14611         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14612         this.el.hide();
14613         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14614                     this.hideEl.createDelegate(this, [callback]));
14615     },
14616
14617     /**
14618      * Hides the dialog.
14619      * @param {Function} callback (optional) Function to call when the dialog is hidden
14620      * @return {Roo.BasicDialog} this
14621      */
14622     hide : function(callback){
14623         if (this.fireEvent("beforehide", this) === false){
14624             return;
14625         }
14626         if(this.shadow){
14627             this.shadow.hide();
14628         }
14629         if(this.shim) {
14630           this.shim.hide();
14631         }
14632         // sometimes animateTarget seems to get set.. causing problems...
14633         // this just double checks..
14634         if(this.animateTarget && Roo.get(this.animateTarget)) {
14635            this.animHide(callback);
14636         }else{
14637             this.el.hide();
14638             this.hideEl(callback);
14639         }
14640         return this;
14641     },
14642
14643     // private
14644     hideEl : function(callback){
14645         this.proxy.hide();
14646         if(this.modal){
14647             this.mask.hide();
14648             Roo.get(document.body).removeClass("x-body-masked");
14649         }
14650         this.fireEvent("hide", this);
14651         if(typeof callback == "function"){
14652             callback();
14653         }
14654     },
14655
14656     // private
14657     hideAction : function(){
14658         this.setLeft("-10000px");
14659         this.setTop("-10000px");
14660         this.setStyle("visibility", "hidden");
14661     },
14662
14663     // private
14664     refreshSize : function(){
14665         this.size = this.el.getSize();
14666         this.xy = this.el.getXY();
14667         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14668     },
14669
14670     // private
14671     // z-index is managed by the DialogManager and may be overwritten at any time
14672     setZIndex : function(index){
14673         if(this.modal){
14674             this.mask.setStyle("z-index", index);
14675         }
14676         if(this.shim){
14677             this.shim.setStyle("z-index", ++index);
14678         }
14679         if(this.shadow){
14680             this.shadow.setZIndex(++index);
14681         }
14682         this.el.setStyle("z-index", ++index);
14683         if(this.proxy){
14684             this.proxy.setStyle("z-index", ++index);
14685         }
14686         if(this.resizer){
14687             this.resizer.proxy.setStyle("z-index", ++index);
14688         }
14689
14690         this.lastZIndex = index;
14691     },
14692
14693     /**
14694      * Returns the element for this dialog
14695      * @return {Roo.Element} The underlying dialog Element
14696      */
14697     getEl : function(){
14698         return this.el;
14699     }
14700 });
14701
14702 /**
14703  * @class Roo.DialogManager
14704  * Provides global access to BasicDialogs that have been created and
14705  * support for z-indexing (layering) multiple open dialogs.
14706  */
14707 Roo.DialogManager = function(){
14708     var list = {};
14709     var accessList = [];
14710     var front = null;
14711
14712     // private
14713     var sortDialogs = function(d1, d2){
14714         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14715     };
14716
14717     // private
14718     var orderDialogs = function(){
14719         accessList.sort(sortDialogs);
14720         var seed = Roo.DialogManager.zseed;
14721         for(var i = 0, len = accessList.length; i < len; i++){
14722             var dlg = accessList[i];
14723             if(dlg){
14724                 dlg.setZIndex(seed + (i*10));
14725             }
14726         }
14727     };
14728
14729     return {
14730         /**
14731          * The starting z-index for BasicDialogs (defaults to 9000)
14732          * @type Number The z-index value
14733          */
14734         zseed : 9000,
14735
14736         // private
14737         register : function(dlg){
14738             list[dlg.id] = dlg;
14739             accessList.push(dlg);
14740         },
14741
14742         // private
14743         unregister : function(dlg){
14744             delete list[dlg.id];
14745             var i=0;
14746             var len=0;
14747             if(!accessList.indexOf){
14748                 for(  i = 0, len = accessList.length; i < len; i++){
14749                     if(accessList[i] == dlg){
14750                         accessList.splice(i, 1);
14751                         return;
14752                     }
14753                 }
14754             }else{
14755                  i = accessList.indexOf(dlg);
14756                 if(i != -1){
14757                     accessList.splice(i, 1);
14758                 }
14759             }
14760         },
14761
14762         /**
14763          * Gets a registered dialog by id
14764          * @param {String/Object} id The id of the dialog or a dialog
14765          * @return {Roo.BasicDialog} this
14766          */
14767         get : function(id){
14768             return typeof id == "object" ? id : list[id];
14769         },
14770
14771         /**
14772          * Brings the specified dialog to the front
14773          * @param {String/Object} dlg The id of the dialog or a dialog
14774          * @return {Roo.BasicDialog} this
14775          */
14776         bringToFront : function(dlg){
14777             dlg = this.get(dlg);
14778             if(dlg != front){
14779                 front = dlg;
14780                 dlg._lastAccess = new Date().getTime();
14781                 orderDialogs();
14782             }
14783             return dlg;
14784         },
14785
14786         /**
14787          * Sends the specified dialog to the back
14788          * @param {String/Object} dlg The id of the dialog or a dialog
14789          * @return {Roo.BasicDialog} this
14790          */
14791         sendToBack : function(dlg){
14792             dlg = this.get(dlg);
14793             dlg._lastAccess = -(new Date().getTime());
14794             orderDialogs();
14795             return dlg;
14796         },
14797
14798         /**
14799          * Hides all dialogs
14800          */
14801         hideAll : function(){
14802             for(var id in list){
14803                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14804                     list[id].hide();
14805                 }
14806             }
14807         }
14808     };
14809 }();
14810
14811 /**
14812  * @class Roo.LayoutDialog
14813  * @extends Roo.BasicDialog
14814  * Dialog which provides adjustments for working with a layout in a Dialog.
14815  * Add your necessary layout config options to the dialog's config.<br>
14816  * Example usage (including a nested layout):
14817  * <pre><code>
14818 if(!dialog){
14819     dialog = new Roo.LayoutDialog("download-dlg", {
14820         modal: true,
14821         width:600,
14822         height:450,
14823         shadow:true,
14824         minWidth:500,
14825         minHeight:350,
14826         autoTabs:true,
14827         proxyDrag:true,
14828         // layout config merges with the dialog config
14829         center:{
14830             tabPosition: "top",
14831             alwaysShowTabs: true
14832         }
14833     });
14834     dialog.addKeyListener(27, dialog.hide, dialog);
14835     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14836     dialog.addButton("Build It!", this.getDownload, this);
14837
14838     // we can even add nested layouts
14839     var innerLayout = new Roo.BorderLayout("dl-inner", {
14840         east: {
14841             initialSize: 200,
14842             autoScroll:true,
14843             split:true
14844         },
14845         center: {
14846             autoScroll:true
14847         }
14848     });
14849     innerLayout.beginUpdate();
14850     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14851     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14852     innerLayout.endUpdate(true);
14853
14854     var layout = dialog.getLayout();
14855     layout.beginUpdate();
14856     layout.add("center", new Roo.ContentPanel("standard-panel",
14857                         {title: "Download the Source", fitToFrame:true}));
14858     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14859                {title: "Build your own roo.js"}));
14860     layout.getRegion("center").showPanel(sp);
14861     layout.endUpdate();
14862 }
14863 </code></pre>
14864     * @constructor
14865     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14866     * @param {Object} config configuration options
14867   */
14868 Roo.LayoutDialog = function(el, cfg){
14869     
14870     var config=  cfg;
14871     if (typeof(cfg) == 'undefined') {
14872         config = Roo.apply({}, el);
14873         // not sure why we use documentElement here.. - it should always be body.
14874         // IE7 borks horribly if we use documentElement.
14875         // webkit also does not like documentElement - it creates a body element...
14876         el = Roo.get( document.body || document.documentElement ).createChild();
14877         //config.autoCreate = true;
14878     }
14879     
14880     
14881     config.autoTabs = false;
14882     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14883     this.body.setStyle({overflow:"hidden", position:"relative"});
14884     this.layout = new Roo.BorderLayout(this.body.dom, config);
14885     this.layout.monitorWindowResize = false;
14886     this.el.addClass("x-dlg-auto-layout");
14887     // fix case when center region overwrites center function
14888     this.center = Roo.BasicDialog.prototype.center;
14889     this.on("show", this.layout.layout, this.layout, true);
14890     if (config.items) {
14891         var xitems = config.items;
14892         delete config.items;
14893         Roo.each(xitems, this.addxtype, this);
14894     }
14895     
14896     
14897 };
14898 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14899     /**
14900      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14901      * @deprecated
14902      */
14903     endUpdate : function(){
14904         this.layout.endUpdate();
14905     },
14906
14907     /**
14908      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14909      *  @deprecated
14910      */
14911     beginUpdate : function(){
14912         this.layout.beginUpdate();
14913     },
14914
14915     /**
14916      * Get the BorderLayout for this dialog
14917      * @return {Roo.BorderLayout}
14918      */
14919     getLayout : function(){
14920         return this.layout;
14921     },
14922
14923     showEl : function(){
14924         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14925         if(Roo.isIE7){
14926             this.layout.layout();
14927         }
14928     },
14929
14930     // private
14931     // Use the syncHeightBeforeShow config option to control this automatically
14932     syncBodyHeight : function(){
14933         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14934         if(this.layout){this.layout.layout();}
14935     },
14936     
14937       /**
14938      * Add an xtype element (actually adds to the layout.)
14939      * @return {Object} xdata xtype object data.
14940      */
14941     
14942     addxtype : function(c) {
14943         return this.layout.addxtype(c);
14944     }
14945 });/*
14946  * Based on:
14947  * Ext JS Library 1.1.1
14948  * Copyright(c) 2006-2007, Ext JS, LLC.
14949  *
14950  * Originally Released Under LGPL - original licence link has changed is not relivant.
14951  *
14952  * Fork - LGPL
14953  * <script type="text/javascript">
14954  */
14955  
14956 /**
14957  * @class Roo.MessageBox
14958  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
14959  * Example usage:
14960  *<pre><code>
14961 // Basic alert:
14962 Roo.Msg.alert('Status', 'Changes saved successfully.');
14963
14964 // Prompt for user data:
14965 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14966     if (btn == 'ok'){
14967         // process text value...
14968     }
14969 });
14970
14971 // Show a dialog using config options:
14972 Roo.Msg.show({
14973    title:'Save Changes?',
14974    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
14975    buttons: Roo.Msg.YESNOCANCEL,
14976    fn: processResult,
14977    animEl: 'elId'
14978 });
14979 </code></pre>
14980  * @singleton
14981  */
14982 Roo.MessageBox = function(){
14983     var dlg, opt, mask, waitTimer;
14984     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
14985     var buttons, activeTextEl, bwidth;
14986
14987     // private
14988     var handleButton = function(button){
14989         dlg.hide();
14990         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
14991     };
14992
14993     // private
14994     var handleHide = function(){
14995         if(opt && opt.cls){
14996             dlg.el.removeClass(opt.cls);
14997         }
14998         if(waitTimer){
14999             Roo.TaskMgr.stop(waitTimer);
15000             waitTimer = null;
15001         }
15002     };
15003
15004     // private
15005     var updateButtons = function(b){
15006         var width = 0;
15007         if(!b){
15008             buttons["ok"].hide();
15009             buttons["cancel"].hide();
15010             buttons["yes"].hide();
15011             buttons["no"].hide();
15012             dlg.footer.dom.style.display = 'none';
15013             return width;
15014         }
15015         dlg.footer.dom.style.display = '';
15016         for(var k in buttons){
15017             if(typeof buttons[k] != "function"){
15018                 if(b[k]){
15019                     buttons[k].show();
15020                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15021                     width += buttons[k].el.getWidth()+15;
15022                 }else{
15023                     buttons[k].hide();
15024                 }
15025             }
15026         }
15027         return width;
15028     };
15029
15030     // private
15031     var handleEsc = function(d, k, e){
15032         if(opt && opt.closable !== false){
15033             dlg.hide();
15034         }
15035         if(e){
15036             e.stopEvent();
15037         }
15038     };
15039
15040     return {
15041         /**
15042          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15043          * @return {Roo.BasicDialog} The BasicDialog element
15044          */
15045         getDialog : function(){
15046            if(!dlg){
15047                 dlg = new Roo.BasicDialog("x-msg-box", {
15048                     autoCreate : true,
15049                     shadow: true,
15050                     draggable: true,
15051                     resizable:false,
15052                     constraintoviewport:false,
15053                     fixedcenter:true,
15054                     collapsible : false,
15055                     shim:true,
15056                     modal: true,
15057                     width:400, height:100,
15058                     buttonAlign:"center",
15059                     closeClick : function(){
15060                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15061                             handleButton("no");
15062                         }else{
15063                             handleButton("cancel");
15064                         }
15065                     }
15066                 });
15067                 dlg.on("hide", handleHide);
15068                 mask = dlg.mask;
15069                 dlg.addKeyListener(27, handleEsc);
15070                 buttons = {};
15071                 var bt = this.buttonText;
15072                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15073                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15074                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15075                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15076                 bodyEl = dlg.body.createChild({
15077
15078                     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>'
15079                 });
15080                 msgEl = bodyEl.dom.firstChild;
15081                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15082                 textboxEl.enableDisplayMode();
15083                 textboxEl.addKeyListener([10,13], function(){
15084                     if(dlg.isVisible() && opt && opt.buttons){
15085                         if(opt.buttons.ok){
15086                             handleButton("ok");
15087                         }else if(opt.buttons.yes){
15088                             handleButton("yes");
15089                         }
15090                     }
15091                 });
15092                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15093                 textareaEl.enableDisplayMode();
15094                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15095                 progressEl.enableDisplayMode();
15096                 var pf = progressEl.dom.firstChild;
15097                 if (pf) {
15098                     pp = Roo.get(pf.firstChild);
15099                     pp.setHeight(pf.offsetHeight);
15100                 }
15101                 
15102             }
15103             return dlg;
15104         },
15105
15106         /**
15107          * Updates the message box body text
15108          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15109          * the XHTML-compliant non-breaking space character '&amp;#160;')
15110          * @return {Roo.MessageBox} This message box
15111          */
15112         updateText : function(text){
15113             if(!dlg.isVisible() && !opt.width){
15114                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15115             }
15116             msgEl.innerHTML = text || '&#160;';
15117       
15118             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15119             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15120             var w = Math.max(
15121                     Math.min(opt.width || cw , this.maxWidth), 
15122                     Math.max(opt.minWidth || this.minWidth, bwidth)
15123             );
15124             if(opt.prompt){
15125                 activeTextEl.setWidth(w);
15126             }
15127             if(dlg.isVisible()){
15128                 dlg.fixedcenter = false;
15129             }
15130             // to big, make it scroll. = But as usual stupid IE does not support
15131             // !important..
15132             
15133             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15134                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15135                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15136             } else {
15137                 bodyEl.dom.style.height = '';
15138                 bodyEl.dom.style.overflowY = '';
15139             }
15140             if (cw > w) {
15141                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15142             } else {
15143                 bodyEl.dom.style.overflowX = '';
15144             }
15145             
15146             dlg.setContentSize(w, bodyEl.getHeight());
15147             if(dlg.isVisible()){
15148                 dlg.fixedcenter = true;
15149             }
15150             return this;
15151         },
15152
15153         /**
15154          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15155          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15156          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15157          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15158          * @return {Roo.MessageBox} This message box
15159          */
15160         updateProgress : function(value, text){
15161             if(text){
15162                 this.updateText(text);
15163             }
15164             if (pp) { // weird bug on my firefox - for some reason this is not defined
15165                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15166             }
15167             return this;
15168         },        
15169
15170         /**
15171          * Returns true if the message box is currently displayed
15172          * @return {Boolean} True if the message box is visible, else false
15173          */
15174         isVisible : function(){
15175             return dlg && dlg.isVisible();  
15176         },
15177
15178         /**
15179          * Hides the message box if it is displayed
15180          */
15181         hide : function(){
15182             if(this.isVisible()){
15183                 dlg.hide();
15184             }  
15185         },
15186
15187         /**
15188          * Displays a new message box, or reinitializes an existing message box, based on the config options
15189          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15190          * The following config object properties are supported:
15191          * <pre>
15192 Property    Type             Description
15193 ----------  ---------------  ------------------------------------------------------------------------------------
15194 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15195                                    closes (defaults to undefined)
15196 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15197                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15198 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15199                                    progress and wait dialogs will ignore this property and always hide the
15200                                    close button as they can only be closed programmatically.
15201 cls               String           A custom CSS class to apply to the message box element
15202 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15203                                    displayed (defaults to 75)
15204 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15205                                    function will be btn (the name of the button that was clicked, if applicable,
15206                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15207                                    Progress and wait dialogs will ignore this option since they do not respond to
15208                                    user actions and can only be closed programmatically, so any required function
15209                                    should be called by the same code after it closes the dialog.
15210 icon              String           A CSS class that provides a background image to be used as an icon for
15211                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15212 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15213 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15214 modal             Boolean          False to allow user interaction with the page while the message box is
15215                                    displayed (defaults to true)
15216 msg               String           A string that will replace the existing message box body text (defaults
15217                                    to the XHTML-compliant non-breaking space character '&#160;')
15218 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15219 progress          Boolean          True to display a progress bar (defaults to false)
15220 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15221 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15222 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15223 title             String           The title text
15224 value             String           The string value to set into the active textbox element if displayed
15225 wait              Boolean          True to display a progress bar (defaults to false)
15226 width             Number           The width of the dialog in pixels
15227 </pre>
15228          *
15229          * Example usage:
15230          * <pre><code>
15231 Roo.Msg.show({
15232    title: 'Address',
15233    msg: 'Please enter your address:',
15234    width: 300,
15235    buttons: Roo.MessageBox.OKCANCEL,
15236    multiline: true,
15237    fn: saveAddress,
15238    animEl: 'addAddressBtn'
15239 });
15240 </code></pre>
15241          * @param {Object} config Configuration options
15242          * @return {Roo.MessageBox} This message box
15243          */
15244         show : function(options)
15245         {
15246             
15247             // this causes nightmares if you show one dialog after another
15248             // especially on callbacks..
15249              
15250             if(this.isVisible()){
15251                 
15252                 this.hide();
15253                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15254                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15255                 Roo.log("New Dialog Message:" +  options.msg )
15256                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15257                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15258                 
15259             }
15260             var d = this.getDialog();
15261             opt = options;
15262             d.setTitle(opt.title || "&#160;");
15263             d.close.setDisplayed(opt.closable !== false);
15264             activeTextEl = textboxEl;
15265             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15266             if(opt.prompt){
15267                 if(opt.multiline){
15268                     textboxEl.hide();
15269                     textareaEl.show();
15270                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15271                         opt.multiline : this.defaultTextHeight);
15272                     activeTextEl = textareaEl;
15273                 }else{
15274                     textboxEl.show();
15275                     textareaEl.hide();
15276                 }
15277             }else{
15278                 textboxEl.hide();
15279                 textareaEl.hide();
15280             }
15281             progressEl.setDisplayed(opt.progress === true);
15282             this.updateProgress(0);
15283             activeTextEl.dom.value = opt.value || "";
15284             if(opt.prompt){
15285                 dlg.setDefaultButton(activeTextEl);
15286             }else{
15287                 var bs = opt.buttons;
15288                 var db = null;
15289                 if(bs && bs.ok){
15290                     db = buttons["ok"];
15291                 }else if(bs && bs.yes){
15292                     db = buttons["yes"];
15293                 }
15294                 dlg.setDefaultButton(db);
15295             }
15296             bwidth = updateButtons(opt.buttons);
15297             this.updateText(opt.msg);
15298             if(opt.cls){
15299                 d.el.addClass(opt.cls);
15300             }
15301             d.proxyDrag = opt.proxyDrag === true;
15302             d.modal = opt.modal !== false;
15303             d.mask = opt.modal !== false ? mask : false;
15304             if(!d.isVisible()){
15305                 // force it to the end of the z-index stack so it gets a cursor in FF
15306                 document.body.appendChild(dlg.el.dom);
15307                 d.animateTarget = null;
15308                 d.show(options.animEl);
15309             }
15310             return this;
15311         },
15312
15313         /**
15314          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15315          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15316          * and closing the message box when the process is complete.
15317          * @param {String} title The title bar text
15318          * @param {String} msg The message box body text
15319          * @return {Roo.MessageBox} This message box
15320          */
15321         progress : function(title, msg){
15322             this.show({
15323                 title : title,
15324                 msg : msg,
15325                 buttons: false,
15326                 progress:true,
15327                 closable:false,
15328                 minWidth: this.minProgressWidth,
15329                 modal : true
15330             });
15331             return this;
15332         },
15333
15334         /**
15335          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15336          * If a callback function is passed it will be called after the user clicks the button, and the
15337          * id of the button that was clicked will be passed as the only parameter to the callback
15338          * (could also be the top-right close button).
15339          * @param {String} title The title bar text
15340          * @param {String} msg The message box body text
15341          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15342          * @param {Object} scope (optional) The scope of the callback function
15343          * @return {Roo.MessageBox} This message box
15344          */
15345         alert : function(title, msg, fn, scope){
15346             this.show({
15347                 title : title,
15348                 msg : msg,
15349                 buttons: this.OK,
15350                 fn: fn,
15351                 scope : scope,
15352                 modal : true
15353             });
15354             return this;
15355         },
15356
15357         /**
15358          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15359          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15360          * You are responsible for closing the message box when the process is complete.
15361          * @param {String} msg The message box body text
15362          * @param {String} title (optional) The title bar text
15363          * @return {Roo.MessageBox} This message box
15364          */
15365         wait : function(msg, title){
15366             this.show({
15367                 title : title,
15368                 msg : msg,
15369                 buttons: false,
15370                 closable:false,
15371                 progress:true,
15372                 modal:true,
15373                 width:300,
15374                 wait:true
15375             });
15376             waitTimer = Roo.TaskMgr.start({
15377                 run: function(i){
15378                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15379                 },
15380                 interval: 1000
15381             });
15382             return this;
15383         },
15384
15385         /**
15386          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15387          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15388          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15389          * @param {String} title The title bar text
15390          * @param {String} msg The message box body text
15391          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15392          * @param {Object} scope (optional) The scope of the callback function
15393          * @return {Roo.MessageBox} This message box
15394          */
15395         confirm : function(title, msg, fn, scope){
15396             this.show({
15397                 title : title,
15398                 msg : msg,
15399                 buttons: this.YESNO,
15400                 fn: fn,
15401                 scope : scope,
15402                 modal : true
15403             });
15404             return this;
15405         },
15406
15407         /**
15408          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15409          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15410          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15411          * (could also be the top-right close button) and the text that was entered will be passed as the two
15412          * parameters to the callback.
15413          * @param {String} title The title bar text
15414          * @param {String} msg The message box body text
15415          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15416          * @param {Object} scope (optional) The scope of the callback function
15417          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15418          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15419          * @return {Roo.MessageBox} This message box
15420          */
15421         prompt : function(title, msg, fn, scope, multiline){
15422             this.show({
15423                 title : title,
15424                 msg : msg,
15425                 buttons: this.OKCANCEL,
15426                 fn: fn,
15427                 minWidth:250,
15428                 scope : scope,
15429                 prompt:true,
15430                 multiline: multiline,
15431                 modal : true
15432             });
15433             return this;
15434         },
15435
15436         /**
15437          * Button config that displays a single OK button
15438          * @type Object
15439          */
15440         OK : {ok:true},
15441         /**
15442          * Button config that displays Yes and No buttons
15443          * @type Object
15444          */
15445         YESNO : {yes:true, no:true},
15446         /**
15447          * Button config that displays OK and Cancel buttons
15448          * @type Object
15449          */
15450         OKCANCEL : {ok:true, cancel:true},
15451         /**
15452          * Button config that displays Yes, No and Cancel buttons
15453          * @type Object
15454          */
15455         YESNOCANCEL : {yes:true, no:true, cancel:true},
15456
15457         /**
15458          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15459          * @type Number
15460          */
15461         defaultTextHeight : 75,
15462         /**
15463          * The maximum width in pixels of the message box (defaults to 600)
15464          * @type Number
15465          */
15466         maxWidth : 600,
15467         /**
15468          * The minimum width in pixels of the message box (defaults to 100)
15469          * @type Number
15470          */
15471         minWidth : 100,
15472         /**
15473          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15474          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15475          * @type Number
15476          */
15477         minProgressWidth : 250,
15478         /**
15479          * An object containing the default button text strings that can be overriden for localized language support.
15480          * Supported properties are: ok, cancel, yes and no.
15481          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15482          * @type Object
15483          */
15484         buttonText : {
15485             ok : "OK",
15486             cancel : "Cancel",
15487             yes : "Yes",
15488             no : "No"
15489         }
15490     };
15491 }();
15492
15493 /**
15494  * Shorthand for {@link Roo.MessageBox}
15495  */
15496 Roo.Msg = Roo.MessageBox;/*
15497  * Based on:
15498  * Ext JS Library 1.1.1
15499  * Copyright(c) 2006-2007, Ext JS, LLC.
15500  *
15501  * Originally Released Under LGPL - original licence link has changed is not relivant.
15502  *
15503  * Fork - LGPL
15504  * <script type="text/javascript">
15505  */
15506 /**
15507  * @class Roo.QuickTips
15508  * Provides attractive and customizable tooltips for any element.
15509  * @singleton
15510  */
15511 Roo.QuickTips = function(){
15512     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15513     var ce, bd, xy, dd;
15514     var visible = false, disabled = true, inited = false;
15515     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15516     
15517     var onOver = function(e){
15518         if(disabled){
15519             return;
15520         }
15521         var t = e.getTarget();
15522         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15523             return;
15524         }
15525         if(ce && t == ce.el){
15526             clearTimeout(hideProc);
15527             return;
15528         }
15529         if(t && tagEls[t.id]){
15530             tagEls[t.id].el = t;
15531             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15532             return;
15533         }
15534         var ttp, et = Roo.fly(t);
15535         var ns = cfg.namespace;
15536         if(tm.interceptTitles && t.title){
15537             ttp = t.title;
15538             t.qtip = ttp;
15539             t.removeAttribute("title");
15540             e.preventDefault();
15541         }else{
15542             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15543         }
15544         if(ttp){
15545             showProc = show.defer(tm.showDelay, tm, [{
15546                 el: t, 
15547                 text: ttp, 
15548                 width: et.getAttributeNS(ns, cfg.width),
15549                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15550                 title: et.getAttributeNS(ns, cfg.title),
15551                     cls: et.getAttributeNS(ns, cfg.cls)
15552             }]);
15553         }
15554     };
15555     
15556     var onOut = function(e){
15557         clearTimeout(showProc);
15558         var t = e.getTarget();
15559         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15560             hideProc = setTimeout(hide, tm.hideDelay);
15561         }
15562     };
15563     
15564     var onMove = function(e){
15565         if(disabled){
15566             return;
15567         }
15568         xy = e.getXY();
15569         xy[1] += 18;
15570         if(tm.trackMouse && ce){
15571             el.setXY(xy);
15572         }
15573     };
15574     
15575     var onDown = function(e){
15576         clearTimeout(showProc);
15577         clearTimeout(hideProc);
15578         if(!e.within(el)){
15579             if(tm.hideOnClick){
15580                 hide();
15581                 tm.disable();
15582                 tm.enable.defer(100, tm);
15583             }
15584         }
15585     };
15586     
15587     var getPad = function(){
15588         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15589     };
15590
15591     var show = function(o){
15592         if(disabled){
15593             return;
15594         }
15595         clearTimeout(dismissProc);
15596         ce = o;
15597         if(removeCls){ // in case manually hidden
15598             el.removeClass(removeCls);
15599             removeCls = null;
15600         }
15601         if(ce.cls){
15602             el.addClass(ce.cls);
15603             removeCls = ce.cls;
15604         }
15605         if(ce.title){
15606             tipTitle.update(ce.title);
15607             tipTitle.show();
15608         }else{
15609             tipTitle.update('');
15610             tipTitle.hide();
15611         }
15612         el.dom.style.width  = tm.maxWidth+'px';
15613         //tipBody.dom.style.width = '';
15614         tipBodyText.update(o.text);
15615         var p = getPad(), w = ce.width;
15616         if(!w){
15617             var td = tipBodyText.dom;
15618             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15619             if(aw > tm.maxWidth){
15620                 w = tm.maxWidth;
15621             }else if(aw < tm.minWidth){
15622                 w = tm.minWidth;
15623             }else{
15624                 w = aw;
15625             }
15626         }
15627         //tipBody.setWidth(w);
15628         el.setWidth(parseInt(w, 10) + p);
15629         if(ce.autoHide === false){
15630             close.setDisplayed(true);
15631             if(dd){
15632                 dd.unlock();
15633             }
15634         }else{
15635             close.setDisplayed(false);
15636             if(dd){
15637                 dd.lock();
15638             }
15639         }
15640         if(xy){
15641             el.avoidY = xy[1]-18;
15642             el.setXY(xy);
15643         }
15644         if(tm.animate){
15645             el.setOpacity(.1);
15646             el.setStyle("visibility", "visible");
15647             el.fadeIn({callback: afterShow});
15648         }else{
15649             afterShow();
15650         }
15651     };
15652     
15653     var afterShow = function(){
15654         if(ce){
15655             el.show();
15656             esc.enable();
15657             if(tm.autoDismiss && ce.autoHide !== false){
15658                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15659             }
15660         }
15661     };
15662     
15663     var hide = function(noanim){
15664         clearTimeout(dismissProc);
15665         clearTimeout(hideProc);
15666         ce = null;
15667         if(el.isVisible()){
15668             esc.disable();
15669             if(noanim !== true && tm.animate){
15670                 el.fadeOut({callback: afterHide});
15671             }else{
15672                 afterHide();
15673             } 
15674         }
15675     };
15676     
15677     var afterHide = function(){
15678         el.hide();
15679         if(removeCls){
15680             el.removeClass(removeCls);
15681             removeCls = null;
15682         }
15683     };
15684     
15685     return {
15686         /**
15687         * @cfg {Number} minWidth
15688         * The minimum width of the quick tip (defaults to 40)
15689         */
15690        minWidth : 40,
15691         /**
15692         * @cfg {Number} maxWidth
15693         * The maximum width of the quick tip (defaults to 300)
15694         */
15695        maxWidth : 300,
15696         /**
15697         * @cfg {Boolean} interceptTitles
15698         * True to automatically use the element's DOM title value if available (defaults to false)
15699         */
15700        interceptTitles : false,
15701         /**
15702         * @cfg {Boolean} trackMouse
15703         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15704         */
15705        trackMouse : false,
15706         /**
15707         * @cfg {Boolean} hideOnClick
15708         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15709         */
15710        hideOnClick : true,
15711         /**
15712         * @cfg {Number} showDelay
15713         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15714         */
15715        showDelay : 500,
15716         /**
15717         * @cfg {Number} hideDelay
15718         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15719         */
15720        hideDelay : 200,
15721         /**
15722         * @cfg {Boolean} autoHide
15723         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15724         * Used in conjunction with hideDelay.
15725         */
15726        autoHide : true,
15727         /**
15728         * @cfg {Boolean}
15729         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15730         * (defaults to true).  Used in conjunction with autoDismissDelay.
15731         */
15732        autoDismiss : true,
15733         /**
15734         * @cfg {Number}
15735         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15736         */
15737        autoDismissDelay : 5000,
15738        /**
15739         * @cfg {Boolean} animate
15740         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15741         */
15742        animate : false,
15743
15744        /**
15745         * @cfg {String} title
15746         * Title text to display (defaults to '').  This can be any valid HTML markup.
15747         */
15748         title: '',
15749        /**
15750         * @cfg {String} text
15751         * Body text to display (defaults to '').  This can be any valid HTML markup.
15752         */
15753         text : '',
15754        /**
15755         * @cfg {String} cls
15756         * A CSS class to apply to the base quick tip element (defaults to '').
15757         */
15758         cls : '',
15759        /**
15760         * @cfg {Number} width
15761         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15762         * minWidth or maxWidth.
15763         */
15764         width : null,
15765
15766     /**
15767      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15768      * or display QuickTips in a page.
15769      */
15770        init : function(){
15771           tm = Roo.QuickTips;
15772           cfg = tm.tagConfig;
15773           if(!inited){
15774               if(!Roo.isReady){ // allow calling of init() before onReady
15775                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15776                   return;
15777               }
15778               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15779               el.fxDefaults = {stopFx: true};
15780               // maximum custom styling
15781               //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>');
15782               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>');              
15783               tipTitle = el.child('h3');
15784               tipTitle.enableDisplayMode("block");
15785               tipBody = el.child('div.x-tip-bd');
15786               tipBodyText = el.child('div.x-tip-bd-inner');
15787               //bdLeft = el.child('div.x-tip-bd-left');
15788               //bdRight = el.child('div.x-tip-bd-right');
15789               close = el.child('div.x-tip-close');
15790               close.enableDisplayMode("block");
15791               close.on("click", hide);
15792               var d = Roo.get(document);
15793               d.on("mousedown", onDown);
15794               d.on("mouseover", onOver);
15795               d.on("mouseout", onOut);
15796               d.on("mousemove", onMove);
15797               esc = d.addKeyListener(27, hide);
15798               esc.disable();
15799               if(Roo.dd.DD){
15800                   dd = el.initDD("default", null, {
15801                       onDrag : function(){
15802                           el.sync();  
15803                       }
15804                   });
15805                   dd.setHandleElId(tipTitle.id);
15806                   dd.lock();
15807               }
15808               inited = true;
15809           }
15810           this.enable(); 
15811        },
15812
15813     /**
15814      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15815      * are supported:
15816      * <pre>
15817 Property    Type                   Description
15818 ----------  ---------------------  ------------------------------------------------------------------------
15819 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15820      * </ul>
15821      * @param {Object} config The config object
15822      */
15823        register : function(config){
15824            var cs = config instanceof Array ? config : arguments;
15825            for(var i = 0, len = cs.length; i < len; i++) {
15826                var c = cs[i];
15827                var target = c.target;
15828                if(target){
15829                    if(target instanceof Array){
15830                        for(var j = 0, jlen = target.length; j < jlen; j++){
15831                            tagEls[target[j]] = c;
15832                        }
15833                    }else{
15834                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15835                    }
15836                }
15837            }
15838        },
15839
15840     /**
15841      * Removes this quick tip from its element and destroys it.
15842      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15843      */
15844        unregister : function(el){
15845            delete tagEls[Roo.id(el)];
15846        },
15847
15848     /**
15849      * Enable this quick tip.
15850      */
15851        enable : function(){
15852            if(inited && disabled){
15853                locks.pop();
15854                if(locks.length < 1){
15855                    disabled = false;
15856                }
15857            }
15858        },
15859
15860     /**
15861      * Disable this quick tip.
15862      */
15863        disable : function(){
15864           disabled = true;
15865           clearTimeout(showProc);
15866           clearTimeout(hideProc);
15867           clearTimeout(dismissProc);
15868           if(ce){
15869               hide(true);
15870           }
15871           locks.push(1);
15872        },
15873
15874     /**
15875      * Returns true if the quick tip is enabled, else false.
15876      */
15877        isEnabled : function(){
15878             return !disabled;
15879        },
15880
15881         // private
15882        tagConfig : {
15883            namespace : "ext",
15884            attribute : "qtip",
15885            width : "width",
15886            target : "target",
15887            title : "qtitle",
15888            hide : "hide",
15889            cls : "qclass"
15890        }
15891    };
15892 }();
15893
15894 // backwards compat
15895 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15896  * Based on:
15897  * Ext JS Library 1.1.1
15898  * Copyright(c) 2006-2007, Ext JS, LLC.
15899  *
15900  * Originally Released Under LGPL - original licence link has changed is not relivant.
15901  *
15902  * Fork - LGPL
15903  * <script type="text/javascript">
15904  */
15905  
15906
15907 /**
15908  * @class Roo.tree.TreePanel
15909  * @extends Roo.data.Tree
15910
15911  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15912  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15913  * @cfg {Boolean} enableDD true to enable drag and drop
15914  * @cfg {Boolean} enableDrag true to enable just drag
15915  * @cfg {Boolean} enableDrop true to enable just drop
15916  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15917  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15918  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15919  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15920  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15921  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15922  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15923  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15924  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15925  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15926  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15927  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15928  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15929  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15930  * @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>
15931  * @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>
15932  * 
15933  * @constructor
15934  * @param {String/HTMLElement/Element} el The container element
15935  * @param {Object} config
15936  */
15937 Roo.tree.TreePanel = function(el, config){
15938     var root = false;
15939     var loader = false;
15940     if (config.root) {
15941         root = config.root;
15942         delete config.root;
15943     }
15944     if (config.loader) {
15945         loader = config.loader;
15946         delete config.loader;
15947     }
15948     
15949     Roo.apply(this, config);
15950     Roo.tree.TreePanel.superclass.constructor.call(this);
15951     this.el = Roo.get(el);
15952     this.el.addClass('x-tree');
15953     //console.log(root);
15954     if (root) {
15955         this.setRootNode( Roo.factory(root, Roo.tree));
15956     }
15957     if (loader) {
15958         this.loader = Roo.factory(loader, Roo.tree);
15959     }
15960    /**
15961     * Read-only. The id of the container element becomes this TreePanel's id.
15962     */
15963     this.id = this.el.id;
15964     this.addEvents({
15965         /**
15966         * @event beforeload
15967         * Fires before a node is loaded, return false to cancel
15968         * @param {Node} node The node being loaded
15969         */
15970         "beforeload" : true,
15971         /**
15972         * @event load
15973         * Fires when a node is loaded
15974         * @param {Node} node The node that was loaded
15975         */
15976         "load" : true,
15977         /**
15978         * @event textchange
15979         * Fires when the text for a node is changed
15980         * @param {Node} node The node
15981         * @param {String} text The new text
15982         * @param {String} oldText The old text
15983         */
15984         "textchange" : true,
15985         /**
15986         * @event beforeexpand
15987         * Fires before a node is expanded, return false to cancel.
15988         * @param {Node} node The node
15989         * @param {Boolean} deep
15990         * @param {Boolean} anim
15991         */
15992         "beforeexpand" : true,
15993         /**
15994         * @event beforecollapse
15995         * Fires before a node is collapsed, return false to cancel.
15996         * @param {Node} node The node
15997         * @param {Boolean} deep
15998         * @param {Boolean} anim
15999         */
16000         "beforecollapse" : true,
16001         /**
16002         * @event expand
16003         * Fires when a node is expanded
16004         * @param {Node} node The node
16005         */
16006         "expand" : true,
16007         /**
16008         * @event disabledchange
16009         * Fires when the disabled status of a node changes
16010         * @param {Node} node The node
16011         * @param {Boolean} disabled
16012         */
16013         "disabledchange" : true,
16014         /**
16015         * @event collapse
16016         * Fires when a node is collapsed
16017         * @param {Node} node The node
16018         */
16019         "collapse" : true,
16020         /**
16021         * @event beforeclick
16022         * Fires before click processing on a node. Return false to cancel the default action.
16023         * @param {Node} node The node
16024         * @param {Roo.EventObject} e The event object
16025         */
16026         "beforeclick":true,
16027         /**
16028         * @event checkchange
16029         * Fires when a node with a checkbox's checked property changes
16030         * @param {Node} this This node
16031         * @param {Boolean} checked
16032         */
16033         "checkchange":true,
16034         /**
16035         * @event click
16036         * Fires when a node is clicked
16037         * @param {Node} node The node
16038         * @param {Roo.EventObject} e The event object
16039         */
16040         "click":true,
16041         /**
16042         * @event dblclick
16043         * Fires when a node is double clicked
16044         * @param {Node} node The node
16045         * @param {Roo.EventObject} e The event object
16046         */
16047         "dblclick":true,
16048         /**
16049         * @event contextmenu
16050         * Fires when a node is right clicked
16051         * @param {Node} node The node
16052         * @param {Roo.EventObject} e The event object
16053         */
16054         "contextmenu":true,
16055         /**
16056         * @event beforechildrenrendered
16057         * Fires right before the child nodes for a node are rendered
16058         * @param {Node} node The node
16059         */
16060         "beforechildrenrendered":true,
16061         /**
16062         * @event startdrag
16063         * Fires when a node starts being dragged
16064         * @param {Roo.tree.TreePanel} this
16065         * @param {Roo.tree.TreeNode} node
16066         * @param {event} e The raw browser event
16067         */ 
16068        "startdrag" : true,
16069        /**
16070         * @event enddrag
16071         * Fires when a drag operation is complete
16072         * @param {Roo.tree.TreePanel} this
16073         * @param {Roo.tree.TreeNode} node
16074         * @param {event} e The raw browser event
16075         */
16076        "enddrag" : true,
16077        /**
16078         * @event dragdrop
16079         * Fires when a dragged node is dropped on a valid DD target
16080         * @param {Roo.tree.TreePanel} this
16081         * @param {Roo.tree.TreeNode} node
16082         * @param {DD} dd The dd it was dropped on
16083         * @param {event} e The raw browser event
16084         */
16085        "dragdrop" : true,
16086        /**
16087         * @event beforenodedrop
16088         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16089         * passed to handlers has the following properties:<br />
16090         * <ul style="padding:5px;padding-left:16px;">
16091         * <li>tree - The TreePanel</li>
16092         * <li>target - The node being targeted for the drop</li>
16093         * <li>data - The drag data from the drag source</li>
16094         * <li>point - The point of the drop - append, above or below</li>
16095         * <li>source - The drag source</li>
16096         * <li>rawEvent - Raw mouse event</li>
16097         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16098         * to be inserted by setting them on this object.</li>
16099         * <li>cancel - Set this to true to cancel the drop.</li>
16100         * </ul>
16101         * @param {Object} dropEvent
16102         */
16103        "beforenodedrop" : true,
16104        /**
16105         * @event nodedrop
16106         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16107         * passed to handlers has the following properties:<br />
16108         * <ul style="padding:5px;padding-left:16px;">
16109         * <li>tree - The TreePanel</li>
16110         * <li>target - The node being targeted for the drop</li>
16111         * <li>data - The drag data from the drag source</li>
16112         * <li>point - The point of the drop - append, above or below</li>
16113         * <li>source - The drag source</li>
16114         * <li>rawEvent - Raw mouse event</li>
16115         * <li>dropNode - Dropped node(s).</li>
16116         * </ul>
16117         * @param {Object} dropEvent
16118         */
16119        "nodedrop" : true,
16120         /**
16121         * @event nodedragover
16122         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16123         * passed to handlers has the following properties:<br />
16124         * <ul style="padding:5px;padding-left:16px;">
16125         * <li>tree - The TreePanel</li>
16126         * <li>target - The node being targeted for the drop</li>
16127         * <li>data - The drag data from the drag source</li>
16128         * <li>point - The point of the drop - append, above or below</li>
16129         * <li>source - The drag source</li>
16130         * <li>rawEvent - Raw mouse event</li>
16131         * <li>dropNode - Drop node(s) provided by the source.</li>
16132         * <li>cancel - Set this to true to signal drop not allowed.</li>
16133         * </ul>
16134         * @param {Object} dragOverEvent
16135         */
16136        "nodedragover" : true
16137         
16138     });
16139     if(this.singleExpand){
16140        this.on("beforeexpand", this.restrictExpand, this);
16141     }
16142     if (this.editor) {
16143         this.editor.tree = this;
16144         this.editor = Roo.factory(this.editor, Roo.tree);
16145     }
16146     
16147     if (this.selModel) {
16148         this.selModel = Roo.factory(this.selModel, Roo.tree);
16149     }
16150    
16151 };
16152 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16153     rootVisible : true,
16154     animate: Roo.enableFx,
16155     lines : true,
16156     enableDD : false,
16157     hlDrop : Roo.enableFx,
16158   
16159     renderer: false,
16160     
16161     rendererTip: false,
16162     // private
16163     restrictExpand : function(node){
16164         var p = node.parentNode;
16165         if(p){
16166             if(p.expandedChild && p.expandedChild.parentNode == p){
16167                 p.expandedChild.collapse();
16168             }
16169             p.expandedChild = node;
16170         }
16171     },
16172
16173     // private override
16174     setRootNode : function(node){
16175         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16176         if(!this.rootVisible){
16177             node.ui = new Roo.tree.RootTreeNodeUI(node);
16178         }
16179         return node;
16180     },
16181
16182     /**
16183      * Returns the container element for this TreePanel
16184      */
16185     getEl : function(){
16186         return this.el;
16187     },
16188
16189     /**
16190      * Returns the default TreeLoader for this TreePanel
16191      */
16192     getLoader : function(){
16193         return this.loader;
16194     },
16195
16196     /**
16197      * Expand all nodes
16198      */
16199     expandAll : function(){
16200         this.root.expand(true);
16201     },
16202
16203     /**
16204      * Collapse all nodes
16205      */
16206     collapseAll : function(){
16207         this.root.collapse(true);
16208     },
16209
16210     /**
16211      * Returns the selection model used by this TreePanel
16212      */
16213     getSelectionModel : function(){
16214         if(!this.selModel){
16215             this.selModel = new Roo.tree.DefaultSelectionModel();
16216         }
16217         return this.selModel;
16218     },
16219
16220     /**
16221      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16222      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16223      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16224      * @return {Array}
16225      */
16226     getChecked : function(a, startNode){
16227         startNode = startNode || this.root;
16228         var r = [];
16229         var f = function(){
16230             if(this.attributes.checked){
16231                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16232             }
16233         }
16234         startNode.cascade(f);
16235         return r;
16236     },
16237
16238     /**
16239      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16240      * @param {String} path
16241      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16242      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16243      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16244      */
16245     expandPath : function(path, attr, callback){
16246         attr = attr || "id";
16247         var keys = path.split(this.pathSeparator);
16248         var curNode = this.root;
16249         if(curNode.attributes[attr] != keys[1]){ // invalid root
16250             if(callback){
16251                 callback(false, null);
16252             }
16253             return;
16254         }
16255         var index = 1;
16256         var f = function(){
16257             if(++index == keys.length){
16258                 if(callback){
16259                     callback(true, curNode);
16260                 }
16261                 return;
16262             }
16263             var c = curNode.findChild(attr, keys[index]);
16264             if(!c){
16265                 if(callback){
16266                     callback(false, curNode);
16267                 }
16268                 return;
16269             }
16270             curNode = c;
16271             c.expand(false, false, f);
16272         };
16273         curNode.expand(false, false, f);
16274     },
16275
16276     /**
16277      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16278      * @param {String} path
16279      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16280      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16281      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16282      */
16283     selectPath : function(path, attr, callback){
16284         attr = attr || "id";
16285         var keys = path.split(this.pathSeparator);
16286         var v = keys.pop();
16287         if(keys.length > 0){
16288             var f = function(success, node){
16289                 if(success && node){
16290                     var n = node.findChild(attr, v);
16291                     if(n){
16292                         n.select();
16293                         if(callback){
16294                             callback(true, n);
16295                         }
16296                     }else if(callback){
16297                         callback(false, n);
16298                     }
16299                 }else{
16300                     if(callback){
16301                         callback(false, n);
16302                     }
16303                 }
16304             };
16305             this.expandPath(keys.join(this.pathSeparator), attr, f);
16306         }else{
16307             this.root.select();
16308             if(callback){
16309                 callback(true, this.root);
16310             }
16311         }
16312     },
16313
16314     getTreeEl : function(){
16315         return this.el;
16316     },
16317
16318     /**
16319      * Trigger rendering of this TreePanel
16320      */
16321     render : function(){
16322         if (this.innerCt) {
16323             return this; // stop it rendering more than once!!
16324         }
16325         
16326         this.innerCt = this.el.createChild({tag:"ul",
16327                cls:"x-tree-root-ct " +
16328                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16329
16330         if(this.containerScroll){
16331             Roo.dd.ScrollManager.register(this.el);
16332         }
16333         if((this.enableDD || this.enableDrop) && !this.dropZone){
16334            /**
16335             * The dropZone used by this tree if drop is enabled
16336             * @type Roo.tree.TreeDropZone
16337             */
16338              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16339                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16340            });
16341         }
16342         if((this.enableDD || this.enableDrag) && !this.dragZone){
16343            /**
16344             * The dragZone used by this tree if drag is enabled
16345             * @type Roo.tree.TreeDragZone
16346             */
16347             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16348                ddGroup: this.ddGroup || "TreeDD",
16349                scroll: this.ddScroll
16350            });
16351         }
16352         this.getSelectionModel().init(this);
16353         if (!this.root) {
16354             Roo.log("ROOT not set in tree");
16355             return this;
16356         }
16357         this.root.render();
16358         if(!this.rootVisible){
16359             this.root.renderChildren();
16360         }
16361         return this;
16362     }
16363 });/*
16364  * Based on:
16365  * Ext JS Library 1.1.1
16366  * Copyright(c) 2006-2007, Ext JS, LLC.
16367  *
16368  * Originally Released Under LGPL - original licence link has changed is not relivant.
16369  *
16370  * Fork - LGPL
16371  * <script type="text/javascript">
16372  */
16373  
16374
16375 /**
16376  * @class Roo.tree.DefaultSelectionModel
16377  * @extends Roo.util.Observable
16378  * The default single selection for a TreePanel.
16379  * @param {Object} cfg Configuration
16380  */
16381 Roo.tree.DefaultSelectionModel = function(cfg){
16382    this.selNode = null;
16383    
16384    
16385    
16386    this.addEvents({
16387        /**
16388         * @event selectionchange
16389         * Fires when the selected node changes
16390         * @param {DefaultSelectionModel} this
16391         * @param {TreeNode} node the new selection
16392         */
16393        "selectionchange" : true,
16394
16395        /**
16396         * @event beforeselect
16397         * Fires before the selected node changes, return false to cancel the change
16398         * @param {DefaultSelectionModel} this
16399         * @param {TreeNode} node the new selection
16400         * @param {TreeNode} node the old selection
16401         */
16402        "beforeselect" : true
16403    });
16404    
16405     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16406 };
16407
16408 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16409     init : function(tree){
16410         this.tree = tree;
16411         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16412         tree.on("click", this.onNodeClick, this);
16413     },
16414     
16415     onNodeClick : function(node, e){
16416         if (e.ctrlKey && this.selNode == node)  {
16417             this.unselect(node);
16418             return;
16419         }
16420         this.select(node);
16421     },
16422     
16423     /**
16424      * Select a node.
16425      * @param {TreeNode} node The node to select
16426      * @return {TreeNode} The selected node
16427      */
16428     select : function(node){
16429         var last = this.selNode;
16430         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16431             if(last){
16432                 last.ui.onSelectedChange(false);
16433             }
16434             this.selNode = node;
16435             node.ui.onSelectedChange(true);
16436             this.fireEvent("selectionchange", this, node, last);
16437         }
16438         return node;
16439     },
16440     
16441     /**
16442      * Deselect a node.
16443      * @param {TreeNode} node The node to unselect
16444      */
16445     unselect : function(node){
16446         if(this.selNode == node){
16447             this.clearSelections();
16448         }    
16449     },
16450     
16451     /**
16452      * Clear all selections
16453      */
16454     clearSelections : function(){
16455         var n = this.selNode;
16456         if(n){
16457             n.ui.onSelectedChange(false);
16458             this.selNode = null;
16459             this.fireEvent("selectionchange", this, null);
16460         }
16461         return n;
16462     },
16463     
16464     /**
16465      * Get the selected node
16466      * @return {TreeNode} The selected node
16467      */
16468     getSelectedNode : function(){
16469         return this.selNode;    
16470     },
16471     
16472     /**
16473      * Returns true if the node is selected
16474      * @param {TreeNode} node The node to check
16475      * @return {Boolean}
16476      */
16477     isSelected : function(node){
16478         return this.selNode == node;  
16479     },
16480
16481     /**
16482      * Selects the node above the selected node in the tree, intelligently walking the nodes
16483      * @return TreeNode The new selection
16484      */
16485     selectPrevious : function(){
16486         var s = this.selNode || this.lastSelNode;
16487         if(!s){
16488             return null;
16489         }
16490         var ps = s.previousSibling;
16491         if(ps){
16492             if(!ps.isExpanded() || ps.childNodes.length < 1){
16493                 return this.select(ps);
16494             } else{
16495                 var lc = ps.lastChild;
16496                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16497                     lc = lc.lastChild;
16498                 }
16499                 return this.select(lc);
16500             }
16501         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16502             return this.select(s.parentNode);
16503         }
16504         return null;
16505     },
16506
16507     /**
16508      * Selects the node above the selected node in the tree, intelligently walking the nodes
16509      * @return TreeNode The new selection
16510      */
16511     selectNext : function(){
16512         var s = this.selNode || this.lastSelNode;
16513         if(!s){
16514             return null;
16515         }
16516         if(s.firstChild && s.isExpanded()){
16517              return this.select(s.firstChild);
16518          }else if(s.nextSibling){
16519              return this.select(s.nextSibling);
16520          }else if(s.parentNode){
16521             var newS = null;
16522             s.parentNode.bubble(function(){
16523                 if(this.nextSibling){
16524                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16525                     return false;
16526                 }
16527             });
16528             return newS;
16529          }
16530         return null;
16531     },
16532
16533     onKeyDown : function(e){
16534         var s = this.selNode || this.lastSelNode;
16535         // undesirable, but required
16536         var sm = this;
16537         if(!s){
16538             return;
16539         }
16540         var k = e.getKey();
16541         switch(k){
16542              case e.DOWN:
16543                  e.stopEvent();
16544                  this.selectNext();
16545              break;
16546              case e.UP:
16547                  e.stopEvent();
16548                  this.selectPrevious();
16549              break;
16550              case e.RIGHT:
16551                  e.preventDefault();
16552                  if(s.hasChildNodes()){
16553                      if(!s.isExpanded()){
16554                          s.expand();
16555                      }else if(s.firstChild){
16556                          this.select(s.firstChild, e);
16557                      }
16558                  }
16559              break;
16560              case e.LEFT:
16561                  e.preventDefault();
16562                  if(s.hasChildNodes() && s.isExpanded()){
16563                      s.collapse();
16564                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16565                      this.select(s.parentNode, e);
16566                  }
16567              break;
16568         };
16569     }
16570 });
16571
16572 /**
16573  * @class Roo.tree.MultiSelectionModel
16574  * @extends Roo.util.Observable
16575  * Multi selection for a TreePanel.
16576  * @param {Object} cfg Configuration
16577  */
16578 Roo.tree.MultiSelectionModel = function(){
16579    this.selNodes = [];
16580    this.selMap = {};
16581    this.addEvents({
16582        /**
16583         * @event selectionchange
16584         * Fires when the selected nodes change
16585         * @param {MultiSelectionModel} this
16586         * @param {Array} nodes Array of the selected nodes
16587         */
16588        "selectionchange" : true
16589    });
16590    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16591    
16592 };
16593
16594 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16595     init : function(tree){
16596         this.tree = tree;
16597         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16598         tree.on("click", this.onNodeClick, this);
16599     },
16600     
16601     onNodeClick : function(node, e){
16602         this.select(node, e, e.ctrlKey);
16603     },
16604     
16605     /**
16606      * Select a node.
16607      * @param {TreeNode} node The node to select
16608      * @param {EventObject} e (optional) An event associated with the selection
16609      * @param {Boolean} keepExisting True to retain existing selections
16610      * @return {TreeNode} The selected node
16611      */
16612     select : function(node, e, keepExisting){
16613         if(keepExisting !== true){
16614             this.clearSelections(true);
16615         }
16616         if(this.isSelected(node)){
16617             this.lastSelNode = node;
16618             return node;
16619         }
16620         this.selNodes.push(node);
16621         this.selMap[node.id] = node;
16622         this.lastSelNode = node;
16623         node.ui.onSelectedChange(true);
16624         this.fireEvent("selectionchange", this, this.selNodes);
16625         return node;
16626     },
16627     
16628     /**
16629      * Deselect a node.
16630      * @param {TreeNode} node The node to unselect
16631      */
16632     unselect : function(node){
16633         if(this.selMap[node.id]){
16634             node.ui.onSelectedChange(false);
16635             var sn = this.selNodes;
16636             var index = -1;
16637             if(sn.indexOf){
16638                 index = sn.indexOf(node);
16639             }else{
16640                 for(var i = 0, len = sn.length; i < len; i++){
16641                     if(sn[i] == node){
16642                         index = i;
16643                         break;
16644                     }
16645                 }
16646             }
16647             if(index != -1){
16648                 this.selNodes.splice(index, 1);
16649             }
16650             delete this.selMap[node.id];
16651             this.fireEvent("selectionchange", this, this.selNodes);
16652         }
16653     },
16654     
16655     /**
16656      * Clear all selections
16657      */
16658     clearSelections : function(suppressEvent){
16659         var sn = this.selNodes;
16660         if(sn.length > 0){
16661             for(var i = 0, len = sn.length; i < len; i++){
16662                 sn[i].ui.onSelectedChange(false);
16663             }
16664             this.selNodes = [];
16665             this.selMap = {};
16666             if(suppressEvent !== true){
16667                 this.fireEvent("selectionchange", this, this.selNodes);
16668             }
16669         }
16670     },
16671     
16672     /**
16673      * Returns true if the node is selected
16674      * @param {TreeNode} node The node to check
16675      * @return {Boolean}
16676      */
16677     isSelected : function(node){
16678         return this.selMap[node.id] ? true : false;  
16679     },
16680     
16681     /**
16682      * Returns an array of the selected nodes
16683      * @return {Array}
16684      */
16685     getSelectedNodes : function(){
16686         return this.selNodes;    
16687     },
16688
16689     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16690
16691     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16692
16693     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16694 });/*
16695  * Based on:
16696  * Ext JS Library 1.1.1
16697  * Copyright(c) 2006-2007, Ext JS, LLC.
16698  *
16699  * Originally Released Under LGPL - original licence link has changed is not relivant.
16700  *
16701  * Fork - LGPL
16702  * <script type="text/javascript">
16703  */
16704  
16705 /**
16706  * @class Roo.tree.TreeNode
16707  * @extends Roo.data.Node
16708  * @cfg {String} text The text for this node
16709  * @cfg {Boolean} expanded true to start the node expanded
16710  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16711  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16712  * @cfg {Boolean} disabled true to start the node disabled
16713  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16714  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16715  * @cfg {String} cls A css class to be added to the node
16716  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16717  * @cfg {String} href URL of the link used for the node (defaults to #)
16718  * @cfg {String} hrefTarget target frame for the link
16719  * @cfg {String} qtip An Ext QuickTip for the node
16720  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16721  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16722  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16723  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16724  * (defaults to undefined with no checkbox rendered)
16725  * @constructor
16726  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16727  */
16728 Roo.tree.TreeNode = function(attributes){
16729     attributes = attributes || {};
16730     if(typeof attributes == "string"){
16731         attributes = {text: attributes};
16732     }
16733     this.childrenRendered = false;
16734     this.rendered = false;
16735     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16736     this.expanded = attributes.expanded === true;
16737     this.isTarget = attributes.isTarget !== false;
16738     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16739     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16740
16741     /**
16742      * Read-only. The text for this node. To change it use setText().
16743      * @type String
16744      */
16745     this.text = attributes.text;
16746     /**
16747      * True if this node is disabled.
16748      * @type Boolean
16749      */
16750     this.disabled = attributes.disabled === true;
16751
16752     this.addEvents({
16753         /**
16754         * @event textchange
16755         * Fires when the text for this node is changed
16756         * @param {Node} this This node
16757         * @param {String} text The new text
16758         * @param {String} oldText The old text
16759         */
16760         "textchange" : true,
16761         /**
16762         * @event beforeexpand
16763         * Fires before this node is expanded, return false to cancel.
16764         * @param {Node} this This node
16765         * @param {Boolean} deep
16766         * @param {Boolean} anim
16767         */
16768         "beforeexpand" : true,
16769         /**
16770         * @event beforecollapse
16771         * Fires before this node is collapsed, return false to cancel.
16772         * @param {Node} this This node
16773         * @param {Boolean} deep
16774         * @param {Boolean} anim
16775         */
16776         "beforecollapse" : true,
16777         /**
16778         * @event expand
16779         * Fires when this node is expanded
16780         * @param {Node} this This node
16781         */
16782         "expand" : true,
16783         /**
16784         * @event disabledchange
16785         * Fires when the disabled status of this node changes
16786         * @param {Node} this This node
16787         * @param {Boolean} disabled
16788         */
16789         "disabledchange" : true,
16790         /**
16791         * @event collapse
16792         * Fires when this node is collapsed
16793         * @param {Node} this This node
16794         */
16795         "collapse" : true,
16796         /**
16797         * @event beforeclick
16798         * Fires before click processing. Return false to cancel the default action.
16799         * @param {Node} this This node
16800         * @param {Roo.EventObject} e The event object
16801         */
16802         "beforeclick":true,
16803         /**
16804         * @event checkchange
16805         * Fires when a node with a checkbox's checked property changes
16806         * @param {Node} this This node
16807         * @param {Boolean} checked
16808         */
16809         "checkchange":true,
16810         /**
16811         * @event click
16812         * Fires when this node is clicked
16813         * @param {Node} this This node
16814         * @param {Roo.EventObject} e The event object
16815         */
16816         "click":true,
16817         /**
16818         * @event dblclick
16819         * Fires when this node is double clicked
16820         * @param {Node} this This node
16821         * @param {Roo.EventObject} e The event object
16822         */
16823         "dblclick":true,
16824         /**
16825         * @event contextmenu
16826         * Fires when this node is right clicked
16827         * @param {Node} this This node
16828         * @param {Roo.EventObject} e The event object
16829         */
16830         "contextmenu":true,
16831         /**
16832         * @event beforechildrenrendered
16833         * Fires right before the child nodes for this node are rendered
16834         * @param {Node} this This node
16835         */
16836         "beforechildrenrendered":true
16837     });
16838
16839     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16840
16841     /**
16842      * Read-only. The UI for this node
16843      * @type TreeNodeUI
16844      */
16845     this.ui = new uiClass(this);
16846     
16847     // finally support items[]
16848     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16849         return;
16850     }
16851     
16852     
16853     Roo.each(this.attributes.items, function(c) {
16854         this.appendChild(Roo.factory(c,Roo.Tree));
16855     }, this);
16856     delete this.attributes.items;
16857     
16858     
16859     
16860 };
16861 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16862     preventHScroll: true,
16863     /**
16864      * Returns true if this node is expanded
16865      * @return {Boolean}
16866      */
16867     isExpanded : function(){
16868         return this.expanded;
16869     },
16870
16871     /**
16872      * Returns the UI object for this node
16873      * @return {TreeNodeUI}
16874      */
16875     getUI : function(){
16876         return this.ui;
16877     },
16878
16879     // private override
16880     setFirstChild : function(node){
16881         var of = this.firstChild;
16882         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16883         if(this.childrenRendered && of && node != of){
16884             of.renderIndent(true, true);
16885         }
16886         if(this.rendered){
16887             this.renderIndent(true, true);
16888         }
16889     },
16890
16891     // private override
16892     setLastChild : function(node){
16893         var ol = this.lastChild;
16894         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16895         if(this.childrenRendered && ol && node != ol){
16896             ol.renderIndent(true, true);
16897         }
16898         if(this.rendered){
16899             this.renderIndent(true, true);
16900         }
16901     },
16902
16903     // these methods are overridden to provide lazy rendering support
16904     // private override
16905     appendChild : function()
16906     {
16907         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16908         if(node && this.childrenRendered){
16909             node.render();
16910         }
16911         this.ui.updateExpandIcon();
16912         return node;
16913     },
16914
16915     // private override
16916     removeChild : function(node){
16917         this.ownerTree.getSelectionModel().unselect(node);
16918         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16919         // if it's been rendered remove dom node
16920         if(this.childrenRendered){
16921             node.ui.remove();
16922         }
16923         if(this.childNodes.length < 1){
16924             this.collapse(false, false);
16925         }else{
16926             this.ui.updateExpandIcon();
16927         }
16928         if(!this.firstChild) {
16929             this.childrenRendered = false;
16930         }
16931         return node;
16932     },
16933
16934     // private override
16935     insertBefore : function(node, refNode){
16936         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16937         if(newNode && refNode && this.childrenRendered){
16938             node.render();
16939         }
16940         this.ui.updateExpandIcon();
16941         return newNode;
16942     },
16943
16944     /**
16945      * Sets the text for this node
16946      * @param {String} text
16947      */
16948     setText : function(text){
16949         var oldText = this.text;
16950         this.text = text;
16951         this.attributes.text = text;
16952         if(this.rendered){ // event without subscribing
16953             this.ui.onTextChange(this, text, oldText);
16954         }
16955         this.fireEvent("textchange", this, text, oldText);
16956     },
16957
16958     /**
16959      * Triggers selection of this node
16960      */
16961     select : function(){
16962         this.getOwnerTree().getSelectionModel().select(this);
16963     },
16964
16965     /**
16966      * Triggers deselection of this node
16967      */
16968     unselect : function(){
16969         this.getOwnerTree().getSelectionModel().unselect(this);
16970     },
16971
16972     /**
16973      * Returns true if this node is selected
16974      * @return {Boolean}
16975      */
16976     isSelected : function(){
16977         return this.getOwnerTree().getSelectionModel().isSelected(this);
16978     },
16979
16980     /**
16981      * Expand this node.
16982      * @param {Boolean} deep (optional) True to expand all children as well
16983      * @param {Boolean} anim (optional) false to cancel the default animation
16984      * @param {Function} callback (optional) A callback to be called when
16985      * expanding this node completes (does not wait for deep expand to complete).
16986      * Called with 1 parameter, this node.
16987      */
16988     expand : function(deep, anim, callback){
16989         if(!this.expanded){
16990             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
16991                 return;
16992             }
16993             if(!this.childrenRendered){
16994                 this.renderChildren();
16995             }
16996             this.expanded = true;
16997             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
16998                 this.ui.animExpand(function(){
16999                     this.fireEvent("expand", this);
17000                     if(typeof callback == "function"){
17001                         callback(this);
17002                     }
17003                     if(deep === true){
17004                         this.expandChildNodes(true);
17005                     }
17006                 }.createDelegate(this));
17007                 return;
17008             }else{
17009                 this.ui.expand();
17010                 this.fireEvent("expand", this);
17011                 if(typeof callback == "function"){
17012                     callback(this);
17013                 }
17014             }
17015         }else{
17016            if(typeof callback == "function"){
17017                callback(this);
17018            }
17019         }
17020         if(deep === true){
17021             this.expandChildNodes(true);
17022         }
17023     },
17024
17025     isHiddenRoot : function(){
17026         return this.isRoot && !this.getOwnerTree().rootVisible;
17027     },
17028
17029     /**
17030      * Collapse this node.
17031      * @param {Boolean} deep (optional) True to collapse all children as well
17032      * @param {Boolean} anim (optional) false to cancel the default animation
17033      */
17034     collapse : function(deep, anim){
17035         if(this.expanded && !this.isHiddenRoot()){
17036             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17037                 return;
17038             }
17039             this.expanded = false;
17040             if((this.getOwnerTree().animate && anim !== false) || anim){
17041                 this.ui.animCollapse(function(){
17042                     this.fireEvent("collapse", this);
17043                     if(deep === true){
17044                         this.collapseChildNodes(true);
17045                     }
17046                 }.createDelegate(this));
17047                 return;
17048             }else{
17049                 this.ui.collapse();
17050                 this.fireEvent("collapse", this);
17051             }
17052         }
17053         if(deep === true){
17054             var cs = this.childNodes;
17055             for(var i = 0, len = cs.length; i < len; i++) {
17056                 cs[i].collapse(true, false);
17057             }
17058         }
17059     },
17060
17061     // private
17062     delayedExpand : function(delay){
17063         if(!this.expandProcId){
17064             this.expandProcId = this.expand.defer(delay, this);
17065         }
17066     },
17067
17068     // private
17069     cancelExpand : function(){
17070         if(this.expandProcId){
17071             clearTimeout(this.expandProcId);
17072         }
17073         this.expandProcId = false;
17074     },
17075
17076     /**
17077      * Toggles expanded/collapsed state of the node
17078      */
17079     toggle : function(){
17080         if(this.expanded){
17081             this.collapse();
17082         }else{
17083             this.expand();
17084         }
17085     },
17086
17087     /**
17088      * Ensures all parent nodes are expanded
17089      */
17090     ensureVisible : function(callback){
17091         var tree = this.getOwnerTree();
17092         tree.expandPath(this.parentNode.getPath(), false, function(){
17093             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17094             Roo.callback(callback);
17095         }.createDelegate(this));
17096     },
17097
17098     /**
17099      * Expand all child nodes
17100      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17101      */
17102     expandChildNodes : function(deep){
17103         var cs = this.childNodes;
17104         for(var i = 0, len = cs.length; i < len; i++) {
17105                 cs[i].expand(deep);
17106         }
17107     },
17108
17109     /**
17110      * Collapse all child nodes
17111      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17112      */
17113     collapseChildNodes : function(deep){
17114         var cs = this.childNodes;
17115         for(var i = 0, len = cs.length; i < len; i++) {
17116                 cs[i].collapse(deep);
17117         }
17118     },
17119
17120     /**
17121      * Disables this node
17122      */
17123     disable : function(){
17124         this.disabled = true;
17125         this.unselect();
17126         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17127             this.ui.onDisableChange(this, true);
17128         }
17129         this.fireEvent("disabledchange", this, true);
17130     },
17131
17132     /**
17133      * Enables this node
17134      */
17135     enable : function(){
17136         this.disabled = false;
17137         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17138             this.ui.onDisableChange(this, false);
17139         }
17140         this.fireEvent("disabledchange", this, false);
17141     },
17142
17143     // private
17144     renderChildren : function(suppressEvent){
17145         if(suppressEvent !== false){
17146             this.fireEvent("beforechildrenrendered", this);
17147         }
17148         var cs = this.childNodes;
17149         for(var i = 0, len = cs.length; i < len; i++){
17150             cs[i].render(true);
17151         }
17152         this.childrenRendered = true;
17153     },
17154
17155     // private
17156     sort : function(fn, scope){
17157         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17158         if(this.childrenRendered){
17159             var cs = this.childNodes;
17160             for(var i = 0, len = cs.length; i < len; i++){
17161                 cs[i].render(true);
17162             }
17163         }
17164     },
17165
17166     // private
17167     render : function(bulkRender){
17168         this.ui.render(bulkRender);
17169         if(!this.rendered){
17170             this.rendered = true;
17171             if(this.expanded){
17172                 this.expanded = false;
17173                 this.expand(false, false);
17174             }
17175         }
17176     },
17177
17178     // private
17179     renderIndent : function(deep, refresh){
17180         if(refresh){
17181             this.ui.childIndent = null;
17182         }
17183         this.ui.renderIndent();
17184         if(deep === true && this.childrenRendered){
17185             var cs = this.childNodes;
17186             for(var i = 0, len = cs.length; i < len; i++){
17187                 cs[i].renderIndent(true, refresh);
17188             }
17189         }
17190     }
17191 });/*
17192  * Based on:
17193  * Ext JS Library 1.1.1
17194  * Copyright(c) 2006-2007, Ext JS, LLC.
17195  *
17196  * Originally Released Under LGPL - original licence link has changed is not relivant.
17197  *
17198  * Fork - LGPL
17199  * <script type="text/javascript">
17200  */
17201  
17202 /**
17203  * @class Roo.tree.AsyncTreeNode
17204  * @extends Roo.tree.TreeNode
17205  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17206  * @constructor
17207  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17208  */
17209  Roo.tree.AsyncTreeNode = function(config){
17210     this.loaded = false;
17211     this.loading = false;
17212     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17213     /**
17214     * @event beforeload
17215     * Fires before this node is loaded, return false to cancel
17216     * @param {Node} this This node
17217     */
17218     this.addEvents({'beforeload':true, 'load': true});
17219     /**
17220     * @event load
17221     * Fires when this node is loaded
17222     * @param {Node} this This node
17223     */
17224     /**
17225      * The loader used by this node (defaults to using the tree's defined loader)
17226      * @type TreeLoader
17227      * @property loader
17228      */
17229 };
17230 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17231     expand : function(deep, anim, callback){
17232         if(this.loading){ // if an async load is already running, waiting til it's done
17233             var timer;
17234             var f = function(){
17235                 if(!this.loading){ // done loading
17236                     clearInterval(timer);
17237                     this.expand(deep, anim, callback);
17238                 }
17239             }.createDelegate(this);
17240             timer = setInterval(f, 200);
17241             return;
17242         }
17243         if(!this.loaded){
17244             if(this.fireEvent("beforeload", this) === false){
17245                 return;
17246             }
17247             this.loading = true;
17248             this.ui.beforeLoad(this);
17249             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17250             if(loader){
17251                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17252                 return;
17253             }
17254         }
17255         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17256     },
17257     
17258     /**
17259      * Returns true if this node is currently loading
17260      * @return {Boolean}
17261      */
17262     isLoading : function(){
17263         return this.loading;  
17264     },
17265     
17266     loadComplete : function(deep, anim, callback){
17267         this.loading = false;
17268         this.loaded = true;
17269         this.ui.afterLoad(this);
17270         this.fireEvent("load", this);
17271         this.expand(deep, anim, callback);
17272     },
17273     
17274     /**
17275      * Returns true if this node has been loaded
17276      * @return {Boolean}
17277      */
17278     isLoaded : function(){
17279         return this.loaded;
17280     },
17281     
17282     hasChildNodes : function(){
17283         if(!this.isLeaf() && !this.loaded){
17284             return true;
17285         }else{
17286             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17287         }
17288     },
17289
17290     /**
17291      * Trigger a reload for this node
17292      * @param {Function} callback
17293      */
17294     reload : function(callback){
17295         this.collapse(false, false);
17296         while(this.firstChild){
17297             this.removeChild(this.firstChild);
17298         }
17299         this.childrenRendered = false;
17300         this.loaded = false;
17301         if(this.isHiddenRoot()){
17302             this.expanded = false;
17303         }
17304         this.expand(false, false, callback);
17305     }
17306 });/*
17307  * Based on:
17308  * Ext JS Library 1.1.1
17309  * Copyright(c) 2006-2007, Ext JS, LLC.
17310  *
17311  * Originally Released Under LGPL - original licence link has changed is not relivant.
17312  *
17313  * Fork - LGPL
17314  * <script type="text/javascript">
17315  */
17316  
17317 /**
17318  * @class Roo.tree.TreeNodeUI
17319  * @constructor
17320  * @param {Object} node The node to render
17321  * The TreeNode UI implementation is separate from the
17322  * tree implementation. Unless you are customizing the tree UI,
17323  * you should never have to use this directly.
17324  */
17325 Roo.tree.TreeNodeUI = function(node){
17326     this.node = node;
17327     this.rendered = false;
17328     this.animating = false;
17329     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17330 };
17331
17332 Roo.tree.TreeNodeUI.prototype = {
17333     removeChild : function(node){
17334         if(this.rendered){
17335             this.ctNode.removeChild(node.ui.getEl());
17336         }
17337     },
17338
17339     beforeLoad : function(){
17340          this.addClass("x-tree-node-loading");
17341     },
17342
17343     afterLoad : function(){
17344          this.removeClass("x-tree-node-loading");
17345     },
17346
17347     onTextChange : function(node, text, oldText){
17348         if(this.rendered){
17349             this.textNode.innerHTML = text;
17350         }
17351     },
17352
17353     onDisableChange : function(node, state){
17354         this.disabled = state;
17355         if(state){
17356             this.addClass("x-tree-node-disabled");
17357         }else{
17358             this.removeClass("x-tree-node-disabled");
17359         }
17360     },
17361
17362     onSelectedChange : function(state){
17363         if(state){
17364             this.focus();
17365             this.addClass("x-tree-selected");
17366         }else{
17367             //this.blur();
17368             this.removeClass("x-tree-selected");
17369         }
17370     },
17371
17372     onMove : function(tree, node, oldParent, newParent, index, refNode){
17373         this.childIndent = null;
17374         if(this.rendered){
17375             var targetNode = newParent.ui.getContainer();
17376             if(!targetNode){//target not rendered
17377                 this.holder = document.createElement("div");
17378                 this.holder.appendChild(this.wrap);
17379                 return;
17380             }
17381             var insertBefore = refNode ? refNode.ui.getEl() : null;
17382             if(insertBefore){
17383                 targetNode.insertBefore(this.wrap, insertBefore);
17384             }else{
17385                 targetNode.appendChild(this.wrap);
17386             }
17387             this.node.renderIndent(true);
17388         }
17389     },
17390
17391     addClass : function(cls){
17392         if(this.elNode){
17393             Roo.fly(this.elNode).addClass(cls);
17394         }
17395     },
17396
17397     removeClass : function(cls){
17398         if(this.elNode){
17399             Roo.fly(this.elNode).removeClass(cls);
17400         }
17401     },
17402
17403     remove : function(){
17404         if(this.rendered){
17405             this.holder = document.createElement("div");
17406             this.holder.appendChild(this.wrap);
17407         }
17408     },
17409
17410     fireEvent : function(){
17411         return this.node.fireEvent.apply(this.node, arguments);
17412     },
17413
17414     initEvents : function(){
17415         this.node.on("move", this.onMove, this);
17416         var E = Roo.EventManager;
17417         var a = this.anchor;
17418
17419         var el = Roo.fly(a, '_treeui');
17420
17421         if(Roo.isOpera){ // opera render bug ignores the CSS
17422             el.setStyle("text-decoration", "none");
17423         }
17424
17425         el.on("click", this.onClick, this);
17426         el.on("dblclick", this.onDblClick, this);
17427
17428         if(this.checkbox){
17429             Roo.EventManager.on(this.checkbox,
17430                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17431         }
17432
17433         el.on("contextmenu", this.onContextMenu, this);
17434
17435         var icon = Roo.fly(this.iconNode);
17436         icon.on("click", this.onClick, this);
17437         icon.on("dblclick", this.onDblClick, this);
17438         icon.on("contextmenu", this.onContextMenu, this);
17439         E.on(this.ecNode, "click", this.ecClick, this, true);
17440
17441         if(this.node.disabled){
17442             this.addClass("x-tree-node-disabled");
17443         }
17444         if(this.node.hidden){
17445             this.addClass("x-tree-node-disabled");
17446         }
17447         var ot = this.node.getOwnerTree();
17448         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17449         if(dd && (!this.node.isRoot || ot.rootVisible)){
17450             Roo.dd.Registry.register(this.elNode, {
17451                 node: this.node,
17452                 handles: this.getDDHandles(),
17453                 isHandle: false
17454             });
17455         }
17456     },
17457
17458     getDDHandles : function(){
17459         return [this.iconNode, this.textNode];
17460     },
17461
17462     hide : function(){
17463         if(this.rendered){
17464             this.wrap.style.display = "none";
17465         }
17466     },
17467
17468     show : function(){
17469         if(this.rendered){
17470             this.wrap.style.display = "";
17471         }
17472     },
17473
17474     onContextMenu : function(e){
17475         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17476             e.preventDefault();
17477             this.focus();
17478             this.fireEvent("contextmenu", this.node, e);
17479         }
17480     },
17481
17482     onClick : function(e){
17483         if(this.dropping){
17484             e.stopEvent();
17485             return;
17486         }
17487         if(this.fireEvent("beforeclick", this.node, e) !== false){
17488             if(!this.disabled && this.node.attributes.href){
17489                 this.fireEvent("click", this.node, e);
17490                 return;
17491             }
17492             e.preventDefault();
17493             if(this.disabled){
17494                 return;
17495             }
17496
17497             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17498                 this.node.toggle();
17499             }
17500
17501             this.fireEvent("click", this.node, e);
17502         }else{
17503             e.stopEvent();
17504         }
17505     },
17506
17507     onDblClick : function(e){
17508         e.preventDefault();
17509         if(this.disabled){
17510             return;
17511         }
17512         if(this.checkbox){
17513             this.toggleCheck();
17514         }
17515         if(!this.animating && this.node.hasChildNodes()){
17516             this.node.toggle();
17517         }
17518         this.fireEvent("dblclick", this.node, e);
17519     },
17520
17521     onCheckChange : function(){
17522         var checked = this.checkbox.checked;
17523         this.node.attributes.checked = checked;
17524         this.fireEvent('checkchange', this.node, checked);
17525     },
17526
17527     ecClick : function(e){
17528         if(!this.animating && this.node.hasChildNodes()){
17529             this.node.toggle();
17530         }
17531     },
17532
17533     startDrop : function(){
17534         this.dropping = true;
17535     },
17536
17537     // delayed drop so the click event doesn't get fired on a drop
17538     endDrop : function(){
17539        setTimeout(function(){
17540            this.dropping = false;
17541        }.createDelegate(this), 50);
17542     },
17543
17544     expand : function(){
17545         this.updateExpandIcon();
17546         this.ctNode.style.display = "";
17547     },
17548
17549     focus : function(){
17550         if(!this.node.preventHScroll){
17551             try{this.anchor.focus();
17552             }catch(e){}
17553         }else if(!Roo.isIE){
17554             try{
17555                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17556                 var l = noscroll.scrollLeft;
17557                 this.anchor.focus();
17558                 noscroll.scrollLeft = l;
17559             }catch(e){}
17560         }
17561     },
17562
17563     toggleCheck : function(value){
17564         var cb = this.checkbox;
17565         if(cb){
17566             cb.checked = (value === undefined ? !cb.checked : value);
17567         }
17568     },
17569
17570     blur : function(){
17571         try{
17572             this.anchor.blur();
17573         }catch(e){}
17574     },
17575
17576     animExpand : function(callback){
17577         var ct = Roo.get(this.ctNode);
17578         ct.stopFx();
17579         if(!this.node.hasChildNodes()){
17580             this.updateExpandIcon();
17581             this.ctNode.style.display = "";
17582             Roo.callback(callback);
17583             return;
17584         }
17585         this.animating = true;
17586         this.updateExpandIcon();
17587
17588         ct.slideIn('t', {
17589            callback : function(){
17590                this.animating = false;
17591                Roo.callback(callback);
17592             },
17593             scope: this,
17594             duration: this.node.ownerTree.duration || .25
17595         });
17596     },
17597
17598     highlight : function(){
17599         var tree = this.node.getOwnerTree();
17600         Roo.fly(this.wrap).highlight(
17601             tree.hlColor || "C3DAF9",
17602             {endColor: tree.hlBaseColor}
17603         );
17604     },
17605
17606     collapse : function(){
17607         this.updateExpandIcon();
17608         this.ctNode.style.display = "none";
17609     },
17610
17611     animCollapse : function(callback){
17612         var ct = Roo.get(this.ctNode);
17613         ct.enableDisplayMode('block');
17614         ct.stopFx();
17615
17616         this.animating = true;
17617         this.updateExpandIcon();
17618
17619         ct.slideOut('t', {
17620             callback : function(){
17621                this.animating = false;
17622                Roo.callback(callback);
17623             },
17624             scope: this,
17625             duration: this.node.ownerTree.duration || .25
17626         });
17627     },
17628
17629     getContainer : function(){
17630         return this.ctNode;
17631     },
17632
17633     getEl : function(){
17634         return this.wrap;
17635     },
17636
17637     appendDDGhost : function(ghostNode){
17638         ghostNode.appendChild(this.elNode.cloneNode(true));
17639     },
17640
17641     getDDRepairXY : function(){
17642         return Roo.lib.Dom.getXY(this.iconNode);
17643     },
17644
17645     onRender : function(){
17646         this.render();
17647     },
17648
17649     render : function(bulkRender){
17650         var n = this.node, a = n.attributes;
17651         var targetNode = n.parentNode ?
17652               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17653
17654         if(!this.rendered){
17655             this.rendered = true;
17656
17657             this.renderElements(n, a, targetNode, bulkRender);
17658
17659             if(a.qtip){
17660                if(this.textNode.setAttributeNS){
17661                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17662                    if(a.qtipTitle){
17663                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17664                    }
17665                }else{
17666                    this.textNode.setAttribute("ext:qtip", a.qtip);
17667                    if(a.qtipTitle){
17668                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17669                    }
17670                }
17671             }else if(a.qtipCfg){
17672                 a.qtipCfg.target = Roo.id(this.textNode);
17673                 Roo.QuickTips.register(a.qtipCfg);
17674             }
17675             this.initEvents();
17676             if(!this.node.expanded){
17677                 this.updateExpandIcon();
17678             }
17679         }else{
17680             if(bulkRender === true) {
17681                 targetNode.appendChild(this.wrap);
17682             }
17683         }
17684     },
17685
17686     renderElements : function(n, a, targetNode, bulkRender)
17687     {
17688         // add some indent caching, this helps performance when rendering a large tree
17689         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17690         var t = n.getOwnerTree();
17691         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17692         if (typeof(n.attributes.html) != 'undefined') {
17693             txt = n.attributes.html;
17694         }
17695         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17696         var cb = typeof a.checked == 'boolean';
17697         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17698         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17699             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17700             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17701             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17702             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17703             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17704              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17705                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17706             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17707             "</li>"];
17708
17709         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17710             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17711                                 n.nextSibling.ui.getEl(), buf.join(""));
17712         }else{
17713             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17714         }
17715
17716         this.elNode = this.wrap.childNodes[0];
17717         this.ctNode = this.wrap.childNodes[1];
17718         var cs = this.elNode.childNodes;
17719         this.indentNode = cs[0];
17720         this.ecNode = cs[1];
17721         this.iconNode = cs[2];
17722         var index = 3;
17723         if(cb){
17724             this.checkbox = cs[3];
17725             index++;
17726         }
17727         this.anchor = cs[index];
17728         this.textNode = cs[index].firstChild;
17729     },
17730
17731     getAnchor : function(){
17732         return this.anchor;
17733     },
17734
17735     getTextEl : function(){
17736         return this.textNode;
17737     },
17738
17739     getIconEl : function(){
17740         return this.iconNode;
17741     },
17742
17743     isChecked : function(){
17744         return this.checkbox ? this.checkbox.checked : false;
17745     },
17746
17747     updateExpandIcon : function(){
17748         if(this.rendered){
17749             var n = this.node, c1, c2;
17750             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17751             var hasChild = n.hasChildNodes();
17752             if(hasChild){
17753                 if(n.expanded){
17754                     cls += "-minus";
17755                     c1 = "x-tree-node-collapsed";
17756                     c2 = "x-tree-node-expanded";
17757                 }else{
17758                     cls += "-plus";
17759                     c1 = "x-tree-node-expanded";
17760                     c2 = "x-tree-node-collapsed";
17761                 }
17762                 if(this.wasLeaf){
17763                     this.removeClass("x-tree-node-leaf");
17764                     this.wasLeaf = false;
17765                 }
17766                 if(this.c1 != c1 || this.c2 != c2){
17767                     Roo.fly(this.elNode).replaceClass(c1, c2);
17768                     this.c1 = c1; this.c2 = c2;
17769                 }
17770             }else{
17771                 // this changes non-leafs into leafs if they have no children.
17772                 // it's not very rational behaviour..
17773                 
17774                 if(!this.wasLeaf && this.node.leaf){
17775                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17776                     delete this.c1;
17777                     delete this.c2;
17778                     this.wasLeaf = true;
17779                 }
17780             }
17781             var ecc = "x-tree-ec-icon "+cls;
17782             if(this.ecc != ecc){
17783                 this.ecNode.className = ecc;
17784                 this.ecc = ecc;
17785             }
17786         }
17787     },
17788
17789     getChildIndent : function(){
17790         if(!this.childIndent){
17791             var buf = [];
17792             var p = this.node;
17793             while(p){
17794                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17795                     if(!p.isLast()) {
17796                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17797                     } else {
17798                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17799                     }
17800                 }
17801                 p = p.parentNode;
17802             }
17803             this.childIndent = buf.join("");
17804         }
17805         return this.childIndent;
17806     },
17807
17808     renderIndent : function(){
17809         if(this.rendered){
17810             var indent = "";
17811             var p = this.node.parentNode;
17812             if(p){
17813                 indent = p.ui.getChildIndent();
17814             }
17815             if(this.indentMarkup != indent){ // don't rerender if not required
17816                 this.indentNode.innerHTML = indent;
17817                 this.indentMarkup = indent;
17818             }
17819             this.updateExpandIcon();
17820         }
17821     }
17822 };
17823
17824 Roo.tree.RootTreeNodeUI = function(){
17825     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17826 };
17827 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17828     render : function(){
17829         if(!this.rendered){
17830             var targetNode = this.node.ownerTree.innerCt.dom;
17831             this.node.expanded = true;
17832             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17833             this.wrap = this.ctNode = targetNode.firstChild;
17834         }
17835     },
17836     collapse : function(){
17837     },
17838     expand : function(){
17839     }
17840 });/*
17841  * Based on:
17842  * Ext JS Library 1.1.1
17843  * Copyright(c) 2006-2007, Ext JS, LLC.
17844  *
17845  * Originally Released Under LGPL - original licence link has changed is not relivant.
17846  *
17847  * Fork - LGPL
17848  * <script type="text/javascript">
17849  */
17850 /**
17851  * @class Roo.tree.TreeLoader
17852  * @extends Roo.util.Observable
17853  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17854  * nodes from a specified URL. The response must be a javascript Array definition
17855  * who's elements are node definition objects. eg:
17856  * <pre><code>
17857 {  success : true,
17858    data :      [
17859    
17860     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17861     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17862     ]
17863 }
17864
17865
17866 </code></pre>
17867  * <br><br>
17868  * The old style respose with just an array is still supported, but not recommended.
17869  * <br><br>
17870  *
17871  * A server request is sent, and child nodes are loaded only when a node is expanded.
17872  * The loading node's id is passed to the server under the parameter name "node" to
17873  * enable the server to produce the correct child nodes.
17874  * <br><br>
17875  * To pass extra parameters, an event handler may be attached to the "beforeload"
17876  * event, and the parameters specified in the TreeLoader's baseParams property:
17877  * <pre><code>
17878     myTreeLoader.on("beforeload", function(treeLoader, node) {
17879         this.baseParams.category = node.attributes.category;
17880     }, this);
17881 </code></pre><
17882  * This would pass an HTTP parameter called "category" to the server containing
17883  * the value of the Node's "category" attribute.
17884  * @constructor
17885  * Creates a new Treeloader.
17886  * @param {Object} config A config object containing config properties.
17887  */
17888 Roo.tree.TreeLoader = function(config){
17889     this.baseParams = {};
17890     this.requestMethod = "POST";
17891     Roo.apply(this, config);
17892
17893     this.addEvents({
17894     
17895         /**
17896          * @event beforeload
17897          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17898          * @param {Object} This TreeLoader object.
17899          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17900          * @param {Object} callback The callback function specified in the {@link #load} call.
17901          */
17902         beforeload : true,
17903         /**
17904          * @event load
17905          * Fires when the node has been successfuly loaded.
17906          * @param {Object} This TreeLoader object.
17907          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17908          * @param {Object} response The response object containing the data from the server.
17909          */
17910         load : true,
17911         /**
17912          * @event loadexception
17913          * Fires if the network request failed.
17914          * @param {Object} This TreeLoader object.
17915          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17916          * @param {Object} response The response object containing the data from the server.
17917          */
17918         loadexception : true,
17919         /**
17920          * @event create
17921          * Fires before a node is created, enabling you to return custom Node types 
17922          * @param {Object} This TreeLoader object.
17923          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17924          */
17925         create : true
17926     });
17927
17928     Roo.tree.TreeLoader.superclass.constructor.call(this);
17929 };
17930
17931 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17932     /**
17933     * @cfg {String} dataUrl The URL from which to request a Json string which
17934     * specifies an array of node definition object representing the child nodes
17935     * to be loaded.
17936     */
17937     /**
17938     * @cfg {String} requestMethod either GET or POST
17939     * defaults to POST (due to BC)
17940     * to be loaded.
17941     */
17942     /**
17943     * @cfg {Object} baseParams (optional) An object containing properties which
17944     * specify HTTP parameters to be passed to each request for child nodes.
17945     */
17946     /**
17947     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17948     * created by this loader. If the attributes sent by the server have an attribute in this object,
17949     * they take priority.
17950     */
17951     /**
17952     * @cfg {Object} uiProviders (optional) An object containing properties which
17953     * 
17954     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17955     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17956     * <i>uiProvider</i> attribute of a returned child node is a string rather
17957     * than a reference to a TreeNodeUI implementation, this that string value
17958     * is used as a property name in the uiProviders object. You can define the provider named
17959     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17960     */
17961     uiProviders : {},
17962
17963     /**
17964     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17965     * child nodes before loading.
17966     */
17967     clearOnLoad : true,
17968
17969     /**
17970     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
17971     * property on loading, rather than expecting an array. (eg. more compatible to a standard
17972     * Grid query { data : [ .....] }
17973     */
17974     
17975     root : false,
17976      /**
17977     * @cfg {String} queryParam (optional) 
17978     * Name of the query as it will be passed on the querystring (defaults to 'node')
17979     * eg. the request will be ?node=[id]
17980     */
17981     
17982     
17983     queryParam: false,
17984     
17985     /**
17986      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
17987      * This is called automatically when a node is expanded, but may be used to reload
17988      * a node (or append new children if the {@link #clearOnLoad} option is false.)
17989      * @param {Roo.tree.TreeNode} node
17990      * @param {Function} callback
17991      */
17992     load : function(node, callback){
17993         if(this.clearOnLoad){
17994             while(node.firstChild){
17995                 node.removeChild(node.firstChild);
17996             }
17997         }
17998         if(node.attributes.children){ // preloaded json children
17999             var cs = node.attributes.children;
18000             for(var i = 0, len = cs.length; i < len; i++){
18001                 node.appendChild(this.createNode(cs[i]));
18002             }
18003             if(typeof callback == "function"){
18004                 callback();
18005             }
18006         }else if(this.dataUrl){
18007             this.requestData(node, callback);
18008         }
18009     },
18010
18011     getParams: function(node){
18012         var buf = [], bp = this.baseParams;
18013         for(var key in bp){
18014             if(typeof bp[key] != "function"){
18015                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18016             }
18017         }
18018         var n = this.queryParam === false ? 'node' : this.queryParam;
18019         buf.push(n + "=", encodeURIComponent(node.id));
18020         return buf.join("");
18021     },
18022
18023     requestData : function(node, callback){
18024         if(this.fireEvent("beforeload", this, node, callback) !== false){
18025             this.transId = Roo.Ajax.request({
18026                 method:this.requestMethod,
18027                 url: this.dataUrl||this.url,
18028                 success: this.handleResponse,
18029                 failure: this.handleFailure,
18030                 scope: this,
18031                 argument: {callback: callback, node: node},
18032                 params: this.getParams(node)
18033             });
18034         }else{
18035             // if the load is cancelled, make sure we notify
18036             // the node that we are done
18037             if(typeof callback == "function"){
18038                 callback();
18039             }
18040         }
18041     },
18042
18043     isLoading : function(){
18044         return this.transId ? true : false;
18045     },
18046
18047     abort : function(){
18048         if(this.isLoading()){
18049             Roo.Ajax.abort(this.transId);
18050         }
18051     },
18052
18053     // private
18054     createNode : function(attr)
18055     {
18056         // apply baseAttrs, nice idea Corey!
18057         if(this.baseAttrs){
18058             Roo.applyIf(attr, this.baseAttrs);
18059         }
18060         if(this.applyLoader !== false){
18061             attr.loader = this;
18062         }
18063         // uiProvider = depreciated..
18064         
18065         if(typeof(attr.uiProvider) == 'string'){
18066            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18067                 /**  eval:var:attr */ eval(attr.uiProvider);
18068         }
18069         if(typeof(this.uiProviders['default']) != 'undefined') {
18070             attr.uiProvider = this.uiProviders['default'];
18071         }
18072         
18073         this.fireEvent('create', this, attr);
18074         
18075         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18076         return(attr.leaf ?
18077                         new Roo.tree.TreeNode(attr) :
18078                         new Roo.tree.AsyncTreeNode(attr));
18079     },
18080
18081     processResponse : function(response, node, callback)
18082     {
18083         var json = response.responseText;
18084         try {
18085             
18086             var o = Roo.decode(json);
18087             
18088             if (this.root === false && typeof(o.success) != undefined) {
18089                 this.root = 'data'; // the default behaviour for list like data..
18090                 }
18091                 
18092             if (this.root !== false &&  !o.success) {
18093                 // it's a failure condition.
18094                 var a = response.argument;
18095                 this.fireEvent("loadexception", this, a.node, response);
18096                 Roo.log("Load failed - should have a handler really");
18097                 return;
18098             }
18099             
18100             
18101             
18102             if (this.root !== false) {
18103                  o = o[this.root];
18104             }
18105             
18106             for(var i = 0, len = o.length; i < len; i++){
18107                 var n = this.createNode(o[i]);
18108                 if(n){
18109                     node.appendChild(n);
18110                 }
18111             }
18112             if(typeof callback == "function"){
18113                 callback(this, node);
18114             }
18115         }catch(e){
18116             this.handleFailure(response);
18117         }
18118     },
18119
18120     handleResponse : function(response){
18121         this.transId = false;
18122         var a = response.argument;
18123         this.processResponse(response, a.node, a.callback);
18124         this.fireEvent("load", this, a.node, response);
18125     },
18126
18127     handleFailure : function(response)
18128     {
18129         // should handle failure better..
18130         this.transId = false;
18131         var a = response.argument;
18132         this.fireEvent("loadexception", this, a.node, response);
18133         if(typeof a.callback == "function"){
18134             a.callback(this, a.node);
18135         }
18136     }
18137 });/*
18138  * Based on:
18139  * Ext JS Library 1.1.1
18140  * Copyright(c) 2006-2007, Ext JS, LLC.
18141  *
18142  * Originally Released Under LGPL - original licence link has changed is not relivant.
18143  *
18144  * Fork - LGPL
18145  * <script type="text/javascript">
18146  */
18147
18148 /**
18149 * @class Roo.tree.TreeFilter
18150 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18151 * @param {TreePanel} tree
18152 * @param {Object} config (optional)
18153  */
18154 Roo.tree.TreeFilter = function(tree, config){
18155     this.tree = tree;
18156     this.filtered = {};
18157     Roo.apply(this, config);
18158 };
18159
18160 Roo.tree.TreeFilter.prototype = {
18161     clearBlank:false,
18162     reverse:false,
18163     autoClear:false,
18164     remove:false,
18165
18166      /**
18167      * Filter the data by a specific attribute.
18168      * @param {String/RegExp} value Either string that the attribute value
18169      * should start with or a RegExp to test against the attribute
18170      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18171      * @param {TreeNode} startNode (optional) The node to start the filter at.
18172      */
18173     filter : function(value, attr, startNode){
18174         attr = attr || "text";
18175         var f;
18176         if(typeof value == "string"){
18177             var vlen = value.length;
18178             // auto clear empty filter
18179             if(vlen == 0 && this.clearBlank){
18180                 this.clear();
18181                 return;
18182             }
18183             value = value.toLowerCase();
18184             f = function(n){
18185                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18186             };
18187         }else if(value.exec){ // regex?
18188             f = function(n){
18189                 return value.test(n.attributes[attr]);
18190             };
18191         }else{
18192             throw 'Illegal filter type, must be string or regex';
18193         }
18194         this.filterBy(f, null, startNode);
18195         },
18196
18197     /**
18198      * Filter by a function. The passed function will be called with each
18199      * node in the tree (or from the startNode). If the function returns true, the node is kept
18200      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18201      * @param {Function} fn The filter function
18202      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18203      */
18204     filterBy : function(fn, scope, startNode){
18205         startNode = startNode || this.tree.root;
18206         if(this.autoClear){
18207             this.clear();
18208         }
18209         var af = this.filtered, rv = this.reverse;
18210         var f = function(n){
18211             if(n == startNode){
18212                 return true;
18213             }
18214             if(af[n.id]){
18215                 return false;
18216             }
18217             var m = fn.call(scope || n, n);
18218             if(!m || rv){
18219                 af[n.id] = n;
18220                 n.ui.hide();
18221                 return false;
18222             }
18223             return true;
18224         };
18225         startNode.cascade(f);
18226         if(this.remove){
18227            for(var id in af){
18228                if(typeof id != "function"){
18229                    var n = af[id];
18230                    if(n && n.parentNode){
18231                        n.parentNode.removeChild(n);
18232                    }
18233                }
18234            }
18235         }
18236     },
18237
18238     /**
18239      * Clears the current filter. Note: with the "remove" option
18240      * set a filter cannot be cleared.
18241      */
18242     clear : function(){
18243         var t = this.tree;
18244         var af = this.filtered;
18245         for(var id in af){
18246             if(typeof id != "function"){
18247                 var n = af[id];
18248                 if(n){
18249                     n.ui.show();
18250                 }
18251             }
18252         }
18253         this.filtered = {};
18254     }
18255 };
18256 /*
18257  * Based on:
18258  * Ext JS Library 1.1.1
18259  * Copyright(c) 2006-2007, Ext JS, LLC.
18260  *
18261  * Originally Released Under LGPL - original licence link has changed is not relivant.
18262  *
18263  * Fork - LGPL
18264  * <script type="text/javascript">
18265  */
18266  
18267
18268 /**
18269  * @class Roo.tree.TreeSorter
18270  * Provides sorting of nodes in a TreePanel
18271  * 
18272  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18273  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18274  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18275  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18276  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18277  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18278  * @constructor
18279  * @param {TreePanel} tree
18280  * @param {Object} config
18281  */
18282 Roo.tree.TreeSorter = function(tree, config){
18283     Roo.apply(this, config);
18284     tree.on("beforechildrenrendered", this.doSort, this);
18285     tree.on("append", this.updateSort, this);
18286     tree.on("insert", this.updateSort, this);
18287     
18288     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18289     var p = this.property || "text";
18290     var sortType = this.sortType;
18291     var fs = this.folderSort;
18292     var cs = this.caseSensitive === true;
18293     var leafAttr = this.leafAttr || 'leaf';
18294
18295     this.sortFn = function(n1, n2){
18296         if(fs){
18297             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18298                 return 1;
18299             }
18300             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18301                 return -1;
18302             }
18303         }
18304         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18305         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18306         if(v1 < v2){
18307                         return dsc ? +1 : -1;
18308                 }else if(v1 > v2){
18309                         return dsc ? -1 : +1;
18310         }else{
18311                 return 0;
18312         }
18313     };
18314 };
18315
18316 Roo.tree.TreeSorter.prototype = {
18317     doSort : function(node){
18318         node.sort(this.sortFn);
18319     },
18320     
18321     compareNodes : function(n1, n2){
18322         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18323     },
18324     
18325     updateSort : function(tree, node){
18326         if(node.childrenRendered){
18327             this.doSort.defer(1, this, [node]);
18328         }
18329     }
18330 };/*
18331  * Based on:
18332  * Ext JS Library 1.1.1
18333  * Copyright(c) 2006-2007, Ext JS, LLC.
18334  *
18335  * Originally Released Under LGPL - original licence link has changed is not relivant.
18336  *
18337  * Fork - LGPL
18338  * <script type="text/javascript">
18339  */
18340
18341 if(Roo.dd.DropZone){
18342     
18343 Roo.tree.TreeDropZone = function(tree, config){
18344     this.allowParentInsert = false;
18345     this.allowContainerDrop = false;
18346     this.appendOnly = false;
18347     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18348     this.tree = tree;
18349     this.lastInsertClass = "x-tree-no-status";
18350     this.dragOverData = {};
18351 };
18352
18353 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18354     ddGroup : "TreeDD",
18355     scroll:  true,
18356     
18357     expandDelay : 1000,
18358     
18359     expandNode : function(node){
18360         if(node.hasChildNodes() && !node.isExpanded()){
18361             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18362         }
18363     },
18364     
18365     queueExpand : function(node){
18366         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18367     },
18368     
18369     cancelExpand : function(){
18370         if(this.expandProcId){
18371             clearTimeout(this.expandProcId);
18372             this.expandProcId = false;
18373         }
18374     },
18375     
18376     isValidDropPoint : function(n, pt, dd, e, data){
18377         if(!n || !data){ return false; }
18378         var targetNode = n.node;
18379         var dropNode = data.node;
18380         // default drop rules
18381         if(!(targetNode && targetNode.isTarget && pt)){
18382             return false;
18383         }
18384         if(pt == "append" && targetNode.allowChildren === false){
18385             return false;
18386         }
18387         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18388             return false;
18389         }
18390         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18391             return false;
18392         }
18393         // reuse the object
18394         var overEvent = this.dragOverData;
18395         overEvent.tree = this.tree;
18396         overEvent.target = targetNode;
18397         overEvent.data = data;
18398         overEvent.point = pt;
18399         overEvent.source = dd;
18400         overEvent.rawEvent = e;
18401         overEvent.dropNode = dropNode;
18402         overEvent.cancel = false;  
18403         var result = this.tree.fireEvent("nodedragover", overEvent);
18404         return overEvent.cancel === false && result !== false;
18405     },
18406     
18407     getDropPoint : function(e, n, dd)
18408     {
18409         var tn = n.node;
18410         if(tn.isRoot){
18411             return tn.allowChildren !== false ? "append" : false; // always append for root
18412         }
18413         var dragEl = n.ddel;
18414         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18415         var y = Roo.lib.Event.getPageY(e);
18416         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18417         
18418         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18419         var noAppend = tn.allowChildren === false;
18420         if(this.appendOnly || tn.parentNode.allowChildren === false){
18421             return noAppend ? false : "append";
18422         }
18423         var noBelow = false;
18424         if(!this.allowParentInsert){
18425             noBelow = tn.hasChildNodes() && tn.isExpanded();
18426         }
18427         var q = (b - t) / (noAppend ? 2 : 3);
18428         if(y >= t && y < (t + q)){
18429             return "above";
18430         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18431             return "below";
18432         }else{
18433             return "append";
18434         }
18435     },
18436     
18437     onNodeEnter : function(n, dd, e, data)
18438     {
18439         this.cancelExpand();
18440     },
18441     
18442     onNodeOver : function(n, dd, e, data)
18443     {
18444        
18445         var pt = this.getDropPoint(e, n, dd);
18446         var node = n.node;
18447         
18448         // auto node expand check
18449         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18450             this.queueExpand(node);
18451         }else if(pt != "append"){
18452             this.cancelExpand();
18453         }
18454         
18455         // set the insert point style on the target node
18456         var returnCls = this.dropNotAllowed;
18457         if(this.isValidDropPoint(n, pt, dd, e, data)){
18458            if(pt){
18459                var el = n.ddel;
18460                var cls;
18461                if(pt == "above"){
18462                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18463                    cls = "x-tree-drag-insert-above";
18464                }else if(pt == "below"){
18465                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18466                    cls = "x-tree-drag-insert-below";
18467                }else{
18468                    returnCls = "x-tree-drop-ok-append";
18469                    cls = "x-tree-drag-append";
18470                }
18471                if(this.lastInsertClass != cls){
18472                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18473                    this.lastInsertClass = cls;
18474                }
18475            }
18476        }
18477        return returnCls;
18478     },
18479     
18480     onNodeOut : function(n, dd, e, data){
18481         
18482         this.cancelExpand();
18483         this.removeDropIndicators(n);
18484     },
18485     
18486     onNodeDrop : function(n, dd, e, data){
18487         var point = this.getDropPoint(e, n, dd);
18488         var targetNode = n.node;
18489         targetNode.ui.startDrop();
18490         if(!this.isValidDropPoint(n, point, dd, e, data)){
18491             targetNode.ui.endDrop();
18492             return false;
18493         }
18494         // first try to find the drop node
18495         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18496         var dropEvent = {
18497             tree : this.tree,
18498             target: targetNode,
18499             data: data,
18500             point: point,
18501             source: dd,
18502             rawEvent: e,
18503             dropNode: dropNode,
18504             cancel: !dropNode   
18505         };
18506         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18507         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18508             targetNode.ui.endDrop();
18509             return false;
18510         }
18511         // allow target changing
18512         targetNode = dropEvent.target;
18513         if(point == "append" && !targetNode.isExpanded()){
18514             targetNode.expand(false, null, function(){
18515                 this.completeDrop(dropEvent);
18516             }.createDelegate(this));
18517         }else{
18518             this.completeDrop(dropEvent);
18519         }
18520         return true;
18521     },
18522     
18523     completeDrop : function(de){
18524         var ns = de.dropNode, p = de.point, t = de.target;
18525         if(!(ns instanceof Array)){
18526             ns = [ns];
18527         }
18528         var n;
18529         for(var i = 0, len = ns.length; i < len; i++){
18530             n = ns[i];
18531             if(p == "above"){
18532                 t.parentNode.insertBefore(n, t);
18533             }else if(p == "below"){
18534                 t.parentNode.insertBefore(n, t.nextSibling);
18535             }else{
18536                 t.appendChild(n);
18537             }
18538         }
18539         n.ui.focus();
18540         if(this.tree.hlDrop){
18541             n.ui.highlight();
18542         }
18543         t.ui.endDrop();
18544         this.tree.fireEvent("nodedrop", de);
18545     },
18546     
18547     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18548         if(this.tree.hlDrop){
18549             dropNode.ui.focus();
18550             dropNode.ui.highlight();
18551         }
18552         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18553     },
18554     
18555     getTree : function(){
18556         return this.tree;
18557     },
18558     
18559     removeDropIndicators : function(n){
18560         if(n && n.ddel){
18561             var el = n.ddel;
18562             Roo.fly(el).removeClass([
18563                     "x-tree-drag-insert-above",
18564                     "x-tree-drag-insert-below",
18565                     "x-tree-drag-append"]);
18566             this.lastInsertClass = "_noclass";
18567         }
18568     },
18569     
18570     beforeDragDrop : function(target, e, id){
18571         this.cancelExpand();
18572         return true;
18573     },
18574     
18575     afterRepair : function(data){
18576         if(data && Roo.enableFx){
18577             data.node.ui.highlight();
18578         }
18579         this.hideProxy();
18580     } 
18581     
18582 });
18583
18584 }
18585 /*
18586  * Based on:
18587  * Ext JS Library 1.1.1
18588  * Copyright(c) 2006-2007, Ext JS, LLC.
18589  *
18590  * Originally Released Under LGPL - original licence link has changed is not relivant.
18591  *
18592  * Fork - LGPL
18593  * <script type="text/javascript">
18594  */
18595  
18596
18597 if(Roo.dd.DragZone){
18598 Roo.tree.TreeDragZone = function(tree, config){
18599     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18600     this.tree = tree;
18601 };
18602
18603 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18604     ddGroup : "TreeDD",
18605    
18606     onBeforeDrag : function(data, e){
18607         var n = data.node;
18608         return n && n.draggable && !n.disabled;
18609     },
18610      
18611     
18612     onInitDrag : function(e){
18613         var data = this.dragData;
18614         this.tree.getSelectionModel().select(data.node);
18615         this.proxy.update("");
18616         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18617         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18618     },
18619     
18620     getRepairXY : function(e, data){
18621         return data.node.ui.getDDRepairXY();
18622     },
18623     
18624     onEndDrag : function(data, e){
18625         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18626         
18627         
18628     },
18629     
18630     onValidDrop : function(dd, e, id){
18631         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18632         this.hideProxy();
18633     },
18634     
18635     beforeInvalidDrop : function(e, id){
18636         // this scrolls the original position back into view
18637         var sm = this.tree.getSelectionModel();
18638         sm.clearSelections();
18639         sm.select(this.dragData.node);
18640     }
18641 });
18642 }/*
18643  * Based on:
18644  * Ext JS Library 1.1.1
18645  * Copyright(c) 2006-2007, Ext JS, LLC.
18646  *
18647  * Originally Released Under LGPL - original licence link has changed is not relivant.
18648  *
18649  * Fork - LGPL
18650  * <script type="text/javascript">
18651  */
18652 /**
18653  * @class Roo.tree.TreeEditor
18654  * @extends Roo.Editor
18655  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18656  * as the editor field.
18657  * @constructor
18658  * @param {Object} config (used to be the tree panel.)
18659  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18660  * 
18661  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18662  * @cfg {Roo.form.TextField|Object} field The field configuration
18663  *
18664  * 
18665  */
18666 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18667     var tree = config;
18668     var field;
18669     if (oldconfig) { // old style..
18670         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18671     } else {
18672         // new style..
18673         tree = config.tree;
18674         config.field = config.field  || {};
18675         config.field.xtype = 'TextField';
18676         field = Roo.factory(config.field, Roo.form);
18677     }
18678     config = config || {};
18679     
18680     
18681     this.addEvents({
18682         /**
18683          * @event beforenodeedit
18684          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18685          * false from the handler of this event.
18686          * @param {Editor} this
18687          * @param {Roo.tree.Node} node 
18688          */
18689         "beforenodeedit" : true
18690     });
18691     
18692     //Roo.log(config);
18693     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18694
18695     this.tree = tree;
18696
18697     tree.on('beforeclick', this.beforeNodeClick, this);
18698     tree.getTreeEl().on('mousedown', this.hide, this);
18699     this.on('complete', this.updateNode, this);
18700     this.on('beforestartedit', this.fitToTree, this);
18701     this.on('startedit', this.bindScroll, this, {delay:10});
18702     this.on('specialkey', this.onSpecialKey, this);
18703 };
18704
18705 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18706     /**
18707      * @cfg {String} alignment
18708      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18709      */
18710     alignment: "l-l",
18711     // inherit
18712     autoSize: false,
18713     /**
18714      * @cfg {Boolean} hideEl
18715      * True to hide the bound element while the editor is displayed (defaults to false)
18716      */
18717     hideEl : false,
18718     /**
18719      * @cfg {String} cls
18720      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18721      */
18722     cls: "x-small-editor x-tree-editor",
18723     /**
18724      * @cfg {Boolean} shim
18725      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18726      */
18727     shim:false,
18728     // inherit
18729     shadow:"frame",
18730     /**
18731      * @cfg {Number} maxWidth
18732      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18733      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18734      * scroll and client offsets into account prior to each edit.
18735      */
18736     maxWidth: 250,
18737
18738     editDelay : 350,
18739
18740     // private
18741     fitToTree : function(ed, el){
18742         var td = this.tree.getTreeEl().dom, nd = el.dom;
18743         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18744             td.scrollLeft = nd.offsetLeft;
18745         }
18746         var w = Math.min(
18747                 this.maxWidth,
18748                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18749         this.setSize(w, '');
18750         
18751         return this.fireEvent('beforenodeedit', this, this.editNode);
18752         
18753     },
18754
18755     // private
18756     triggerEdit : function(node){
18757         this.completeEdit();
18758         this.editNode = node;
18759         this.startEdit(node.ui.textNode, node.text);
18760     },
18761
18762     // private
18763     bindScroll : function(){
18764         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18765     },
18766
18767     // private
18768     beforeNodeClick : function(node, e){
18769         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18770         this.lastClick = new Date();
18771         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18772             e.stopEvent();
18773             this.triggerEdit(node);
18774             return false;
18775         }
18776         return true;
18777     },
18778
18779     // private
18780     updateNode : function(ed, value){
18781         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18782         this.editNode.setText(value);
18783     },
18784
18785     // private
18786     onHide : function(){
18787         Roo.tree.TreeEditor.superclass.onHide.call(this);
18788         if(this.editNode){
18789             this.editNode.ui.focus();
18790         }
18791     },
18792
18793     // private
18794     onSpecialKey : function(field, e){
18795         var k = e.getKey();
18796         if(k == e.ESC){
18797             e.stopEvent();
18798             this.cancelEdit();
18799         }else if(k == e.ENTER && !e.hasModifier()){
18800             e.stopEvent();
18801             this.completeEdit();
18802         }
18803     }
18804 });//<Script type="text/javascript">
18805 /*
18806  * Based on:
18807  * Ext JS Library 1.1.1
18808  * Copyright(c) 2006-2007, Ext JS, LLC.
18809  *
18810  * Originally Released Under LGPL - original licence link has changed is not relivant.
18811  *
18812  * Fork - LGPL
18813  * <script type="text/javascript">
18814  */
18815  
18816 /**
18817  * Not documented??? - probably should be...
18818  */
18819
18820 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18821     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18822     
18823     renderElements : function(n, a, targetNode, bulkRender){
18824         //consel.log("renderElements?");
18825         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18826
18827         var t = n.getOwnerTree();
18828         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18829         
18830         var cols = t.columns;
18831         var bw = t.borderWidth;
18832         var c = cols[0];
18833         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18834          var cb = typeof a.checked == "boolean";
18835         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18836         var colcls = 'x-t-' + tid + '-c0';
18837         var buf = [
18838             '<li class="x-tree-node">',
18839             
18840                 
18841                 '<div class="x-tree-node-el ', a.cls,'">',
18842                     // extran...
18843                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18844                 
18845                 
18846                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18847                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18848                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18849                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18850                            (a.iconCls ? ' '+a.iconCls : ''),
18851                            '" unselectable="on" />',
18852                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18853                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18854                              
18855                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18856                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18857                             '<span unselectable="on" qtip="' + tx + '">',
18858                              tx,
18859                              '</span></a>' ,
18860                     '</div>',
18861                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18862                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18863                  ];
18864         for(var i = 1, len = cols.length; i < len; i++){
18865             c = cols[i];
18866             colcls = 'x-t-' + tid + '-c' +i;
18867             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18868             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18869                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18870                       "</div>");
18871          }
18872          
18873          buf.push(
18874             '</a>',
18875             '<div class="x-clear"></div></div>',
18876             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18877             "</li>");
18878         
18879         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18880             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18881                                 n.nextSibling.ui.getEl(), buf.join(""));
18882         }else{
18883             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18884         }
18885         var el = this.wrap.firstChild;
18886         this.elRow = el;
18887         this.elNode = el.firstChild;
18888         this.ranchor = el.childNodes[1];
18889         this.ctNode = this.wrap.childNodes[1];
18890         var cs = el.firstChild.childNodes;
18891         this.indentNode = cs[0];
18892         this.ecNode = cs[1];
18893         this.iconNode = cs[2];
18894         var index = 3;
18895         if(cb){
18896             this.checkbox = cs[3];
18897             index++;
18898         }
18899         this.anchor = cs[index];
18900         
18901         this.textNode = cs[index].firstChild;
18902         
18903         //el.on("click", this.onClick, this);
18904         //el.on("dblclick", this.onDblClick, this);
18905         
18906         
18907        // console.log(this);
18908     },
18909     initEvents : function(){
18910         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18911         
18912             
18913         var a = this.ranchor;
18914
18915         var el = Roo.get(a);
18916
18917         if(Roo.isOpera){ // opera render bug ignores the CSS
18918             el.setStyle("text-decoration", "none");
18919         }
18920
18921         el.on("click", this.onClick, this);
18922         el.on("dblclick", this.onDblClick, this);
18923         el.on("contextmenu", this.onContextMenu, this);
18924         
18925     },
18926     
18927     /*onSelectedChange : function(state){
18928         if(state){
18929             this.focus();
18930             this.addClass("x-tree-selected");
18931         }else{
18932             //this.blur();
18933             this.removeClass("x-tree-selected");
18934         }
18935     },*/
18936     addClass : function(cls){
18937         if(this.elRow){
18938             Roo.fly(this.elRow).addClass(cls);
18939         }
18940         
18941     },
18942     
18943     
18944     removeClass : function(cls){
18945         if(this.elRow){
18946             Roo.fly(this.elRow).removeClass(cls);
18947         }
18948     }
18949
18950     
18951     
18952 });//<Script type="text/javascript">
18953
18954 /*
18955  * Based on:
18956  * Ext JS Library 1.1.1
18957  * Copyright(c) 2006-2007, Ext JS, LLC.
18958  *
18959  * Originally Released Under LGPL - original licence link has changed is not relivant.
18960  *
18961  * Fork - LGPL
18962  * <script type="text/javascript">
18963  */
18964  
18965
18966 /**
18967  * @class Roo.tree.ColumnTree
18968  * @extends Roo.data.TreePanel
18969  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
18970  * @cfg {int} borderWidth  compined right/left border allowance
18971  * @constructor
18972  * @param {String/HTMLElement/Element} el The container element
18973  * @param {Object} config
18974  */
18975 Roo.tree.ColumnTree =  function(el, config)
18976 {
18977    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
18978    this.addEvents({
18979         /**
18980         * @event resize
18981         * Fire this event on a container when it resizes
18982         * @param {int} w Width
18983         * @param {int} h Height
18984         */
18985        "resize" : true
18986     });
18987     this.on('resize', this.onResize, this);
18988 };
18989
18990 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
18991     //lines:false,
18992     
18993     
18994     borderWidth: Roo.isBorderBox ? 0 : 2, 
18995     headEls : false,
18996     
18997     render : function(){
18998         // add the header.....
18999        
19000         Roo.tree.ColumnTree.superclass.render.apply(this);
19001         
19002         this.el.addClass('x-column-tree');
19003         
19004         this.headers = this.el.createChild(
19005             {cls:'x-tree-headers'},this.innerCt.dom);
19006    
19007         var cols = this.columns, c;
19008         var totalWidth = 0;
19009         this.headEls = [];
19010         var  len = cols.length;
19011         for(var i = 0; i < len; i++){
19012              c = cols[i];
19013              totalWidth += c.width;
19014             this.headEls.push(this.headers.createChild({
19015                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19016                  cn: {
19017                      cls:'x-tree-hd-text',
19018                      html: c.header
19019                  },
19020                  style:'width:'+(c.width-this.borderWidth)+'px;'
19021              }));
19022         }
19023         this.headers.createChild({cls:'x-clear'});
19024         // prevent floats from wrapping when clipped
19025         this.headers.setWidth(totalWidth);
19026         //this.innerCt.setWidth(totalWidth);
19027         this.innerCt.setStyle({ overflow: 'auto' });
19028         this.onResize(this.width, this.height);
19029              
19030         
19031     },
19032     onResize : function(w,h)
19033     {
19034         this.height = h;
19035         this.width = w;
19036         // resize cols..
19037         this.innerCt.setWidth(this.width);
19038         this.innerCt.setHeight(this.height-20);
19039         
19040         // headers...
19041         var cols = this.columns, c;
19042         var totalWidth = 0;
19043         var expEl = false;
19044         var len = cols.length;
19045         for(var i = 0; i < len; i++){
19046             c = cols[i];
19047             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19048                 // it's the expander..
19049                 expEl  = this.headEls[i];
19050                 continue;
19051             }
19052             totalWidth += c.width;
19053             
19054         }
19055         if (expEl) {
19056             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19057         }
19058         this.headers.setWidth(w-20);
19059
19060         
19061         
19062         
19063     }
19064 });
19065 /*
19066  * Based on:
19067  * Ext JS Library 1.1.1
19068  * Copyright(c) 2006-2007, Ext JS, LLC.
19069  *
19070  * Originally Released Under LGPL - original licence link has changed is not relivant.
19071  *
19072  * Fork - LGPL
19073  * <script type="text/javascript">
19074  */
19075  
19076 /**
19077  * @class Roo.menu.Menu
19078  * @extends Roo.util.Observable
19079  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19080  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19081  * @constructor
19082  * Creates a new Menu
19083  * @param {Object} config Configuration options
19084  */
19085 Roo.menu.Menu = function(config){
19086     Roo.apply(this, config);
19087     this.id = this.id || Roo.id();
19088     this.addEvents({
19089         /**
19090          * @event beforeshow
19091          * Fires before this menu is displayed
19092          * @param {Roo.menu.Menu} this
19093          */
19094         beforeshow : true,
19095         /**
19096          * @event beforehide
19097          * Fires before this menu is hidden
19098          * @param {Roo.menu.Menu} this
19099          */
19100         beforehide : true,
19101         /**
19102          * @event show
19103          * Fires after this menu is displayed
19104          * @param {Roo.menu.Menu} this
19105          */
19106         show : true,
19107         /**
19108          * @event hide
19109          * Fires after this menu is hidden
19110          * @param {Roo.menu.Menu} this
19111          */
19112         hide : true,
19113         /**
19114          * @event click
19115          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19116          * @param {Roo.menu.Menu} this
19117          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19118          * @param {Roo.EventObject} e
19119          */
19120         click : true,
19121         /**
19122          * @event mouseover
19123          * Fires when the mouse is hovering over this menu
19124          * @param {Roo.menu.Menu} this
19125          * @param {Roo.EventObject} e
19126          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19127          */
19128         mouseover : true,
19129         /**
19130          * @event mouseout
19131          * Fires when the mouse exits this menu
19132          * @param {Roo.menu.Menu} this
19133          * @param {Roo.EventObject} e
19134          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19135          */
19136         mouseout : true,
19137         /**
19138          * @event itemclick
19139          * Fires when a menu item contained in this menu is clicked
19140          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19141          * @param {Roo.EventObject} e
19142          */
19143         itemclick: true
19144     });
19145     if (this.registerMenu) {
19146         Roo.menu.MenuMgr.register(this);
19147     }
19148     
19149     var mis = this.items;
19150     this.items = new Roo.util.MixedCollection();
19151     if(mis){
19152         this.add.apply(this, mis);
19153     }
19154 };
19155
19156 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19157     /**
19158      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19159      */
19160     minWidth : 120,
19161     /**
19162      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19163      * for bottom-right shadow (defaults to "sides")
19164      */
19165     shadow : "sides",
19166     /**
19167      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19168      * this menu (defaults to "tl-tr?")
19169      */
19170     subMenuAlign : "tl-tr?",
19171     /**
19172      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19173      * relative to its element of origin (defaults to "tl-bl?")
19174      */
19175     defaultAlign : "tl-bl?",
19176     /**
19177      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19178      */
19179     allowOtherMenus : false,
19180     /**
19181      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19182      */
19183     registerMenu : true,
19184
19185     hidden:true,
19186
19187     // private
19188     render : function(){
19189         if(this.el){
19190             return;
19191         }
19192         var el = this.el = new Roo.Layer({
19193             cls: "x-menu",
19194             shadow:this.shadow,
19195             constrain: false,
19196             parentEl: this.parentEl || document.body,
19197             zindex:15000
19198         });
19199
19200         this.keyNav = new Roo.menu.MenuNav(this);
19201
19202         if(this.plain){
19203             el.addClass("x-menu-plain");
19204         }
19205         if(this.cls){
19206             el.addClass(this.cls);
19207         }
19208         // generic focus element
19209         this.focusEl = el.createChild({
19210             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19211         });
19212         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19213         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19214         
19215         ul.on("mouseover", this.onMouseOver, this);
19216         ul.on("mouseout", this.onMouseOut, this);
19217         this.items.each(function(item){
19218             if (item.hidden) {
19219                 return;
19220             }
19221             
19222             var li = document.createElement("li");
19223             li.className = "x-menu-list-item";
19224             ul.dom.appendChild(li);
19225             item.render(li, this);
19226         }, this);
19227         this.ul = ul;
19228         this.autoWidth();
19229     },
19230
19231     // private
19232     autoWidth : function(){
19233         var el = this.el, ul = this.ul;
19234         if(!el){
19235             return;
19236         }
19237         var w = this.width;
19238         if(w){
19239             el.setWidth(w);
19240         }else if(Roo.isIE){
19241             el.setWidth(this.minWidth);
19242             var t = el.dom.offsetWidth; // force recalc
19243             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19244         }
19245     },
19246
19247     // private
19248     delayAutoWidth : function(){
19249         if(this.rendered){
19250             if(!this.awTask){
19251                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19252             }
19253             this.awTask.delay(20);
19254         }
19255     },
19256
19257     // private
19258     findTargetItem : function(e){
19259         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19260         if(t && t.menuItemId){
19261             return this.items.get(t.menuItemId);
19262         }
19263     },
19264
19265     // private
19266     onClick : function(e){
19267         Roo.log("menu.onClick");
19268         var t = this.findTargetItem(e);
19269         if(!t){
19270             return;
19271         }
19272         Roo.log(e);
19273         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19274             if(t == this.activeItem && t.shouldDeactivate(e)){
19275                 this.activeItem.deactivate();
19276                 delete this.activeItem;
19277                 return;
19278             }
19279             if(t.canActivate){
19280                 this.setActiveItem(t, true);
19281             }
19282             return;
19283             
19284             
19285         }
19286         
19287         t.onClick(e);
19288         this.fireEvent("click", this, t, e);
19289     },
19290
19291     // private
19292     setActiveItem : function(item, autoExpand){
19293         if(item != this.activeItem){
19294             if(this.activeItem){
19295                 this.activeItem.deactivate();
19296             }
19297             this.activeItem = item;
19298             item.activate(autoExpand);
19299         }else if(autoExpand){
19300             item.expandMenu();
19301         }
19302     },
19303
19304     // private
19305     tryActivate : function(start, step){
19306         var items = this.items;
19307         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19308             var item = items.get(i);
19309             if(!item.disabled && item.canActivate){
19310                 this.setActiveItem(item, false);
19311                 return item;
19312             }
19313         }
19314         return false;
19315     },
19316
19317     // private
19318     onMouseOver : function(e){
19319         var t;
19320         if(t = this.findTargetItem(e)){
19321             if(t.canActivate && !t.disabled){
19322                 this.setActiveItem(t, true);
19323             }
19324         }
19325         this.fireEvent("mouseover", this, e, t);
19326     },
19327
19328     // private
19329     onMouseOut : function(e){
19330         var t;
19331         if(t = this.findTargetItem(e)){
19332             if(t == this.activeItem && t.shouldDeactivate(e)){
19333                 this.activeItem.deactivate();
19334                 delete this.activeItem;
19335             }
19336         }
19337         this.fireEvent("mouseout", this, e, t);
19338     },
19339
19340     /**
19341      * Read-only.  Returns true if the menu is currently displayed, else false.
19342      * @type Boolean
19343      */
19344     isVisible : function(){
19345         return this.el && !this.hidden;
19346     },
19347
19348     /**
19349      * Displays this menu relative to another element
19350      * @param {String/HTMLElement/Roo.Element} element The element to align to
19351      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19352      * the element (defaults to this.defaultAlign)
19353      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19354      */
19355     show : function(el, pos, parentMenu){
19356         this.parentMenu = parentMenu;
19357         if(!this.el){
19358             this.render();
19359         }
19360         this.fireEvent("beforeshow", this);
19361         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19362     },
19363
19364     /**
19365      * Displays this menu at a specific xy position
19366      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19367      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19368      */
19369     showAt : function(xy, parentMenu, /* private: */_e){
19370         this.parentMenu = parentMenu;
19371         if(!this.el){
19372             this.render();
19373         }
19374         if(_e !== false){
19375             this.fireEvent("beforeshow", this);
19376             xy = this.el.adjustForConstraints(xy);
19377         }
19378         this.el.setXY(xy);
19379         this.el.show();
19380         this.hidden = false;
19381         this.focus();
19382         this.fireEvent("show", this);
19383     },
19384
19385     focus : function(){
19386         if(!this.hidden){
19387             this.doFocus.defer(50, this);
19388         }
19389     },
19390
19391     doFocus : function(){
19392         if(!this.hidden){
19393             this.focusEl.focus();
19394         }
19395     },
19396
19397     /**
19398      * Hides this menu and optionally all parent menus
19399      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19400      */
19401     hide : function(deep){
19402         if(this.el && this.isVisible()){
19403             this.fireEvent("beforehide", this);
19404             if(this.activeItem){
19405                 this.activeItem.deactivate();
19406                 this.activeItem = null;
19407             }
19408             this.el.hide();
19409             this.hidden = true;
19410             this.fireEvent("hide", this);
19411         }
19412         if(deep === true && this.parentMenu){
19413             this.parentMenu.hide(true);
19414         }
19415     },
19416
19417     /**
19418      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19419      * Any of the following are valid:
19420      * <ul>
19421      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19422      * <li>An HTMLElement object which will be converted to a menu item</li>
19423      * <li>A menu item config object that will be created as a new menu item</li>
19424      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19425      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19426      * </ul>
19427      * Usage:
19428      * <pre><code>
19429 // Create the menu
19430 var menu = new Roo.menu.Menu();
19431
19432 // Create a menu item to add by reference
19433 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19434
19435 // Add a bunch of items at once using different methods.
19436 // Only the last item added will be returned.
19437 var item = menu.add(
19438     menuItem,                // add existing item by ref
19439     'Dynamic Item',          // new TextItem
19440     '-',                     // new separator
19441     { text: 'Config Item' }  // new item by config
19442 );
19443 </code></pre>
19444      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19445      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19446      */
19447     add : function(){
19448         var a = arguments, l = a.length, item;
19449         for(var i = 0; i < l; i++){
19450             var el = a[i];
19451             if ((typeof(el) == "object") && el.xtype && el.xns) {
19452                 el = Roo.factory(el, Roo.menu);
19453             }
19454             
19455             if(el.render){ // some kind of Item
19456                 item = this.addItem(el);
19457             }else if(typeof el == "string"){ // string
19458                 if(el == "separator" || el == "-"){
19459                     item = this.addSeparator();
19460                 }else{
19461                     item = this.addText(el);
19462                 }
19463             }else if(el.tagName || el.el){ // element
19464                 item = this.addElement(el);
19465             }else if(typeof el == "object"){ // must be menu item config?
19466                 item = this.addMenuItem(el);
19467             }
19468         }
19469         return item;
19470     },
19471
19472     /**
19473      * Returns this menu's underlying {@link Roo.Element} object
19474      * @return {Roo.Element} The element
19475      */
19476     getEl : function(){
19477         if(!this.el){
19478             this.render();
19479         }
19480         return this.el;
19481     },
19482
19483     /**
19484      * Adds a separator bar to the menu
19485      * @return {Roo.menu.Item} The menu item that was added
19486      */
19487     addSeparator : function(){
19488         return this.addItem(new Roo.menu.Separator());
19489     },
19490
19491     /**
19492      * Adds an {@link Roo.Element} object to the menu
19493      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19494      * @return {Roo.menu.Item} The menu item that was added
19495      */
19496     addElement : function(el){
19497         return this.addItem(new Roo.menu.BaseItem(el));
19498     },
19499
19500     /**
19501      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19502      * @param {Roo.menu.Item} item The menu item to add
19503      * @return {Roo.menu.Item} The menu item that was added
19504      */
19505     addItem : function(item){
19506         this.items.add(item);
19507         if(this.ul){
19508             var li = document.createElement("li");
19509             li.className = "x-menu-list-item";
19510             this.ul.dom.appendChild(li);
19511             item.render(li, this);
19512             this.delayAutoWidth();
19513         }
19514         return item;
19515     },
19516
19517     /**
19518      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19519      * @param {Object} config A MenuItem config object
19520      * @return {Roo.menu.Item} The menu item that was added
19521      */
19522     addMenuItem : function(config){
19523         if(!(config instanceof Roo.menu.Item)){
19524             if(typeof config.checked == "boolean"){ // must be check menu item config?
19525                 config = new Roo.menu.CheckItem(config);
19526             }else{
19527                 config = new Roo.menu.Item(config);
19528             }
19529         }
19530         return this.addItem(config);
19531     },
19532
19533     /**
19534      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19535      * @param {String} text The text to display in the menu item
19536      * @return {Roo.menu.Item} The menu item that was added
19537      */
19538     addText : function(text){
19539         return this.addItem(new Roo.menu.TextItem({ text : text }));
19540     },
19541
19542     /**
19543      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19544      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19545      * @param {Roo.menu.Item} item The menu item to add
19546      * @return {Roo.menu.Item} The menu item that was added
19547      */
19548     insert : function(index, item){
19549         this.items.insert(index, item);
19550         if(this.ul){
19551             var li = document.createElement("li");
19552             li.className = "x-menu-list-item";
19553             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19554             item.render(li, this);
19555             this.delayAutoWidth();
19556         }
19557         return item;
19558     },
19559
19560     /**
19561      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19562      * @param {Roo.menu.Item} item The menu item to remove
19563      */
19564     remove : function(item){
19565         this.items.removeKey(item.id);
19566         item.destroy();
19567     },
19568
19569     /**
19570      * Removes and destroys all items in the menu
19571      */
19572     removeAll : function(){
19573         var f;
19574         while(f = this.items.first()){
19575             this.remove(f);
19576         }
19577     }
19578 });
19579
19580 // MenuNav is a private utility class used internally by the Menu
19581 Roo.menu.MenuNav = function(menu){
19582     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19583     this.scope = this.menu = menu;
19584 };
19585
19586 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19587     doRelay : function(e, h){
19588         var k = e.getKey();
19589         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19590             this.menu.tryActivate(0, 1);
19591             return false;
19592         }
19593         return h.call(this.scope || this, e, this.menu);
19594     },
19595
19596     up : function(e, m){
19597         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19598             m.tryActivate(m.items.length-1, -1);
19599         }
19600     },
19601
19602     down : function(e, m){
19603         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19604             m.tryActivate(0, 1);
19605         }
19606     },
19607
19608     right : function(e, m){
19609         if(m.activeItem){
19610             m.activeItem.expandMenu(true);
19611         }
19612     },
19613
19614     left : function(e, m){
19615         m.hide();
19616         if(m.parentMenu && m.parentMenu.activeItem){
19617             m.parentMenu.activeItem.activate();
19618         }
19619     },
19620
19621     enter : function(e, m){
19622         if(m.activeItem){
19623             e.stopPropagation();
19624             m.activeItem.onClick(e);
19625             m.fireEvent("click", this, m.activeItem);
19626             return true;
19627         }
19628     }
19629 });/*
19630  * Based on:
19631  * Ext JS Library 1.1.1
19632  * Copyright(c) 2006-2007, Ext JS, LLC.
19633  *
19634  * Originally Released Under LGPL - original licence link has changed is not relivant.
19635  *
19636  * Fork - LGPL
19637  * <script type="text/javascript">
19638  */
19639  
19640 /**
19641  * @class Roo.menu.MenuMgr
19642  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19643  * @singleton
19644  */
19645 Roo.menu.MenuMgr = function(){
19646    var menus, active, groups = {}, attached = false, lastShow = new Date();
19647
19648    // private - called when first menu is created
19649    function init(){
19650        menus = {};
19651        active = new Roo.util.MixedCollection();
19652        Roo.get(document).addKeyListener(27, function(){
19653            if(active.length > 0){
19654                hideAll();
19655            }
19656        });
19657    }
19658
19659    // private
19660    function hideAll(){
19661        if(active && active.length > 0){
19662            var c = active.clone();
19663            c.each(function(m){
19664                m.hide();
19665            });
19666        }
19667    }
19668
19669    // private
19670    function onHide(m){
19671        active.remove(m);
19672        if(active.length < 1){
19673            Roo.get(document).un("mousedown", onMouseDown);
19674            attached = false;
19675        }
19676    }
19677
19678    // private
19679    function onShow(m){
19680        var last = active.last();
19681        lastShow = new Date();
19682        active.add(m);
19683        if(!attached){
19684            Roo.get(document).on("mousedown", onMouseDown);
19685            attached = true;
19686        }
19687        if(m.parentMenu){
19688           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19689           m.parentMenu.activeChild = m;
19690        }else if(last && last.isVisible()){
19691           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19692        }
19693    }
19694
19695    // private
19696    function onBeforeHide(m){
19697        if(m.activeChild){
19698            m.activeChild.hide();
19699        }
19700        if(m.autoHideTimer){
19701            clearTimeout(m.autoHideTimer);
19702            delete m.autoHideTimer;
19703        }
19704    }
19705
19706    // private
19707    function onBeforeShow(m){
19708        var pm = m.parentMenu;
19709        if(!pm && !m.allowOtherMenus){
19710            hideAll();
19711        }else if(pm && pm.activeChild && active != m){
19712            pm.activeChild.hide();
19713        }
19714    }
19715
19716    // private
19717    function onMouseDown(e){
19718        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19719            hideAll();
19720        }
19721    }
19722
19723    // private
19724    function onBeforeCheck(mi, state){
19725        if(state){
19726            var g = groups[mi.group];
19727            for(var i = 0, l = g.length; i < l; i++){
19728                if(g[i] != mi){
19729                    g[i].setChecked(false);
19730                }
19731            }
19732        }
19733    }
19734
19735    return {
19736
19737        /**
19738         * Hides all menus that are currently visible
19739         */
19740        hideAll : function(){
19741             hideAll();  
19742        },
19743
19744        // private
19745        register : function(menu){
19746            if(!menus){
19747                init();
19748            }
19749            menus[menu.id] = menu;
19750            menu.on("beforehide", onBeforeHide);
19751            menu.on("hide", onHide);
19752            menu.on("beforeshow", onBeforeShow);
19753            menu.on("show", onShow);
19754            var g = menu.group;
19755            if(g && menu.events["checkchange"]){
19756                if(!groups[g]){
19757                    groups[g] = [];
19758                }
19759                groups[g].push(menu);
19760                menu.on("checkchange", onCheck);
19761            }
19762        },
19763
19764         /**
19765          * Returns a {@link Roo.menu.Menu} object
19766          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19767          * be used to generate and return a new Menu instance.
19768          */
19769        get : function(menu){
19770            if(typeof menu == "string"){ // menu id
19771                return menus[menu];
19772            }else if(menu.events){  // menu instance
19773                return menu;
19774            }else if(typeof menu.length == 'number'){ // array of menu items?
19775                return new Roo.menu.Menu({items:menu});
19776            }else{ // otherwise, must be a config
19777                return new Roo.menu.Menu(menu);
19778            }
19779        },
19780
19781        // private
19782        unregister : function(menu){
19783            delete menus[menu.id];
19784            menu.un("beforehide", onBeforeHide);
19785            menu.un("hide", onHide);
19786            menu.un("beforeshow", onBeforeShow);
19787            menu.un("show", onShow);
19788            var g = menu.group;
19789            if(g && menu.events["checkchange"]){
19790                groups[g].remove(menu);
19791                menu.un("checkchange", onCheck);
19792            }
19793        },
19794
19795        // private
19796        registerCheckable : function(menuItem){
19797            var g = menuItem.group;
19798            if(g){
19799                if(!groups[g]){
19800                    groups[g] = [];
19801                }
19802                groups[g].push(menuItem);
19803                menuItem.on("beforecheckchange", onBeforeCheck);
19804            }
19805        },
19806
19807        // private
19808        unregisterCheckable : function(menuItem){
19809            var g = menuItem.group;
19810            if(g){
19811                groups[g].remove(menuItem);
19812                menuItem.un("beforecheckchange", onBeforeCheck);
19813            }
19814        }
19815    };
19816 }();/*
19817  * Based on:
19818  * Ext JS Library 1.1.1
19819  * Copyright(c) 2006-2007, Ext JS, LLC.
19820  *
19821  * Originally Released Under LGPL - original licence link has changed is not relivant.
19822  *
19823  * Fork - LGPL
19824  * <script type="text/javascript">
19825  */
19826  
19827
19828 /**
19829  * @class Roo.menu.BaseItem
19830  * @extends Roo.Component
19831  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19832  * management and base configuration options shared by all menu components.
19833  * @constructor
19834  * Creates a new BaseItem
19835  * @param {Object} config Configuration options
19836  */
19837 Roo.menu.BaseItem = function(config){
19838     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19839
19840     this.addEvents({
19841         /**
19842          * @event click
19843          * Fires when this item is clicked
19844          * @param {Roo.menu.BaseItem} this
19845          * @param {Roo.EventObject} e
19846          */
19847         click: true,
19848         /**
19849          * @event activate
19850          * Fires when this item is activated
19851          * @param {Roo.menu.BaseItem} this
19852          */
19853         activate : true,
19854         /**
19855          * @event deactivate
19856          * Fires when this item is deactivated
19857          * @param {Roo.menu.BaseItem} this
19858          */
19859         deactivate : true
19860     });
19861
19862     if(this.handler){
19863         this.on("click", this.handler, this.scope, true);
19864     }
19865 };
19866
19867 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19868     /**
19869      * @cfg {Function} handler
19870      * A function that will handle the click event of this menu item (defaults to undefined)
19871      */
19872     /**
19873      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19874      */
19875     canActivate : false,
19876     
19877      /**
19878      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19879      */
19880     hidden: false,
19881     
19882     /**
19883      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19884      */
19885     activeClass : "x-menu-item-active",
19886     /**
19887      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19888      */
19889     hideOnClick : true,
19890     /**
19891      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19892      */
19893     hideDelay : 100,
19894
19895     // private
19896     ctype: "Roo.menu.BaseItem",
19897
19898     // private
19899     actionMode : "container",
19900
19901     // private
19902     render : function(container, parentMenu){
19903         this.parentMenu = parentMenu;
19904         Roo.menu.BaseItem.superclass.render.call(this, container);
19905         this.container.menuItemId = this.id;
19906     },
19907
19908     // private
19909     onRender : function(container, position){
19910         this.el = Roo.get(this.el);
19911         container.dom.appendChild(this.el.dom);
19912     },
19913
19914     // private
19915     onClick : function(e){
19916         if(!this.disabled && this.fireEvent("click", this, e) !== false
19917                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19918             this.handleClick(e);
19919         }else{
19920             e.stopEvent();
19921         }
19922     },
19923
19924     // private
19925     activate : function(){
19926         if(this.disabled){
19927             return false;
19928         }
19929         var li = this.container;
19930         li.addClass(this.activeClass);
19931         this.region = li.getRegion().adjust(2, 2, -2, -2);
19932         this.fireEvent("activate", this);
19933         return true;
19934     },
19935
19936     // private
19937     deactivate : function(){
19938         this.container.removeClass(this.activeClass);
19939         this.fireEvent("deactivate", this);
19940     },
19941
19942     // private
19943     shouldDeactivate : function(e){
19944         return !this.region || !this.region.contains(e.getPoint());
19945     },
19946
19947     // private
19948     handleClick : function(e){
19949         if(this.hideOnClick){
19950             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19951         }
19952     },
19953
19954     // private
19955     expandMenu : function(autoActivate){
19956         // do nothing
19957     },
19958
19959     // private
19960     hideMenu : function(){
19961         // do nothing
19962     }
19963 });/*
19964  * Based on:
19965  * Ext JS Library 1.1.1
19966  * Copyright(c) 2006-2007, Ext JS, LLC.
19967  *
19968  * Originally Released Under LGPL - original licence link has changed is not relivant.
19969  *
19970  * Fork - LGPL
19971  * <script type="text/javascript">
19972  */
19973  
19974 /**
19975  * @class Roo.menu.Adapter
19976  * @extends Roo.menu.BaseItem
19977  * 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.
19978  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
19979  * @constructor
19980  * Creates a new Adapter
19981  * @param {Object} config Configuration options
19982  */
19983 Roo.menu.Adapter = function(component, config){
19984     Roo.menu.Adapter.superclass.constructor.call(this, config);
19985     this.component = component;
19986 };
19987 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
19988     // private
19989     canActivate : true,
19990
19991     // private
19992     onRender : function(container, position){
19993         this.component.render(container);
19994         this.el = this.component.getEl();
19995     },
19996
19997     // private
19998     activate : function(){
19999         if(this.disabled){
20000             return false;
20001         }
20002         this.component.focus();
20003         this.fireEvent("activate", this);
20004         return true;
20005     },
20006
20007     // private
20008     deactivate : function(){
20009         this.fireEvent("deactivate", this);
20010     },
20011
20012     // private
20013     disable : function(){
20014         this.component.disable();
20015         Roo.menu.Adapter.superclass.disable.call(this);
20016     },
20017
20018     // private
20019     enable : function(){
20020         this.component.enable();
20021         Roo.menu.Adapter.superclass.enable.call(this);
20022     }
20023 });/*
20024  * Based on:
20025  * Ext JS Library 1.1.1
20026  * Copyright(c) 2006-2007, Ext JS, LLC.
20027  *
20028  * Originally Released Under LGPL - original licence link has changed is not relivant.
20029  *
20030  * Fork - LGPL
20031  * <script type="text/javascript">
20032  */
20033
20034 /**
20035  * @class Roo.menu.TextItem
20036  * @extends Roo.menu.BaseItem
20037  * Adds a static text string to a menu, usually used as either a heading or group separator.
20038  * Note: old style constructor with text is still supported.
20039  * 
20040  * @constructor
20041  * Creates a new TextItem
20042  * @param {Object} cfg Configuration
20043  */
20044 Roo.menu.TextItem = function(cfg){
20045     if (typeof(cfg) == 'string') {
20046         this.text = cfg;
20047     } else {
20048         Roo.apply(this,cfg);
20049     }
20050     
20051     Roo.menu.TextItem.superclass.constructor.call(this);
20052 };
20053
20054 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20055     /**
20056      * @cfg {Boolean} text Text to show on item.
20057      */
20058     text : '',
20059     
20060     /**
20061      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20062      */
20063     hideOnClick : false,
20064     /**
20065      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20066      */
20067     itemCls : "x-menu-text",
20068
20069     // private
20070     onRender : function(){
20071         var s = document.createElement("span");
20072         s.className = this.itemCls;
20073         s.innerHTML = this.text;
20074         this.el = s;
20075         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20076     }
20077 });/*
20078  * Based on:
20079  * Ext JS Library 1.1.1
20080  * Copyright(c) 2006-2007, Ext JS, LLC.
20081  *
20082  * Originally Released Under LGPL - original licence link has changed is not relivant.
20083  *
20084  * Fork - LGPL
20085  * <script type="text/javascript">
20086  */
20087
20088 /**
20089  * @class Roo.menu.Separator
20090  * @extends Roo.menu.BaseItem
20091  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20092  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20093  * @constructor
20094  * @param {Object} config Configuration options
20095  */
20096 Roo.menu.Separator = function(config){
20097     Roo.menu.Separator.superclass.constructor.call(this, config);
20098 };
20099
20100 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20101     /**
20102      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20103      */
20104     itemCls : "x-menu-sep",
20105     /**
20106      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20107      */
20108     hideOnClick : false,
20109
20110     // private
20111     onRender : function(li){
20112         var s = document.createElement("span");
20113         s.className = this.itemCls;
20114         s.innerHTML = "&#160;";
20115         this.el = s;
20116         li.addClass("x-menu-sep-li");
20117         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20118     }
20119 });/*
20120  * Based on:
20121  * Ext JS Library 1.1.1
20122  * Copyright(c) 2006-2007, Ext JS, LLC.
20123  *
20124  * Originally Released Under LGPL - original licence link has changed is not relivant.
20125  *
20126  * Fork - LGPL
20127  * <script type="text/javascript">
20128  */
20129 /**
20130  * @class Roo.menu.Item
20131  * @extends Roo.menu.BaseItem
20132  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20133  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20134  * activation and click handling.
20135  * @constructor
20136  * Creates a new Item
20137  * @param {Object} config Configuration options
20138  */
20139 Roo.menu.Item = function(config){
20140     Roo.menu.Item.superclass.constructor.call(this, config);
20141     if(this.menu){
20142         this.menu = Roo.menu.MenuMgr.get(this.menu);
20143     }
20144 };
20145 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20146     
20147     /**
20148      * @cfg {String} text
20149      * The text to show on the menu item.
20150      */
20151     text: '',
20152      /**
20153      * @cfg {String} HTML to render in menu
20154      * The text to show on the menu item (HTML version).
20155      */
20156     html: '',
20157     /**
20158      * @cfg {String} icon
20159      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20160      */
20161     icon: undefined,
20162     /**
20163      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20164      */
20165     itemCls : "x-menu-item",
20166     /**
20167      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20168      */
20169     canActivate : true,
20170     /**
20171      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20172      */
20173     showDelay: 200,
20174     // doc'd in BaseItem
20175     hideDelay: 200,
20176
20177     // private
20178     ctype: "Roo.menu.Item",
20179     
20180     // private
20181     onRender : function(container, position){
20182         var el = document.createElement("a");
20183         el.hideFocus = true;
20184         el.unselectable = "on";
20185         el.href = this.href || "#";
20186         if(this.hrefTarget){
20187             el.target = this.hrefTarget;
20188         }
20189         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20190         
20191         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20192         
20193         el.innerHTML = String.format(
20194                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20195                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20196         this.el = el;
20197         Roo.menu.Item.superclass.onRender.call(this, container, position);
20198     },
20199
20200     /**
20201      * Sets the text to display in this menu item
20202      * @param {String} text The text to display
20203      * @param {Boolean} isHTML true to indicate text is pure html.
20204      */
20205     setText : function(text, isHTML){
20206         if (isHTML) {
20207             this.html = text;
20208         } else {
20209             this.text = text;
20210             this.html = '';
20211         }
20212         if(this.rendered){
20213             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20214      
20215             this.el.update(String.format(
20216                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20217                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20218             this.parentMenu.autoWidth();
20219         }
20220     },
20221
20222     // private
20223     handleClick : function(e){
20224         if(!this.href){ // if no link defined, stop the event automatically
20225             e.stopEvent();
20226         }
20227         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20228     },
20229
20230     // private
20231     activate : function(autoExpand){
20232         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20233             this.focus();
20234             if(autoExpand){
20235                 this.expandMenu();
20236             }
20237         }
20238         return true;
20239     },
20240
20241     // private
20242     shouldDeactivate : function(e){
20243         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20244             if(this.menu && this.menu.isVisible()){
20245                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20246             }
20247             return true;
20248         }
20249         return false;
20250     },
20251
20252     // private
20253     deactivate : function(){
20254         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20255         this.hideMenu();
20256     },
20257
20258     // private
20259     expandMenu : function(autoActivate){
20260         if(!this.disabled && this.menu){
20261             clearTimeout(this.hideTimer);
20262             delete this.hideTimer;
20263             if(!this.menu.isVisible() && !this.showTimer){
20264                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20265             }else if (this.menu.isVisible() && autoActivate){
20266                 this.menu.tryActivate(0, 1);
20267             }
20268         }
20269     },
20270
20271     // private
20272     deferExpand : function(autoActivate){
20273         delete this.showTimer;
20274         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20275         if(autoActivate){
20276             this.menu.tryActivate(0, 1);
20277         }
20278     },
20279
20280     // private
20281     hideMenu : function(){
20282         clearTimeout(this.showTimer);
20283         delete this.showTimer;
20284         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20285             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20286         }
20287     },
20288
20289     // private
20290     deferHide : function(){
20291         delete this.hideTimer;
20292         this.menu.hide();
20293     }
20294 });/*
20295  * Based on:
20296  * Ext JS Library 1.1.1
20297  * Copyright(c) 2006-2007, Ext JS, LLC.
20298  *
20299  * Originally Released Under LGPL - original licence link has changed is not relivant.
20300  *
20301  * Fork - LGPL
20302  * <script type="text/javascript">
20303  */
20304  
20305 /**
20306  * @class Roo.menu.CheckItem
20307  * @extends Roo.menu.Item
20308  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20309  * @constructor
20310  * Creates a new CheckItem
20311  * @param {Object} config Configuration options
20312  */
20313 Roo.menu.CheckItem = function(config){
20314     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20315     this.addEvents({
20316         /**
20317          * @event beforecheckchange
20318          * Fires before the checked value is set, providing an opportunity to cancel if needed
20319          * @param {Roo.menu.CheckItem} this
20320          * @param {Boolean} checked The new checked value that will be set
20321          */
20322         "beforecheckchange" : true,
20323         /**
20324          * @event checkchange
20325          * Fires after the checked value has been set
20326          * @param {Roo.menu.CheckItem} this
20327          * @param {Boolean} checked The checked value that was set
20328          */
20329         "checkchange" : true
20330     });
20331     if(this.checkHandler){
20332         this.on('checkchange', this.checkHandler, this.scope);
20333     }
20334 };
20335 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20336     /**
20337      * @cfg {String} group
20338      * All check items with the same group name will automatically be grouped into a single-select
20339      * radio button group (defaults to '')
20340      */
20341     /**
20342      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20343      */
20344     itemCls : "x-menu-item x-menu-check-item",
20345     /**
20346      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20347      */
20348     groupClass : "x-menu-group-item",
20349
20350     /**
20351      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20352      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20353      * initialized with checked = true will be rendered as checked.
20354      */
20355     checked: false,
20356
20357     // private
20358     ctype: "Roo.menu.CheckItem",
20359
20360     // private
20361     onRender : function(c){
20362         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20363         if(this.group){
20364             this.el.addClass(this.groupClass);
20365         }
20366         Roo.menu.MenuMgr.registerCheckable(this);
20367         if(this.checked){
20368             this.checked = false;
20369             this.setChecked(true, true);
20370         }
20371     },
20372
20373     // private
20374     destroy : function(){
20375         if(this.rendered){
20376             Roo.menu.MenuMgr.unregisterCheckable(this);
20377         }
20378         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20379     },
20380
20381     /**
20382      * Set the checked state of this item
20383      * @param {Boolean} checked The new checked value
20384      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20385      */
20386     setChecked : function(state, suppressEvent){
20387         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20388             if(this.container){
20389                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20390             }
20391             this.checked = state;
20392             if(suppressEvent !== true){
20393                 this.fireEvent("checkchange", this, state);
20394             }
20395         }
20396     },
20397
20398     // private
20399     handleClick : function(e){
20400        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20401            this.setChecked(!this.checked);
20402        }
20403        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20404     }
20405 });/*
20406  * Based on:
20407  * Ext JS Library 1.1.1
20408  * Copyright(c) 2006-2007, Ext JS, LLC.
20409  *
20410  * Originally Released Under LGPL - original licence link has changed is not relivant.
20411  *
20412  * Fork - LGPL
20413  * <script type="text/javascript">
20414  */
20415  
20416 /**
20417  * @class Roo.menu.DateItem
20418  * @extends Roo.menu.Adapter
20419  * A menu item that wraps the {@link Roo.DatPicker} component.
20420  * @constructor
20421  * Creates a new DateItem
20422  * @param {Object} config Configuration options
20423  */
20424 Roo.menu.DateItem = function(config){
20425     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20426     /** The Roo.DatePicker object @type Roo.DatePicker */
20427     this.picker = this.component;
20428     this.addEvents({select: true});
20429     
20430     this.picker.on("render", function(picker){
20431         picker.getEl().swallowEvent("click");
20432         picker.container.addClass("x-menu-date-item");
20433     });
20434
20435     this.picker.on("select", this.onSelect, this);
20436 };
20437
20438 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20439     // private
20440     onSelect : function(picker, date){
20441         this.fireEvent("select", this, date, picker);
20442         Roo.menu.DateItem.superclass.handleClick.call(this);
20443     }
20444 });/*
20445  * Based on:
20446  * Ext JS Library 1.1.1
20447  * Copyright(c) 2006-2007, Ext JS, LLC.
20448  *
20449  * Originally Released Under LGPL - original licence link has changed is not relivant.
20450  *
20451  * Fork - LGPL
20452  * <script type="text/javascript">
20453  */
20454  
20455 /**
20456  * @class Roo.menu.ColorItem
20457  * @extends Roo.menu.Adapter
20458  * A menu item that wraps the {@link Roo.ColorPalette} component.
20459  * @constructor
20460  * Creates a new ColorItem
20461  * @param {Object} config Configuration options
20462  */
20463 Roo.menu.ColorItem = function(config){
20464     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20465     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20466     this.palette = this.component;
20467     this.relayEvents(this.palette, ["select"]);
20468     if(this.selectHandler){
20469         this.on('select', this.selectHandler, this.scope);
20470     }
20471 };
20472 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20473  * Based on:
20474  * Ext JS Library 1.1.1
20475  * Copyright(c) 2006-2007, Ext JS, LLC.
20476  *
20477  * Originally Released Under LGPL - original licence link has changed is not relivant.
20478  *
20479  * Fork - LGPL
20480  * <script type="text/javascript">
20481  */
20482  
20483
20484 /**
20485  * @class Roo.menu.DateMenu
20486  * @extends Roo.menu.Menu
20487  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20488  * @constructor
20489  * Creates a new DateMenu
20490  * @param {Object} config Configuration options
20491  */
20492 Roo.menu.DateMenu = function(config){
20493     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20494     this.plain = true;
20495     var di = new Roo.menu.DateItem(config);
20496     this.add(di);
20497     /**
20498      * The {@link Roo.DatePicker} instance for this DateMenu
20499      * @type DatePicker
20500      */
20501     this.picker = di.picker;
20502     /**
20503      * @event select
20504      * @param {DatePicker} picker
20505      * @param {Date} date
20506      */
20507     this.relayEvents(di, ["select"]);
20508     this.on('beforeshow', function(){
20509         if(this.picker){
20510             this.picker.hideMonthPicker(false);
20511         }
20512     }, this);
20513 };
20514 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20515     cls:'x-date-menu'
20516 });/*
20517  * Based on:
20518  * Ext JS Library 1.1.1
20519  * Copyright(c) 2006-2007, Ext JS, LLC.
20520  *
20521  * Originally Released Under LGPL - original licence link has changed is not relivant.
20522  *
20523  * Fork - LGPL
20524  * <script type="text/javascript">
20525  */
20526  
20527
20528 /**
20529  * @class Roo.menu.ColorMenu
20530  * @extends Roo.menu.Menu
20531  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20532  * @constructor
20533  * Creates a new ColorMenu
20534  * @param {Object} config Configuration options
20535  */
20536 Roo.menu.ColorMenu = function(config){
20537     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20538     this.plain = true;
20539     var ci = new Roo.menu.ColorItem(config);
20540     this.add(ci);
20541     /**
20542      * The {@link Roo.ColorPalette} instance for this ColorMenu
20543      * @type ColorPalette
20544      */
20545     this.palette = ci.palette;
20546     /**
20547      * @event select
20548      * @param {ColorPalette} palette
20549      * @param {String} color
20550      */
20551     this.relayEvents(ci, ["select"]);
20552 };
20553 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20554  * Based on:
20555  * Ext JS Library 1.1.1
20556  * Copyright(c) 2006-2007, Ext JS, LLC.
20557  *
20558  * Originally Released Under LGPL - original licence link has changed is not relivant.
20559  *
20560  * Fork - LGPL
20561  * <script type="text/javascript">
20562  */
20563  
20564 /**
20565  * @class Roo.form.Field
20566  * @extends Roo.BoxComponent
20567  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20568  * @constructor
20569  * Creates a new Field
20570  * @param {Object} config Configuration options
20571  */
20572 Roo.form.Field = function(config){
20573     Roo.form.Field.superclass.constructor.call(this, config);
20574 };
20575
20576 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20577     /**
20578      * @cfg {String} fieldLabel Label to use when rendering a form.
20579      */
20580        /**
20581      * @cfg {String} qtip Mouse over tip
20582      */
20583      
20584     /**
20585      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20586      */
20587     invalidClass : "x-form-invalid",
20588     /**
20589      * @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")
20590      */
20591     invalidText : "The value in this field is invalid",
20592     /**
20593      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20594      */
20595     focusClass : "x-form-focus",
20596     /**
20597      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20598       automatic validation (defaults to "keyup").
20599      */
20600     validationEvent : "keyup",
20601     /**
20602      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20603      */
20604     validateOnBlur : true,
20605     /**
20606      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20607      */
20608     validationDelay : 250,
20609     /**
20610      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20611      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20612      */
20613     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20614     /**
20615      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20616      */
20617     fieldClass : "x-form-field",
20618     /**
20619      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20620      *<pre>
20621 Value         Description
20622 -----------   ----------------------------------------------------------------------
20623 qtip          Display a quick tip when the user hovers over the field
20624 title         Display a default browser title attribute popup
20625 under         Add a block div beneath the field containing the error text
20626 side          Add an error icon to the right of the field with a popup on hover
20627 [element id]  Add the error text directly to the innerHTML of the specified element
20628 </pre>
20629      */
20630     msgTarget : 'qtip',
20631     /**
20632      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20633      */
20634     msgFx : 'normal',
20635
20636     /**
20637      * @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.
20638      */
20639     readOnly : false,
20640
20641     /**
20642      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20643      */
20644     disabled : false,
20645
20646     /**
20647      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20648      */
20649     inputType : undefined,
20650     
20651     /**
20652      * @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).
20653          */
20654         tabIndex : undefined,
20655         
20656     // private
20657     isFormField : true,
20658
20659     // private
20660     hasFocus : false,
20661     /**
20662      * @property {Roo.Element} fieldEl
20663      * Element Containing the rendered Field (with label etc.)
20664      */
20665     /**
20666      * @cfg {Mixed} value A value to initialize this field with.
20667      */
20668     value : undefined,
20669
20670     /**
20671      * @cfg {String} name The field's HTML name attribute.
20672      */
20673     /**
20674      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20675      */
20676
20677         // private ??
20678         initComponent : function(){
20679         Roo.form.Field.superclass.initComponent.call(this);
20680         this.addEvents({
20681             /**
20682              * @event focus
20683              * Fires when this field receives input focus.
20684              * @param {Roo.form.Field} this
20685              */
20686             focus : true,
20687             /**
20688              * @event blur
20689              * Fires when this field loses input focus.
20690              * @param {Roo.form.Field} this
20691              */
20692             blur : true,
20693             /**
20694              * @event specialkey
20695              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20696              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20697              * @param {Roo.form.Field} this
20698              * @param {Roo.EventObject} e The event object
20699              */
20700             specialkey : true,
20701             /**
20702              * @event change
20703              * Fires just before the field blurs if the field value has changed.
20704              * @param {Roo.form.Field} this
20705              * @param {Mixed} newValue The new value
20706              * @param {Mixed} oldValue The original value
20707              */
20708             change : true,
20709             /**
20710              * @event invalid
20711              * Fires after the field has been marked as invalid.
20712              * @param {Roo.form.Field} this
20713              * @param {String} msg The validation message
20714              */
20715             invalid : true,
20716             /**
20717              * @event valid
20718              * Fires after the field has been validated with no errors.
20719              * @param {Roo.form.Field} this
20720              */
20721             valid : true,
20722              /**
20723              * @event keyup
20724              * Fires after the key up
20725              * @param {Roo.form.Field} this
20726              * @param {Roo.EventObject}  e The event Object
20727              */
20728             keyup : true
20729         });
20730     },
20731
20732     /**
20733      * Returns the name attribute of the field if available
20734      * @return {String} name The field name
20735      */
20736     getName: function(){
20737          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20738     },
20739
20740     // private
20741     onRender : function(ct, position){
20742         Roo.form.Field.superclass.onRender.call(this, ct, position);
20743         if(!this.el){
20744             var cfg = this.getAutoCreate();
20745             if(!cfg.name){
20746                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20747             }
20748             if (!cfg.name.length) {
20749                 delete cfg.name;
20750             }
20751             if(this.inputType){
20752                 cfg.type = this.inputType;
20753             }
20754             this.el = ct.createChild(cfg, position);
20755         }
20756         var type = this.el.dom.type;
20757         if(type){
20758             if(type == 'password'){
20759                 type = 'text';
20760             }
20761             this.el.addClass('x-form-'+type);
20762         }
20763         if(this.readOnly){
20764             this.el.dom.readOnly = true;
20765         }
20766         if(this.tabIndex !== undefined){
20767             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20768         }
20769
20770         this.el.addClass([this.fieldClass, this.cls]);
20771         this.initValue();
20772     },
20773
20774     /**
20775      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20776      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20777      * @return {Roo.form.Field} this
20778      */
20779     applyTo : function(target){
20780         this.allowDomMove = false;
20781         this.el = Roo.get(target);
20782         this.render(this.el.dom.parentNode);
20783         return this;
20784     },
20785
20786     // private
20787     initValue : function(){
20788         if(this.value !== undefined){
20789             this.setValue(this.value);
20790         }else if(this.el.dom.value.length > 0){
20791             this.setValue(this.el.dom.value);
20792         }
20793     },
20794
20795     /**
20796      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20797      */
20798     isDirty : function() {
20799         if(this.disabled) {
20800             return false;
20801         }
20802         return String(this.getValue()) !== String(this.originalValue);
20803     },
20804
20805     // private
20806     afterRender : function(){
20807         Roo.form.Field.superclass.afterRender.call(this);
20808         this.initEvents();
20809     },
20810
20811     // private
20812     fireKey : function(e){
20813         //Roo.log('field ' + e.getKey());
20814         if(e.isNavKeyPress()){
20815             this.fireEvent("specialkey", this, e);
20816         }
20817     },
20818
20819     /**
20820      * Resets the current field value to the originally loaded value and clears any validation messages
20821      */
20822     reset : function(){
20823         this.setValue(this.resetValue);
20824         this.clearInvalid();
20825     },
20826
20827     // private
20828     initEvents : function(){
20829         // safari killled keypress - so keydown is now used..
20830         this.el.on("keydown" , this.fireKey,  this);
20831         this.el.on("focus", this.onFocus,  this);
20832         this.el.on("blur", this.onBlur,  this);
20833         this.el.relayEvent('keyup', this);
20834
20835         // reference to original value for reset
20836         this.originalValue = this.getValue();
20837         this.resetValue =  this.getValue();
20838     },
20839
20840     // private
20841     onFocus : function(){
20842         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20843             this.el.addClass(this.focusClass);
20844         }
20845         if(!this.hasFocus){
20846             this.hasFocus = true;
20847             this.startValue = this.getValue();
20848             this.fireEvent("focus", this);
20849         }
20850     },
20851
20852     beforeBlur : Roo.emptyFn,
20853
20854     // private
20855     onBlur : function(){
20856         this.beforeBlur();
20857         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20858             this.el.removeClass(this.focusClass);
20859         }
20860         this.hasFocus = false;
20861         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20862             this.validate();
20863         }
20864         var v = this.getValue();
20865         if(String(v) !== String(this.startValue)){
20866             this.fireEvent('change', this, v, this.startValue);
20867         }
20868         this.fireEvent("blur", this);
20869     },
20870
20871     /**
20872      * Returns whether or not the field value is currently valid
20873      * @param {Boolean} preventMark True to disable marking the field invalid
20874      * @return {Boolean} True if the value is valid, else false
20875      */
20876     isValid : function(preventMark){
20877         if(this.disabled){
20878             return true;
20879         }
20880         var restore = this.preventMark;
20881         this.preventMark = preventMark === true;
20882         var v = this.validateValue(this.processValue(this.getRawValue()));
20883         this.preventMark = restore;
20884         return v;
20885     },
20886
20887     /**
20888      * Validates the field value
20889      * @return {Boolean} True if the value is valid, else false
20890      */
20891     validate : function(){
20892         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20893             this.clearInvalid();
20894             return true;
20895         }
20896         return false;
20897     },
20898
20899     processValue : function(value){
20900         return value;
20901     },
20902
20903     // private
20904     // Subclasses should provide the validation implementation by overriding this
20905     validateValue : function(value){
20906         return true;
20907     },
20908
20909     /**
20910      * Mark this field as invalid
20911      * @param {String} msg The validation message
20912      */
20913     markInvalid : function(msg){
20914         if(!this.rendered || this.preventMark){ // not rendered
20915             return;
20916         }
20917         
20918         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20919         
20920         obj.el.addClass(this.invalidClass);
20921         msg = msg || this.invalidText;
20922         switch(this.msgTarget){
20923             case 'qtip':
20924                 obj.el.dom.qtip = msg;
20925                 obj.el.dom.qclass = 'x-form-invalid-tip';
20926                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20927                     Roo.QuickTips.enable();
20928                 }
20929                 break;
20930             case 'title':
20931                 this.el.dom.title = msg;
20932                 break;
20933             case 'under':
20934                 if(!this.errorEl){
20935                     var elp = this.el.findParent('.x-form-element', 5, true);
20936                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20937                     this.errorEl.setWidth(elp.getWidth(true)-20);
20938                 }
20939                 this.errorEl.update(msg);
20940                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20941                 break;
20942             case 'side':
20943                 if(!this.errorIcon){
20944                     var elp = this.el.findParent('.x-form-element', 5, true);
20945                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20946                 }
20947                 this.alignErrorIcon();
20948                 this.errorIcon.dom.qtip = msg;
20949                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20950                 this.errorIcon.show();
20951                 this.on('resize', this.alignErrorIcon, this);
20952                 break;
20953             default:
20954                 var t = Roo.getDom(this.msgTarget);
20955                 t.innerHTML = msg;
20956                 t.style.display = this.msgDisplay;
20957                 break;
20958         }
20959         this.fireEvent('invalid', this, msg);
20960     },
20961
20962     // private
20963     alignErrorIcon : function(){
20964         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20965     },
20966
20967     /**
20968      * Clear any invalid styles/messages for this field
20969      */
20970     clearInvalid : function(){
20971         if(!this.rendered || this.preventMark){ // not rendered
20972             return;
20973         }
20974         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20975         
20976         obj.el.removeClass(this.invalidClass);
20977         switch(this.msgTarget){
20978             case 'qtip':
20979                 obj.el.dom.qtip = '';
20980                 break;
20981             case 'title':
20982                 this.el.dom.title = '';
20983                 break;
20984             case 'under':
20985                 if(this.errorEl){
20986                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
20987                 }
20988                 break;
20989             case 'side':
20990                 if(this.errorIcon){
20991                     this.errorIcon.dom.qtip = '';
20992                     this.errorIcon.hide();
20993                     this.un('resize', this.alignErrorIcon, this);
20994                 }
20995                 break;
20996             default:
20997                 var t = Roo.getDom(this.msgTarget);
20998                 t.innerHTML = '';
20999                 t.style.display = 'none';
21000                 break;
21001         }
21002         this.fireEvent('valid', this);
21003     },
21004
21005     /**
21006      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21007      * @return {Mixed} value The field value
21008      */
21009     getRawValue : function(){
21010         var v = this.el.getValue();
21011         
21012         return v;
21013     },
21014
21015     /**
21016      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21017      * @return {Mixed} value The field value
21018      */
21019     getValue : function(){
21020         var v = this.el.getValue();
21021          
21022         return v;
21023     },
21024
21025     /**
21026      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21027      * @param {Mixed} value The value to set
21028      */
21029     setRawValue : function(v){
21030         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21031     },
21032
21033     /**
21034      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21035      * @param {Mixed} value The value to set
21036      */
21037     setValue : function(v){
21038         this.value = v;
21039         if(this.rendered){
21040             this.el.dom.value = (v === null || v === undefined ? '' : v);
21041              this.validate();
21042         }
21043     },
21044
21045     adjustSize : function(w, h){
21046         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21047         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21048         return s;
21049     },
21050
21051     adjustWidth : function(tag, w){
21052         tag = tag.toLowerCase();
21053         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21054             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21055                 if(tag == 'input'){
21056                     return w + 2;
21057                 }
21058                 if(tag == 'textarea'){
21059                     return w-2;
21060                 }
21061             }else if(Roo.isOpera){
21062                 if(tag == 'input'){
21063                     return w + 2;
21064                 }
21065                 if(tag == 'textarea'){
21066                     return w-2;
21067                 }
21068             }
21069         }
21070         return w;
21071     }
21072 });
21073
21074
21075 // anything other than normal should be considered experimental
21076 Roo.form.Field.msgFx = {
21077     normal : {
21078         show: function(msgEl, f){
21079             msgEl.setDisplayed('block');
21080         },
21081
21082         hide : function(msgEl, f){
21083             msgEl.setDisplayed(false).update('');
21084         }
21085     },
21086
21087     slide : {
21088         show: function(msgEl, f){
21089             msgEl.slideIn('t', {stopFx:true});
21090         },
21091
21092         hide : function(msgEl, f){
21093             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21094         }
21095     },
21096
21097     slideRight : {
21098         show: function(msgEl, f){
21099             msgEl.fixDisplay();
21100             msgEl.alignTo(f.el, 'tl-tr');
21101             msgEl.slideIn('l', {stopFx:true});
21102         },
21103
21104         hide : function(msgEl, f){
21105             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21106         }
21107     }
21108 };/*
21109  * Based on:
21110  * Ext JS Library 1.1.1
21111  * Copyright(c) 2006-2007, Ext JS, LLC.
21112  *
21113  * Originally Released Under LGPL - original licence link has changed is not relivant.
21114  *
21115  * Fork - LGPL
21116  * <script type="text/javascript">
21117  */
21118  
21119
21120 /**
21121  * @class Roo.form.TextField
21122  * @extends Roo.form.Field
21123  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21124  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21125  * @constructor
21126  * Creates a new TextField
21127  * @param {Object} config Configuration options
21128  */
21129 Roo.form.TextField = function(config){
21130     Roo.form.TextField.superclass.constructor.call(this, config);
21131     this.addEvents({
21132         /**
21133          * @event autosize
21134          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21135          * according to the default logic, but this event provides a hook for the developer to apply additional
21136          * logic at runtime to resize the field if needed.
21137              * @param {Roo.form.Field} this This text field
21138              * @param {Number} width The new field width
21139              */
21140         autosize : true
21141     });
21142 };
21143
21144 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21145     /**
21146      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21147      */
21148     grow : false,
21149     /**
21150      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21151      */
21152     growMin : 30,
21153     /**
21154      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21155      */
21156     growMax : 800,
21157     /**
21158      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21159      */
21160     vtype : null,
21161     /**
21162      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21163      */
21164     maskRe : null,
21165     /**
21166      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21167      */
21168     disableKeyFilter : false,
21169     /**
21170      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21171      */
21172     allowBlank : true,
21173     /**
21174      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21175      */
21176     minLength : 0,
21177     /**
21178      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21179      */
21180     maxLength : Number.MAX_VALUE,
21181     /**
21182      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21183      */
21184     minLengthText : "The minimum length for this field is {0}",
21185     /**
21186      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21187      */
21188     maxLengthText : "The maximum length for this field is {0}",
21189     /**
21190      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21191      */
21192     selectOnFocus : false,
21193     /**
21194      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21195      */
21196     blankText : "This field is required",
21197     /**
21198      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21199      * If available, this function will be called only after the basic validators all return true, and will be passed the
21200      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21201      */
21202     validator : null,
21203     /**
21204      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21205      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21206      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21207      */
21208     regex : null,
21209     /**
21210      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21211      */
21212     regexText : "",
21213     /**
21214      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21215      */
21216     emptyText : null,
21217    
21218
21219     // private
21220     initEvents : function()
21221     {
21222         if (this.emptyText) {
21223             this.el.attr('placeholder', this.emptyText);
21224         }
21225         
21226         Roo.form.TextField.superclass.initEvents.call(this);
21227         if(this.validationEvent == 'keyup'){
21228             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21229             this.el.on('keyup', this.filterValidation, this);
21230         }
21231         else if(this.validationEvent !== false){
21232             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21233         }
21234         
21235         if(this.selectOnFocus){
21236             this.on("focus", this.preFocus, this);
21237             
21238         }
21239         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21240             this.el.on("keypress", this.filterKeys, this);
21241         }
21242         if(this.grow){
21243             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21244             this.el.on("click", this.autoSize,  this);
21245         }
21246         if(this.el.is('input[type=password]') && Roo.isSafari){
21247             this.el.on('keydown', this.SafariOnKeyDown, this);
21248         }
21249     },
21250
21251     processValue : function(value){
21252         if(this.stripCharsRe){
21253             var newValue = value.replace(this.stripCharsRe, '');
21254             if(newValue !== value){
21255                 this.setRawValue(newValue);
21256                 return newValue;
21257             }
21258         }
21259         return value;
21260     },
21261
21262     filterValidation : function(e){
21263         if(!e.isNavKeyPress()){
21264             this.validationTask.delay(this.validationDelay);
21265         }
21266     },
21267
21268     // private
21269     onKeyUp : function(e){
21270         if(!e.isNavKeyPress()){
21271             this.autoSize();
21272         }
21273     },
21274
21275     /**
21276      * Resets the current field value to the originally-loaded value and clears any validation messages.
21277      *  
21278      */
21279     reset : function(){
21280         Roo.form.TextField.superclass.reset.call(this);
21281        
21282     },
21283
21284     
21285     // private
21286     preFocus : function(){
21287         
21288         if(this.selectOnFocus){
21289             this.el.dom.select();
21290         }
21291     },
21292
21293     
21294     // private
21295     filterKeys : function(e){
21296         var k = e.getKey();
21297         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21298             return;
21299         }
21300         var c = e.getCharCode(), cc = String.fromCharCode(c);
21301         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21302             return;
21303         }
21304         if(!this.maskRe.test(cc)){
21305             e.stopEvent();
21306         }
21307     },
21308
21309     setValue : function(v){
21310         
21311         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21312         
21313         this.autoSize();
21314     },
21315
21316     /**
21317      * Validates a value according to the field's validation rules and marks the field as invalid
21318      * if the validation fails
21319      * @param {Mixed} value The value to validate
21320      * @return {Boolean} True if the value is valid, else false
21321      */
21322     validateValue : function(value){
21323         if(value.length < 1)  { // if it's blank
21324              if(this.allowBlank){
21325                 this.clearInvalid();
21326                 return true;
21327              }else{
21328                 this.markInvalid(this.blankText);
21329                 return false;
21330              }
21331         }
21332         if(value.length < this.minLength){
21333             this.markInvalid(String.format(this.minLengthText, this.minLength));
21334             return false;
21335         }
21336         if(value.length > this.maxLength){
21337             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21338             return false;
21339         }
21340         if(this.vtype){
21341             var vt = Roo.form.VTypes;
21342             if(!vt[this.vtype](value, this)){
21343                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21344                 return false;
21345             }
21346         }
21347         if(typeof this.validator == "function"){
21348             var msg = this.validator(value);
21349             if(msg !== true){
21350                 this.markInvalid(msg);
21351                 return false;
21352             }
21353         }
21354         if(this.regex && !this.regex.test(value)){
21355             this.markInvalid(this.regexText);
21356             return false;
21357         }
21358         return true;
21359     },
21360
21361     /**
21362      * Selects text in this field
21363      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21364      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21365      */
21366     selectText : function(start, end){
21367         var v = this.getRawValue();
21368         if(v.length > 0){
21369             start = start === undefined ? 0 : start;
21370             end = end === undefined ? v.length : end;
21371             var d = this.el.dom;
21372             if(d.setSelectionRange){
21373                 d.setSelectionRange(start, end);
21374             }else if(d.createTextRange){
21375                 var range = d.createTextRange();
21376                 range.moveStart("character", start);
21377                 range.moveEnd("character", v.length-end);
21378                 range.select();
21379             }
21380         }
21381     },
21382
21383     /**
21384      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21385      * This only takes effect if grow = true, and fires the autosize event.
21386      */
21387     autoSize : function(){
21388         if(!this.grow || !this.rendered){
21389             return;
21390         }
21391         if(!this.metrics){
21392             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21393         }
21394         var el = this.el;
21395         var v = el.dom.value;
21396         var d = document.createElement('div');
21397         d.appendChild(document.createTextNode(v));
21398         v = d.innerHTML;
21399         d = null;
21400         v += "&#160;";
21401         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21402         this.el.setWidth(w);
21403         this.fireEvent("autosize", this, w);
21404     },
21405     
21406     // private
21407     SafariOnKeyDown : function(event)
21408     {
21409         // this is a workaround for a password hang bug on chrome/ webkit.
21410         
21411         var isSelectAll = false;
21412         
21413         if(this.el.dom.selectionEnd > 0){
21414             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21415         }
21416         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21417             event.preventDefault();
21418             this.setValue('');
21419             return;
21420         }
21421         
21422         if(isSelectAll){ // backspace and delete key
21423             
21424             event.preventDefault();
21425             // this is very hacky as keydown always get's upper case.
21426             //
21427             var cc = String.fromCharCode(event.getCharCode());
21428             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21429             
21430         }
21431         
21432         
21433     }
21434 });/*
21435  * Based on:
21436  * Ext JS Library 1.1.1
21437  * Copyright(c) 2006-2007, Ext JS, LLC.
21438  *
21439  * Originally Released Under LGPL - original licence link has changed is not relivant.
21440  *
21441  * Fork - LGPL
21442  * <script type="text/javascript">
21443  */
21444  
21445 /**
21446  * @class Roo.form.Hidden
21447  * @extends Roo.form.TextField
21448  * Simple Hidden element used on forms 
21449  * 
21450  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21451  * 
21452  * @constructor
21453  * Creates a new Hidden form element.
21454  * @param {Object} config Configuration options
21455  */
21456
21457
21458
21459 // easy hidden field...
21460 Roo.form.Hidden = function(config){
21461     Roo.form.Hidden.superclass.constructor.call(this, config);
21462 };
21463   
21464 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21465     fieldLabel:      '',
21466     inputType:      'hidden',
21467     width:          50,
21468     allowBlank:     true,
21469     labelSeparator: '',
21470     hidden:         true,
21471     itemCls :       'x-form-item-display-none'
21472
21473
21474 });
21475
21476
21477 /*
21478  * Based on:
21479  * Ext JS Library 1.1.1
21480  * Copyright(c) 2006-2007, Ext JS, LLC.
21481  *
21482  * Originally Released Under LGPL - original licence link has changed is not relivant.
21483  *
21484  * Fork - LGPL
21485  * <script type="text/javascript">
21486  */
21487  
21488 /**
21489  * @class Roo.form.TriggerField
21490  * @extends Roo.form.TextField
21491  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21492  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21493  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21494  * for which you can provide a custom implementation.  For example:
21495  * <pre><code>
21496 var trigger = new Roo.form.TriggerField();
21497 trigger.onTriggerClick = myTriggerFn;
21498 trigger.applyTo('my-field');
21499 </code></pre>
21500  *
21501  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21502  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21503  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21504  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21505  * @constructor
21506  * Create a new TriggerField.
21507  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21508  * to the base TextField)
21509  */
21510 Roo.form.TriggerField = function(config){
21511     this.mimicing = false;
21512     Roo.form.TriggerField.superclass.constructor.call(this, config);
21513 };
21514
21515 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21516     /**
21517      * @cfg {String} triggerClass A CSS class to apply to the trigger
21518      */
21519     /**
21520      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21521      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21522      */
21523     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21524     /**
21525      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21526      */
21527     hideTrigger:false,
21528
21529     /** @cfg {Boolean} grow @hide */
21530     /** @cfg {Number} growMin @hide */
21531     /** @cfg {Number} growMax @hide */
21532
21533     /**
21534      * @hide 
21535      * @method
21536      */
21537     autoSize: Roo.emptyFn,
21538     // private
21539     monitorTab : true,
21540     // private
21541     deferHeight : true,
21542
21543     
21544     actionMode : 'wrap',
21545     // private
21546     onResize : function(w, h){
21547         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21548         if(typeof w == 'number'){
21549             var x = w - this.trigger.getWidth();
21550             this.el.setWidth(this.adjustWidth('input', x));
21551             this.trigger.setStyle('left', x+'px');
21552         }
21553     },
21554
21555     // private
21556     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21557
21558     // private
21559     getResizeEl : function(){
21560         return this.wrap;
21561     },
21562
21563     // private
21564     getPositionEl : function(){
21565         return this.wrap;
21566     },
21567
21568     // private
21569     alignErrorIcon : function(){
21570         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21571     },
21572
21573     // private
21574     onRender : function(ct, position){
21575         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21576         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21577         this.trigger = this.wrap.createChild(this.triggerConfig ||
21578                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21579         if(this.hideTrigger){
21580             this.trigger.setDisplayed(false);
21581         }
21582         this.initTrigger();
21583         if(!this.width){
21584             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21585         }
21586     },
21587
21588     // private
21589     initTrigger : function(){
21590         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21591         this.trigger.addClassOnOver('x-form-trigger-over');
21592         this.trigger.addClassOnClick('x-form-trigger-click');
21593     },
21594
21595     // private
21596     onDestroy : function(){
21597         if(this.trigger){
21598             this.trigger.removeAllListeners();
21599             this.trigger.remove();
21600         }
21601         if(this.wrap){
21602             this.wrap.remove();
21603         }
21604         Roo.form.TriggerField.superclass.onDestroy.call(this);
21605     },
21606
21607     // private
21608     onFocus : function(){
21609         Roo.form.TriggerField.superclass.onFocus.call(this);
21610         if(!this.mimicing){
21611             this.wrap.addClass('x-trigger-wrap-focus');
21612             this.mimicing = true;
21613             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21614             if(this.monitorTab){
21615                 this.el.on("keydown", this.checkTab, this);
21616             }
21617         }
21618     },
21619
21620     // private
21621     checkTab : function(e){
21622         if(e.getKey() == e.TAB){
21623             this.triggerBlur();
21624         }
21625     },
21626
21627     // private
21628     onBlur : function(){
21629         // do nothing
21630     },
21631
21632     // private
21633     mimicBlur : function(e, t){
21634         if(!this.wrap.contains(t) && this.validateBlur()){
21635             this.triggerBlur();
21636         }
21637     },
21638
21639     // private
21640     triggerBlur : function(){
21641         this.mimicing = false;
21642         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21643         if(this.monitorTab){
21644             this.el.un("keydown", this.checkTab, this);
21645         }
21646         this.wrap.removeClass('x-trigger-wrap-focus');
21647         Roo.form.TriggerField.superclass.onBlur.call(this);
21648     },
21649
21650     // private
21651     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21652     validateBlur : function(e, t){
21653         return true;
21654     },
21655
21656     // private
21657     onDisable : function(){
21658         Roo.form.TriggerField.superclass.onDisable.call(this);
21659         if(this.wrap){
21660             this.wrap.addClass('x-item-disabled');
21661         }
21662     },
21663
21664     // private
21665     onEnable : function(){
21666         Roo.form.TriggerField.superclass.onEnable.call(this);
21667         if(this.wrap){
21668             this.wrap.removeClass('x-item-disabled');
21669         }
21670     },
21671
21672     // private
21673     onShow : function(){
21674         var ae = this.getActionEl();
21675         
21676         if(ae){
21677             ae.dom.style.display = '';
21678             ae.dom.style.visibility = 'visible';
21679         }
21680     },
21681
21682     // private
21683     
21684     onHide : function(){
21685         var ae = this.getActionEl();
21686         ae.dom.style.display = 'none';
21687     },
21688
21689     /**
21690      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21691      * by an implementing function.
21692      * @method
21693      * @param {EventObject} e
21694      */
21695     onTriggerClick : Roo.emptyFn
21696 });
21697
21698 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21699 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21700 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21701 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21702     initComponent : function(){
21703         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21704
21705         this.triggerConfig = {
21706             tag:'span', cls:'x-form-twin-triggers', cn:[
21707             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21708             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21709         ]};
21710     },
21711
21712     getTrigger : function(index){
21713         return this.triggers[index];
21714     },
21715
21716     initTrigger : function(){
21717         var ts = this.trigger.select('.x-form-trigger', true);
21718         this.wrap.setStyle('overflow', 'hidden');
21719         var triggerField = this;
21720         ts.each(function(t, all, index){
21721             t.hide = function(){
21722                 var w = triggerField.wrap.getWidth();
21723                 this.dom.style.display = 'none';
21724                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21725             };
21726             t.show = function(){
21727                 var w = triggerField.wrap.getWidth();
21728                 this.dom.style.display = '';
21729                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21730             };
21731             var triggerIndex = 'Trigger'+(index+1);
21732
21733             if(this['hide'+triggerIndex]){
21734                 t.dom.style.display = 'none';
21735             }
21736             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21737             t.addClassOnOver('x-form-trigger-over');
21738             t.addClassOnClick('x-form-trigger-click');
21739         }, this);
21740         this.triggers = ts.elements;
21741     },
21742
21743     onTrigger1Click : Roo.emptyFn,
21744     onTrigger2Click : Roo.emptyFn
21745 });/*
21746  * Based on:
21747  * Ext JS Library 1.1.1
21748  * Copyright(c) 2006-2007, Ext JS, LLC.
21749  *
21750  * Originally Released Under LGPL - original licence link has changed is not relivant.
21751  *
21752  * Fork - LGPL
21753  * <script type="text/javascript">
21754  */
21755  
21756 /**
21757  * @class Roo.form.TextArea
21758  * @extends Roo.form.TextField
21759  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21760  * support for auto-sizing.
21761  * @constructor
21762  * Creates a new TextArea
21763  * @param {Object} config Configuration options
21764  */
21765 Roo.form.TextArea = function(config){
21766     Roo.form.TextArea.superclass.constructor.call(this, config);
21767     // these are provided exchanges for backwards compat
21768     // minHeight/maxHeight were replaced by growMin/growMax to be
21769     // compatible with TextField growing config values
21770     if(this.minHeight !== undefined){
21771         this.growMin = this.minHeight;
21772     }
21773     if(this.maxHeight !== undefined){
21774         this.growMax = this.maxHeight;
21775     }
21776 };
21777
21778 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21779     /**
21780      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21781      */
21782     growMin : 60,
21783     /**
21784      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21785      */
21786     growMax: 1000,
21787     /**
21788      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21789      * in the field (equivalent to setting overflow: hidden, defaults to false)
21790      */
21791     preventScrollbars: false,
21792     /**
21793      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21794      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21795      */
21796
21797     // private
21798     onRender : function(ct, position){
21799         if(!this.el){
21800             this.defaultAutoCreate = {
21801                 tag: "textarea",
21802                 style:"width:300px;height:60px;",
21803                 autocomplete: "off"
21804             };
21805         }
21806         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21807         if(this.grow){
21808             this.textSizeEl = Roo.DomHelper.append(document.body, {
21809                 tag: "pre", cls: "x-form-grow-sizer"
21810             });
21811             if(this.preventScrollbars){
21812                 this.el.setStyle("overflow", "hidden");
21813             }
21814             this.el.setHeight(this.growMin);
21815         }
21816     },
21817
21818     onDestroy : function(){
21819         if(this.textSizeEl){
21820             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21821         }
21822         Roo.form.TextArea.superclass.onDestroy.call(this);
21823     },
21824
21825     // private
21826     onKeyUp : function(e){
21827         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21828             this.autoSize();
21829         }
21830     },
21831
21832     /**
21833      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21834      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21835      */
21836     autoSize : function(){
21837         if(!this.grow || !this.textSizeEl){
21838             return;
21839         }
21840         var el = this.el;
21841         var v = el.dom.value;
21842         var ts = this.textSizeEl;
21843
21844         ts.innerHTML = '';
21845         ts.appendChild(document.createTextNode(v));
21846         v = ts.innerHTML;
21847
21848         Roo.fly(ts).setWidth(this.el.getWidth());
21849         if(v.length < 1){
21850             v = "&#160;&#160;";
21851         }else{
21852             if(Roo.isIE){
21853                 v = v.replace(/\n/g, '<p>&#160;</p>');
21854             }
21855             v += "&#160;\n&#160;";
21856         }
21857         ts.innerHTML = v;
21858         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21859         if(h != this.lastHeight){
21860             this.lastHeight = h;
21861             this.el.setHeight(h);
21862             this.fireEvent("autosize", this, h);
21863         }
21864     }
21865 });/*
21866  * Based on:
21867  * Ext JS Library 1.1.1
21868  * Copyright(c) 2006-2007, Ext JS, LLC.
21869  *
21870  * Originally Released Under LGPL - original licence link has changed is not relivant.
21871  *
21872  * Fork - LGPL
21873  * <script type="text/javascript">
21874  */
21875  
21876
21877 /**
21878  * @class Roo.form.NumberField
21879  * @extends Roo.form.TextField
21880  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21881  * @constructor
21882  * Creates a new NumberField
21883  * @param {Object} config Configuration options
21884  */
21885 Roo.form.NumberField = function(config){
21886     Roo.form.NumberField.superclass.constructor.call(this, config);
21887 };
21888
21889 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21890     /**
21891      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21892      */
21893     fieldClass: "x-form-field x-form-num-field",
21894     /**
21895      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21896      */
21897     allowDecimals : true,
21898     /**
21899      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21900      */
21901     decimalSeparator : ".",
21902     /**
21903      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21904      */
21905     decimalPrecision : 2,
21906     /**
21907      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21908      */
21909     allowNegative : true,
21910     /**
21911      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21912      */
21913     minValue : Number.NEGATIVE_INFINITY,
21914     /**
21915      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21916      */
21917     maxValue : Number.MAX_VALUE,
21918     /**
21919      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21920      */
21921     minText : "The minimum value for this field is {0}",
21922     /**
21923      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21924      */
21925     maxText : "The maximum value for this field is {0}",
21926     /**
21927      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21928      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21929      */
21930     nanText : "{0} is not a valid number",
21931
21932     // private
21933     initEvents : function(){
21934         Roo.form.NumberField.superclass.initEvents.call(this);
21935         var allowed = "0123456789";
21936         if(this.allowDecimals){
21937             allowed += this.decimalSeparator;
21938         }
21939         if(this.allowNegative){
21940             allowed += "-";
21941         }
21942         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21943         var keyPress = function(e){
21944             var k = e.getKey();
21945             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21946                 return;
21947             }
21948             var c = e.getCharCode();
21949             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21950                 e.stopEvent();
21951             }
21952         };
21953         this.el.on("keypress", keyPress, this);
21954     },
21955
21956     // private
21957     validateValue : function(value){
21958         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21959             return false;
21960         }
21961         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21962              return true;
21963         }
21964         var num = this.parseValue(value);
21965         if(isNaN(num)){
21966             this.markInvalid(String.format(this.nanText, value));
21967             return false;
21968         }
21969         if(num < this.minValue){
21970             this.markInvalid(String.format(this.minText, this.minValue));
21971             return false;
21972         }
21973         if(num > this.maxValue){
21974             this.markInvalid(String.format(this.maxText, this.maxValue));
21975             return false;
21976         }
21977         return true;
21978     },
21979
21980     getValue : function(){
21981         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
21982     },
21983
21984     // private
21985     parseValue : function(value){
21986         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
21987         return isNaN(value) ? '' : value;
21988     },
21989
21990     // private
21991     fixPrecision : function(value){
21992         var nan = isNaN(value);
21993         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
21994             return nan ? '' : value;
21995         }
21996         return parseFloat(value).toFixed(this.decimalPrecision);
21997     },
21998
21999     setValue : function(v){
22000         v = this.fixPrecision(v);
22001         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22002     },
22003
22004     // private
22005     decimalPrecisionFcn : function(v){
22006         return Math.floor(v);
22007     },
22008
22009     beforeBlur : function(){
22010         var v = this.parseValue(this.getRawValue());
22011         if(v){
22012             this.setValue(v);
22013         }
22014     }
22015 });/*
22016  * Based on:
22017  * Ext JS Library 1.1.1
22018  * Copyright(c) 2006-2007, Ext JS, LLC.
22019  *
22020  * Originally Released Under LGPL - original licence link has changed is not relivant.
22021  *
22022  * Fork - LGPL
22023  * <script type="text/javascript">
22024  */
22025  
22026 /**
22027  * @class Roo.form.DateField
22028  * @extends Roo.form.TriggerField
22029  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22030 * @constructor
22031 * Create a new DateField
22032 * @param {Object} config
22033  */
22034 Roo.form.DateField = function(config){
22035     Roo.form.DateField.superclass.constructor.call(this, config);
22036     
22037       this.addEvents({
22038          
22039         /**
22040          * @event select
22041          * Fires when a date is selected
22042              * @param {Roo.form.DateField} combo This combo box
22043              * @param {Date} date The date selected
22044              */
22045         'select' : true
22046          
22047     });
22048     
22049     
22050     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22051     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22052     this.ddMatch = null;
22053     if(this.disabledDates){
22054         var dd = this.disabledDates;
22055         var re = "(?:";
22056         for(var i = 0; i < dd.length; i++){
22057             re += dd[i];
22058             if(i != dd.length-1) re += "|";
22059         }
22060         this.ddMatch = new RegExp(re + ")");
22061     }
22062 };
22063
22064 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22065     /**
22066      * @cfg {String} format
22067      * The default date format string which can be overriden for localization support.  The format must be
22068      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22069      */
22070     format : "m/d/y",
22071     /**
22072      * @cfg {String} altFormats
22073      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22074      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22075      */
22076     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22077     /**
22078      * @cfg {Array} disabledDays
22079      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22080      */
22081     disabledDays : null,
22082     /**
22083      * @cfg {String} disabledDaysText
22084      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22085      */
22086     disabledDaysText : "Disabled",
22087     /**
22088      * @cfg {Array} disabledDates
22089      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22090      * expression so they are very powerful. Some examples:
22091      * <ul>
22092      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22093      * <li>["03/08", "09/16"] would disable those days for every year</li>
22094      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22095      * <li>["03/../2006"] would disable every day in March 2006</li>
22096      * <li>["^03"] would disable every day in every March</li>
22097      * </ul>
22098      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22099      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22100      */
22101     disabledDates : null,
22102     /**
22103      * @cfg {String} disabledDatesText
22104      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22105      */
22106     disabledDatesText : "Disabled",
22107     /**
22108      * @cfg {Date/String} minValue
22109      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22110      * valid format (defaults to null).
22111      */
22112     minValue : null,
22113     /**
22114      * @cfg {Date/String} maxValue
22115      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22116      * valid format (defaults to null).
22117      */
22118     maxValue : null,
22119     /**
22120      * @cfg {String} minText
22121      * The error text to display when the date in the cell is before minValue (defaults to
22122      * 'The date in this field must be after {minValue}').
22123      */
22124     minText : "The date in this field must be equal to or after {0}",
22125     /**
22126      * @cfg {String} maxText
22127      * The error text to display when the date in the cell is after maxValue (defaults to
22128      * 'The date in this field must be before {maxValue}').
22129      */
22130     maxText : "The date in this field must be equal to or before {0}",
22131     /**
22132      * @cfg {String} invalidText
22133      * The error text to display when the date in the field is invalid (defaults to
22134      * '{value} is not a valid date - it must be in the format {format}').
22135      */
22136     invalidText : "{0} is not a valid date - it must be in the format {1}",
22137     /**
22138      * @cfg {String} triggerClass
22139      * An additional CSS class used to style the trigger button.  The trigger will always get the
22140      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22141      * which displays a calendar icon).
22142      */
22143     triggerClass : 'x-form-date-trigger',
22144     
22145
22146     /**
22147      * @cfg {Boolean} useIso
22148      * if enabled, then the date field will use a hidden field to store the 
22149      * real value as iso formated date. default (false)
22150      */ 
22151     useIso : false,
22152     /**
22153      * @cfg {String/Object} autoCreate
22154      * A DomHelper element spec, or true for a default element spec (defaults to
22155      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22156      */ 
22157     // private
22158     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22159     
22160     // private
22161     hiddenField: false,
22162     
22163     onRender : function(ct, position)
22164     {
22165         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22166         if (this.useIso) {
22167             //this.el.dom.removeAttribute('name'); 
22168             Roo.log("Changing name?");
22169             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22170             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22171                     'before', true);
22172             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22173             // prevent input submission
22174             this.hiddenName = this.name;
22175         }
22176             
22177             
22178     },
22179     
22180     // private
22181     validateValue : function(value)
22182     {
22183         value = this.formatDate(value);
22184         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22185             Roo.log('super failed');
22186             return false;
22187         }
22188         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22189              return true;
22190         }
22191         var svalue = value;
22192         value = this.parseDate(value);
22193         if(!value){
22194             Roo.log('parse date failed' + svalue);
22195             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22196             return false;
22197         }
22198         var time = value.getTime();
22199         if(this.minValue && time < this.minValue.getTime()){
22200             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22201             return false;
22202         }
22203         if(this.maxValue && time > this.maxValue.getTime()){
22204             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22205             return false;
22206         }
22207         if(this.disabledDays){
22208             var day = value.getDay();
22209             for(var i = 0; i < this.disabledDays.length; i++) {
22210                 if(day === this.disabledDays[i]){
22211                     this.markInvalid(this.disabledDaysText);
22212                     return false;
22213                 }
22214             }
22215         }
22216         var fvalue = this.formatDate(value);
22217         if(this.ddMatch && this.ddMatch.test(fvalue)){
22218             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22219             return false;
22220         }
22221         return true;
22222     },
22223
22224     // private
22225     // Provides logic to override the default TriggerField.validateBlur which just returns true
22226     validateBlur : function(){
22227         return !this.menu || !this.menu.isVisible();
22228     },
22229     
22230     getName: function()
22231     {
22232         // returns hidden if it's set..
22233         if (!this.rendered) {return ''};
22234         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22235         
22236     },
22237
22238     /**
22239      * Returns the current date value of the date field.
22240      * @return {Date} The date value
22241      */
22242     getValue : function(){
22243         
22244         return  this.hiddenField ?
22245                 this.hiddenField.value :
22246                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22247     },
22248
22249     /**
22250      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22251      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22252      * (the default format used is "m/d/y").
22253      * <br />Usage:
22254      * <pre><code>
22255 //All of these calls set the same date value (May 4, 2006)
22256
22257 //Pass a date object:
22258 var dt = new Date('5/4/06');
22259 dateField.setValue(dt);
22260
22261 //Pass a date string (default format):
22262 dateField.setValue('5/4/06');
22263
22264 //Pass a date string (custom format):
22265 dateField.format = 'Y-m-d';
22266 dateField.setValue('2006-5-4');
22267 </code></pre>
22268      * @param {String/Date} date The date or valid date string
22269      */
22270     setValue : function(date){
22271         if (this.hiddenField) {
22272             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22273         }
22274         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22275         // make sure the value field is always stored as a date..
22276         this.value = this.parseDate(date);
22277         
22278         
22279     },
22280
22281     // private
22282     parseDate : function(value){
22283         if(!value || value instanceof Date){
22284             return value;
22285         }
22286         var v = Date.parseDate(value, this.format);
22287          if (!v && this.useIso) {
22288             v = Date.parseDate(value, 'Y-m-d');
22289         }
22290         if(!v && this.altFormats){
22291             if(!this.altFormatsArray){
22292                 this.altFormatsArray = this.altFormats.split("|");
22293             }
22294             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22295                 v = Date.parseDate(value, this.altFormatsArray[i]);
22296             }
22297         }
22298         return v;
22299     },
22300
22301     // private
22302     formatDate : function(date, fmt){
22303         return (!date || !(date instanceof Date)) ?
22304                date : date.dateFormat(fmt || this.format);
22305     },
22306
22307     // private
22308     menuListeners : {
22309         select: function(m, d){
22310             
22311             this.setValue(d);
22312             this.fireEvent('select', this, d);
22313         },
22314         show : function(){ // retain focus styling
22315             this.onFocus();
22316         },
22317         hide : function(){
22318             this.focus.defer(10, this);
22319             var ml = this.menuListeners;
22320             this.menu.un("select", ml.select,  this);
22321             this.menu.un("show", ml.show,  this);
22322             this.menu.un("hide", ml.hide,  this);
22323         }
22324     },
22325
22326     // private
22327     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22328     onTriggerClick : function(){
22329         if(this.disabled){
22330             return;
22331         }
22332         if(this.menu == null){
22333             this.menu = new Roo.menu.DateMenu();
22334         }
22335         Roo.apply(this.menu.picker,  {
22336             showClear: this.allowBlank,
22337             minDate : this.minValue,
22338             maxDate : this.maxValue,
22339             disabledDatesRE : this.ddMatch,
22340             disabledDatesText : this.disabledDatesText,
22341             disabledDays : this.disabledDays,
22342             disabledDaysText : this.disabledDaysText,
22343             format : this.useIso ? 'Y-m-d' : this.format,
22344             minText : String.format(this.minText, this.formatDate(this.minValue)),
22345             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22346         });
22347         this.menu.on(Roo.apply({}, this.menuListeners, {
22348             scope:this
22349         }));
22350         this.menu.picker.setValue(this.getValue() || new Date());
22351         this.menu.show(this.el, "tl-bl?");
22352     },
22353
22354     beforeBlur : function(){
22355         var v = this.parseDate(this.getRawValue());
22356         if(v){
22357             this.setValue(v);
22358         }
22359     },
22360
22361     /*@
22362      * overide
22363      * 
22364      */
22365     isDirty : function() {
22366         if(this.disabled) {
22367             return false;
22368         }
22369         
22370         if(typeof(this.startValue) === 'undefined'){
22371             return false;
22372         }
22373         
22374         return String(this.getValue()) !== String(this.startValue);
22375         
22376     }
22377 });/*
22378  * Based on:
22379  * Ext JS Library 1.1.1
22380  * Copyright(c) 2006-2007, Ext JS, LLC.
22381  *
22382  * Originally Released Under LGPL - original licence link has changed is not relivant.
22383  *
22384  * Fork - LGPL
22385  * <script type="text/javascript">
22386  */
22387  
22388 /**
22389  * @class Roo.form.MonthField
22390  * @extends Roo.form.TriggerField
22391  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22392 * @constructor
22393 * Create a new MonthField
22394 * @param {Object} config
22395  */
22396 Roo.form.MonthField = function(config){
22397     
22398     Roo.form.MonthField.superclass.constructor.call(this, config);
22399     
22400       this.addEvents({
22401          
22402         /**
22403          * @event select
22404          * Fires when a date is selected
22405              * @param {Roo.form.MonthFieeld} combo This combo box
22406              * @param {Date} date The date selected
22407              */
22408         'select' : true
22409          
22410     });
22411     
22412     
22413     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22414     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22415     this.ddMatch = null;
22416     if(this.disabledDates){
22417         var dd = this.disabledDates;
22418         var re = "(?:";
22419         for(var i = 0; i < dd.length; i++){
22420             re += dd[i];
22421             if(i != dd.length-1) re += "|";
22422         }
22423         this.ddMatch = new RegExp(re + ")");
22424     }
22425 };
22426
22427 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22428     /**
22429      * @cfg {String} format
22430      * The default date format string which can be overriden for localization support.  The format must be
22431      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22432      */
22433     format : "M Y",
22434     /**
22435      * @cfg {String} altFormats
22436      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22437      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22438      */
22439     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22440     /**
22441      * @cfg {Array} disabledDays
22442      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22443      */
22444     disabledDays : [0,1,2,3,4,5,6],
22445     /**
22446      * @cfg {String} disabledDaysText
22447      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22448      */
22449     disabledDaysText : "Disabled",
22450     /**
22451      * @cfg {Array} disabledDates
22452      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22453      * expression so they are very powerful. Some examples:
22454      * <ul>
22455      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22456      * <li>["03/08", "09/16"] would disable those days for every year</li>
22457      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22458      * <li>["03/../2006"] would disable every day in March 2006</li>
22459      * <li>["^03"] would disable every day in every March</li>
22460      * </ul>
22461      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22462      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22463      */
22464     disabledDates : null,
22465     /**
22466      * @cfg {String} disabledDatesText
22467      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22468      */
22469     disabledDatesText : "Disabled",
22470     /**
22471      * @cfg {Date/String} minValue
22472      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22473      * valid format (defaults to null).
22474      */
22475     minValue : null,
22476     /**
22477      * @cfg {Date/String} maxValue
22478      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22479      * valid format (defaults to null).
22480      */
22481     maxValue : null,
22482     /**
22483      * @cfg {String} minText
22484      * The error text to display when the date in the cell is before minValue (defaults to
22485      * 'The date in this field must be after {minValue}').
22486      */
22487     minText : "The date in this field must be equal to or after {0}",
22488     /**
22489      * @cfg {String} maxTextf
22490      * The error text to display when the date in the cell is after maxValue (defaults to
22491      * 'The date in this field must be before {maxValue}').
22492      */
22493     maxText : "The date in this field must be equal to or before {0}",
22494     /**
22495      * @cfg {String} invalidText
22496      * The error text to display when the date in the field is invalid (defaults to
22497      * '{value} is not a valid date - it must be in the format {format}').
22498      */
22499     invalidText : "{0} is not a valid date - it must be in the format {1}",
22500     /**
22501      * @cfg {String} triggerClass
22502      * An additional CSS class used to style the trigger button.  The trigger will always get the
22503      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22504      * which displays a calendar icon).
22505      */
22506     triggerClass : 'x-form-date-trigger',
22507     
22508
22509     /**
22510      * @cfg {Boolean} useIso
22511      * if enabled, then the date field will use a hidden field to store the 
22512      * real value as iso formated date. default (true)
22513      */ 
22514     useIso : true,
22515     /**
22516      * @cfg {String/Object} autoCreate
22517      * A DomHelper element spec, or true for a default element spec (defaults to
22518      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22519      */ 
22520     // private
22521     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22522     
22523     // private
22524     hiddenField: false,
22525     
22526     hideMonthPicker : false,
22527     
22528     onRender : function(ct, position)
22529     {
22530         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22531         if (this.useIso) {
22532             this.el.dom.removeAttribute('name'); 
22533             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22534                     'before', true);
22535             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22536             // prevent input submission
22537             this.hiddenName = this.name;
22538         }
22539             
22540             
22541     },
22542     
22543     // private
22544     validateValue : function(value)
22545     {
22546         value = this.formatDate(value);
22547         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22548             return false;
22549         }
22550         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22551              return true;
22552         }
22553         var svalue = value;
22554         value = this.parseDate(value);
22555         if(!value){
22556             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22557             return false;
22558         }
22559         var time = value.getTime();
22560         if(this.minValue && time < this.minValue.getTime()){
22561             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22562             return false;
22563         }
22564         if(this.maxValue && time > this.maxValue.getTime()){
22565             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22566             return false;
22567         }
22568         /*if(this.disabledDays){
22569             var day = value.getDay();
22570             for(var i = 0; i < this.disabledDays.length; i++) {
22571                 if(day === this.disabledDays[i]){
22572                     this.markInvalid(this.disabledDaysText);
22573                     return false;
22574                 }
22575             }
22576         }
22577         */
22578         var fvalue = this.formatDate(value);
22579         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22580             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22581             return false;
22582         }
22583         */
22584         return true;
22585     },
22586
22587     // private
22588     // Provides logic to override the default TriggerField.validateBlur which just returns true
22589     validateBlur : function(){
22590         return !this.menu || !this.menu.isVisible();
22591     },
22592
22593     /**
22594      * Returns the current date value of the date field.
22595      * @return {Date} The date value
22596      */
22597     getValue : function(){
22598         
22599         
22600         
22601         return  this.hiddenField ?
22602                 this.hiddenField.value :
22603                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22604     },
22605
22606     /**
22607      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22608      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22609      * (the default format used is "m/d/y").
22610      * <br />Usage:
22611      * <pre><code>
22612 //All of these calls set the same date value (May 4, 2006)
22613
22614 //Pass a date object:
22615 var dt = new Date('5/4/06');
22616 monthField.setValue(dt);
22617
22618 //Pass a date string (default format):
22619 monthField.setValue('5/4/06');
22620
22621 //Pass a date string (custom format):
22622 monthField.format = 'Y-m-d';
22623 monthField.setValue('2006-5-4');
22624 </code></pre>
22625      * @param {String/Date} date The date or valid date string
22626      */
22627     setValue : function(date){
22628         Roo.log('month setValue' + date);
22629         // can only be first of month..
22630         
22631         var val = this.parseDate(date);
22632         
22633         if (this.hiddenField) {
22634             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22635         }
22636         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22637         this.value = this.parseDate(date);
22638     },
22639
22640     // private
22641     parseDate : function(value){
22642         if(!value || value instanceof Date){
22643             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22644             return value;
22645         }
22646         var v = Date.parseDate(value, this.format);
22647         if (!v && this.useIso) {
22648             v = Date.parseDate(value, 'Y-m-d');
22649         }
22650         if (v) {
22651             // 
22652             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22653         }
22654         
22655         
22656         if(!v && this.altFormats){
22657             if(!this.altFormatsArray){
22658                 this.altFormatsArray = this.altFormats.split("|");
22659             }
22660             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22661                 v = Date.parseDate(value, this.altFormatsArray[i]);
22662             }
22663         }
22664         return v;
22665     },
22666
22667     // private
22668     formatDate : function(date, fmt){
22669         return (!date || !(date instanceof Date)) ?
22670                date : date.dateFormat(fmt || this.format);
22671     },
22672
22673     // private
22674     menuListeners : {
22675         select: function(m, d){
22676             this.setValue(d);
22677             this.fireEvent('select', this, d);
22678         },
22679         show : function(){ // retain focus styling
22680             this.onFocus();
22681         },
22682         hide : function(){
22683             this.focus.defer(10, this);
22684             var ml = this.menuListeners;
22685             this.menu.un("select", ml.select,  this);
22686             this.menu.un("show", ml.show,  this);
22687             this.menu.un("hide", ml.hide,  this);
22688         }
22689     },
22690     // private
22691     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22692     onTriggerClick : function(){
22693         if(this.disabled){
22694             return;
22695         }
22696         if(this.menu == null){
22697             this.menu = new Roo.menu.DateMenu();
22698            
22699         }
22700         
22701         Roo.apply(this.menu.picker,  {
22702             
22703             showClear: this.allowBlank,
22704             minDate : this.minValue,
22705             maxDate : this.maxValue,
22706             disabledDatesRE : this.ddMatch,
22707             disabledDatesText : this.disabledDatesText,
22708             
22709             format : this.useIso ? 'Y-m-d' : this.format,
22710             minText : String.format(this.minText, this.formatDate(this.minValue)),
22711             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22712             
22713         });
22714          this.menu.on(Roo.apply({}, this.menuListeners, {
22715             scope:this
22716         }));
22717        
22718         
22719         var m = this.menu;
22720         var p = m.picker;
22721         
22722         // hide month picker get's called when we called by 'before hide';
22723         
22724         var ignorehide = true;
22725         p.hideMonthPicker  = function(disableAnim){
22726             if (ignorehide) {
22727                 return;
22728             }
22729              if(this.monthPicker){
22730                 Roo.log("hideMonthPicker called");
22731                 if(disableAnim === true){
22732                     this.monthPicker.hide();
22733                 }else{
22734                     this.monthPicker.slideOut('t', {duration:.2});
22735                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22736                     p.fireEvent("select", this, this.value);
22737                     m.hide();
22738                 }
22739             }
22740         }
22741         
22742         Roo.log('picker set value');
22743         Roo.log(this.getValue());
22744         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22745         m.show(this.el, 'tl-bl?');
22746         ignorehide  = false;
22747         // this will trigger hideMonthPicker..
22748         
22749         
22750         // hidden the day picker
22751         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22752         
22753         
22754         
22755       
22756         
22757         p.showMonthPicker.defer(100, p);
22758     
22759         
22760        
22761     },
22762
22763     beforeBlur : function(){
22764         var v = this.parseDate(this.getRawValue());
22765         if(v){
22766             this.setValue(v);
22767         }
22768     }
22769
22770     /** @cfg {Boolean} grow @hide */
22771     /** @cfg {Number} growMin @hide */
22772     /** @cfg {Number} growMax @hide */
22773     /**
22774      * @hide
22775      * @method autoSize
22776      */
22777 });/*
22778  * Based on:
22779  * Ext JS Library 1.1.1
22780  * Copyright(c) 2006-2007, Ext JS, LLC.
22781  *
22782  * Originally Released Under LGPL - original licence link has changed is not relivant.
22783  *
22784  * Fork - LGPL
22785  * <script type="text/javascript">
22786  */
22787  
22788
22789 /**
22790  * @class Roo.form.ComboBox
22791  * @extends Roo.form.TriggerField
22792  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22793  * @constructor
22794  * Create a new ComboBox.
22795  * @param {Object} config Configuration options
22796  */
22797 Roo.form.ComboBox = function(config){
22798     Roo.form.ComboBox.superclass.constructor.call(this, config);
22799     this.addEvents({
22800         /**
22801          * @event expand
22802          * Fires when the dropdown list is expanded
22803              * @param {Roo.form.ComboBox} combo This combo box
22804              */
22805         'expand' : true,
22806         /**
22807          * @event collapse
22808          * Fires when the dropdown list is collapsed
22809              * @param {Roo.form.ComboBox} combo This combo box
22810              */
22811         'collapse' : true,
22812         /**
22813          * @event beforeselect
22814          * Fires before a list item is selected. Return false to cancel the selection.
22815              * @param {Roo.form.ComboBox} combo This combo box
22816              * @param {Roo.data.Record} record The data record returned from the underlying store
22817              * @param {Number} index The index of the selected item in the dropdown list
22818              */
22819         'beforeselect' : true,
22820         /**
22821          * @event select
22822          * Fires when a list item is selected
22823              * @param {Roo.form.ComboBox} combo This combo box
22824              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22825              * @param {Number} index The index of the selected item in the dropdown list
22826              */
22827         'select' : true,
22828         /**
22829          * @event beforequery
22830          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22831          * The event object passed has these properties:
22832              * @param {Roo.form.ComboBox} combo This combo box
22833              * @param {String} query The query
22834              * @param {Boolean} forceAll true to force "all" query
22835              * @param {Boolean} cancel true to cancel the query
22836              * @param {Object} e The query event object
22837              */
22838         'beforequery': true,
22839          /**
22840          * @event add
22841          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22842              * @param {Roo.form.ComboBox} combo This combo box
22843              */
22844         'add' : true,
22845         /**
22846          * @event edit
22847          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22848              * @param {Roo.form.ComboBox} combo This combo box
22849              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22850              */
22851         'edit' : true
22852         
22853         
22854     });
22855     if(this.transform){
22856         this.allowDomMove = false;
22857         var s = Roo.getDom(this.transform);
22858         if(!this.hiddenName){
22859             this.hiddenName = s.name;
22860         }
22861         if(!this.store){
22862             this.mode = 'local';
22863             var d = [], opts = s.options;
22864             for(var i = 0, len = opts.length;i < len; i++){
22865                 var o = opts[i];
22866                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22867                 if(o.selected) {
22868                     this.value = value;
22869                 }
22870                 d.push([value, o.text]);
22871             }
22872             this.store = new Roo.data.SimpleStore({
22873                 'id': 0,
22874                 fields: ['value', 'text'],
22875                 data : d
22876             });
22877             this.valueField = 'value';
22878             this.displayField = 'text';
22879         }
22880         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22881         if(!this.lazyRender){
22882             this.target = true;
22883             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22884             s.parentNode.removeChild(s); // remove it
22885             this.render(this.el.parentNode);
22886         }else{
22887             s.parentNode.removeChild(s); // remove it
22888         }
22889
22890     }
22891     if (this.store) {
22892         this.store = Roo.factory(this.store, Roo.data);
22893     }
22894     
22895     this.selectedIndex = -1;
22896     if(this.mode == 'local'){
22897         if(config.queryDelay === undefined){
22898             this.queryDelay = 10;
22899         }
22900         if(config.minChars === undefined){
22901             this.minChars = 0;
22902         }
22903     }
22904 };
22905
22906 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22907     /**
22908      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22909      */
22910     /**
22911      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22912      * rendering into an Roo.Editor, defaults to false)
22913      */
22914     /**
22915      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22916      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22917      */
22918     /**
22919      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22920      */
22921     /**
22922      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22923      * the dropdown list (defaults to undefined, with no header element)
22924      */
22925
22926      /**
22927      * @cfg {String/Roo.Template} tpl The template to use to render the output
22928      */
22929      
22930     // private
22931     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22932     /**
22933      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22934      */
22935     listWidth: undefined,
22936     /**
22937      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22938      * mode = 'remote' or 'text' if mode = 'local')
22939      */
22940     displayField: undefined,
22941     /**
22942      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22943      * mode = 'remote' or 'value' if mode = 'local'). 
22944      * Note: use of a valueField requires the user make a selection
22945      * in order for a value to be mapped.
22946      */
22947     valueField: undefined,
22948     
22949     
22950     /**
22951      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22952      * field's data value (defaults to the underlying DOM element's name)
22953      */
22954     hiddenName: undefined,
22955     /**
22956      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22957      */
22958     listClass: '',
22959     /**
22960      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22961      */
22962     selectedClass: 'x-combo-selected',
22963     /**
22964      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22965      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22966      * which displays a downward arrow icon).
22967      */
22968     triggerClass : 'x-form-arrow-trigger',
22969     /**
22970      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22971      */
22972     shadow:'sides',
22973     /**
22974      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22975      * anchor positions (defaults to 'tl-bl')
22976      */
22977     listAlign: 'tl-bl?',
22978     /**
22979      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22980      */
22981     maxHeight: 300,
22982     /**
22983      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22984      * query specified by the allQuery config option (defaults to 'query')
22985      */
22986     triggerAction: 'query',
22987     /**
22988      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22989      * (defaults to 4, does not apply if editable = false)
22990      */
22991     minChars : 4,
22992     /**
22993      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22994      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22995      */
22996     typeAhead: false,
22997     /**
22998      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22999      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23000      */
23001     queryDelay: 500,
23002     /**
23003      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23004      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23005      */
23006     pageSize: 0,
23007     /**
23008      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23009      * when editable = true (defaults to false)
23010      */
23011     selectOnFocus:false,
23012     /**
23013      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23014      */
23015     queryParam: 'query',
23016     /**
23017      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23018      * when mode = 'remote' (defaults to 'Loading...')
23019      */
23020     loadingText: 'Loading...',
23021     /**
23022      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23023      */
23024     resizable: false,
23025     /**
23026      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23027      */
23028     handleHeight : 8,
23029     /**
23030      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23031      * traditional select (defaults to true)
23032      */
23033     editable: true,
23034     /**
23035      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23036      */
23037     allQuery: '',
23038     /**
23039      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23040      */
23041     mode: 'remote',
23042     /**
23043      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23044      * listWidth has a higher value)
23045      */
23046     minListWidth : 70,
23047     /**
23048      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23049      * allow the user to set arbitrary text into the field (defaults to false)
23050      */
23051     forceSelection:false,
23052     /**
23053      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23054      * if typeAhead = true (defaults to 250)
23055      */
23056     typeAheadDelay : 250,
23057     /**
23058      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23059      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23060      */
23061     valueNotFoundText : undefined,
23062     /**
23063      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23064      */
23065     blockFocus : false,
23066     
23067     /**
23068      * @cfg {Boolean} disableClear Disable showing of clear button.
23069      */
23070     disableClear : false,
23071     /**
23072      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23073      */
23074     alwaysQuery : false,
23075     
23076     //private
23077     addicon : false,
23078     editicon: false,
23079     
23080     // element that contains real text value.. (when hidden is used..)
23081      
23082     // private
23083     onRender : function(ct, position){
23084         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23085         if(this.hiddenName){
23086             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23087                     'before', true);
23088             this.hiddenField.value =
23089                 this.hiddenValue !== undefined ? this.hiddenValue :
23090                 this.value !== undefined ? this.value : '';
23091
23092             // prevent input submission
23093             this.el.dom.removeAttribute('name');
23094              
23095              
23096         }
23097         if(Roo.isGecko){
23098             this.el.dom.setAttribute('autocomplete', 'off');
23099         }
23100
23101         var cls = 'x-combo-list';
23102
23103         this.list = new Roo.Layer({
23104             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23105         });
23106
23107         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23108         this.list.setWidth(lw);
23109         this.list.swallowEvent('mousewheel');
23110         this.assetHeight = 0;
23111
23112         if(this.title){
23113             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23114             this.assetHeight += this.header.getHeight();
23115         }
23116
23117         this.innerList = this.list.createChild({cls:cls+'-inner'});
23118         this.innerList.on('mouseover', this.onViewOver, this);
23119         this.innerList.on('mousemove', this.onViewMove, this);
23120         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23121         
23122         if(this.allowBlank && !this.pageSize && !this.disableClear){
23123             this.footer = this.list.createChild({cls:cls+'-ft'});
23124             this.pageTb = new Roo.Toolbar(this.footer);
23125            
23126         }
23127         if(this.pageSize){
23128             this.footer = this.list.createChild({cls:cls+'-ft'});
23129             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23130                     {pageSize: this.pageSize});
23131             
23132         }
23133         
23134         if (this.pageTb && this.allowBlank && !this.disableClear) {
23135             var _this = this;
23136             this.pageTb.add(new Roo.Toolbar.Fill(), {
23137                 cls: 'x-btn-icon x-btn-clear',
23138                 text: '&#160;',
23139                 handler: function()
23140                 {
23141                     _this.collapse();
23142                     _this.clearValue();
23143                     _this.onSelect(false, -1);
23144                 }
23145             });
23146         }
23147         if (this.footer) {
23148             this.assetHeight += this.footer.getHeight();
23149         }
23150         
23151
23152         if(!this.tpl){
23153             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23154         }
23155
23156         this.view = new Roo.View(this.innerList, this.tpl, {
23157             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23158         });
23159
23160         this.view.on('click', this.onViewClick, this);
23161
23162         this.store.on('beforeload', this.onBeforeLoad, this);
23163         this.store.on('load', this.onLoad, this);
23164         this.store.on('loadexception', this.onLoadException, this);
23165
23166         if(this.resizable){
23167             this.resizer = new Roo.Resizable(this.list,  {
23168                pinned:true, handles:'se'
23169             });
23170             this.resizer.on('resize', function(r, w, h){
23171                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23172                 this.listWidth = w;
23173                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23174                 this.restrictHeight();
23175             }, this);
23176             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23177         }
23178         if(!this.editable){
23179             this.editable = true;
23180             this.setEditable(false);
23181         }  
23182         
23183         
23184         if (typeof(this.events.add.listeners) != 'undefined') {
23185             
23186             this.addicon = this.wrap.createChild(
23187                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23188        
23189             this.addicon.on('click', function(e) {
23190                 this.fireEvent('add', this);
23191             }, this);
23192         }
23193         if (typeof(this.events.edit.listeners) != 'undefined') {
23194             
23195             this.editicon = this.wrap.createChild(
23196                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23197             if (this.addicon) {
23198                 this.editicon.setStyle('margin-left', '40px');
23199             }
23200             this.editicon.on('click', function(e) {
23201                 
23202                 // we fire even  if inothing is selected..
23203                 this.fireEvent('edit', this, this.lastData );
23204                 
23205             }, this);
23206         }
23207         
23208         
23209         
23210     },
23211
23212     // private
23213     initEvents : function(){
23214         Roo.form.ComboBox.superclass.initEvents.call(this);
23215
23216         this.keyNav = new Roo.KeyNav(this.el, {
23217             "up" : function(e){
23218                 this.inKeyMode = true;
23219                 this.selectPrev();
23220             },
23221
23222             "down" : function(e){
23223                 if(!this.isExpanded()){
23224                     this.onTriggerClick();
23225                 }else{
23226                     this.inKeyMode = true;
23227                     this.selectNext();
23228                 }
23229             },
23230
23231             "enter" : function(e){
23232                 this.onViewClick();
23233                 //return true;
23234             },
23235
23236             "esc" : function(e){
23237                 this.collapse();
23238             },
23239
23240             "tab" : function(e){
23241                 this.onViewClick(false);
23242                 this.fireEvent("specialkey", this, e);
23243                 return true;
23244             },
23245
23246             scope : this,
23247
23248             doRelay : function(foo, bar, hname){
23249                 if(hname == 'down' || this.scope.isExpanded()){
23250                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23251                 }
23252                 return true;
23253             },
23254
23255             forceKeyDown: true
23256         });
23257         this.queryDelay = Math.max(this.queryDelay || 10,
23258                 this.mode == 'local' ? 10 : 250);
23259         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23260         if(this.typeAhead){
23261             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23262         }
23263         if(this.editable !== false){
23264             this.el.on("keyup", this.onKeyUp, this);
23265         }
23266         if(this.forceSelection){
23267             this.on('blur', this.doForce, this);
23268         }
23269     },
23270
23271     onDestroy : function(){
23272         if(this.view){
23273             this.view.setStore(null);
23274             this.view.el.removeAllListeners();
23275             this.view.el.remove();
23276             this.view.purgeListeners();
23277         }
23278         if(this.list){
23279             this.list.destroy();
23280         }
23281         if(this.store){
23282             this.store.un('beforeload', this.onBeforeLoad, this);
23283             this.store.un('load', this.onLoad, this);
23284             this.store.un('loadexception', this.onLoadException, this);
23285         }
23286         Roo.form.ComboBox.superclass.onDestroy.call(this);
23287     },
23288
23289     // private
23290     fireKey : function(e){
23291         if(e.isNavKeyPress() && !this.list.isVisible()){
23292             this.fireEvent("specialkey", this, e);
23293         }
23294     },
23295
23296     // private
23297     onResize: function(w, h){
23298         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23299         
23300         if(typeof w != 'number'){
23301             // we do not handle it!?!?
23302             return;
23303         }
23304         var tw = this.trigger.getWidth();
23305         tw += this.addicon ? this.addicon.getWidth() : 0;
23306         tw += this.editicon ? this.editicon.getWidth() : 0;
23307         var x = w - tw;
23308         this.el.setWidth( this.adjustWidth('input', x));
23309             
23310         this.trigger.setStyle('left', x+'px');
23311         
23312         if(this.list && this.listWidth === undefined){
23313             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23314             this.list.setWidth(lw);
23315             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23316         }
23317         
23318     
23319         
23320     },
23321
23322     /**
23323      * Allow or prevent the user from directly editing the field text.  If false is passed,
23324      * the user will only be able to select from the items defined in the dropdown list.  This method
23325      * is the runtime equivalent of setting the 'editable' config option at config time.
23326      * @param {Boolean} value True to allow the user to directly edit the field text
23327      */
23328     setEditable : function(value){
23329         if(value == this.editable){
23330             return;
23331         }
23332         this.editable = value;
23333         if(!value){
23334             this.el.dom.setAttribute('readOnly', true);
23335             this.el.on('mousedown', this.onTriggerClick,  this);
23336             this.el.addClass('x-combo-noedit');
23337         }else{
23338             this.el.dom.setAttribute('readOnly', false);
23339             this.el.un('mousedown', this.onTriggerClick,  this);
23340             this.el.removeClass('x-combo-noedit');
23341         }
23342     },
23343
23344     // private
23345     onBeforeLoad : function(){
23346         if(!this.hasFocus){
23347             return;
23348         }
23349         this.innerList.update(this.loadingText ?
23350                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23351         this.restrictHeight();
23352         this.selectedIndex = -1;
23353     },
23354
23355     // private
23356     onLoad : function(){
23357         if(!this.hasFocus){
23358             return;
23359         }
23360         if(this.store.getCount() > 0){
23361             this.expand();
23362             this.restrictHeight();
23363             if(this.lastQuery == this.allQuery){
23364                 if(this.editable){
23365                     this.el.dom.select();
23366                 }
23367                 if(!this.selectByValue(this.value, true)){
23368                     this.select(0, true);
23369                 }
23370             }else{
23371                 this.selectNext();
23372                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23373                     this.taTask.delay(this.typeAheadDelay);
23374                 }
23375             }
23376         }else{
23377             this.onEmptyResults();
23378         }
23379         //this.el.focus();
23380     },
23381     // private
23382     onLoadException : function()
23383     {
23384         this.collapse();
23385         Roo.log(this.store.reader.jsonData);
23386         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23387             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23388         }
23389         
23390         
23391     },
23392     // private
23393     onTypeAhead : function(){
23394         if(this.store.getCount() > 0){
23395             var r = this.store.getAt(0);
23396             var newValue = r.data[this.displayField];
23397             var len = newValue.length;
23398             var selStart = this.getRawValue().length;
23399             if(selStart != len){
23400                 this.setRawValue(newValue);
23401                 this.selectText(selStart, newValue.length);
23402             }
23403         }
23404     },
23405
23406     // private
23407     onSelect : function(record, index){
23408         if(this.fireEvent('beforeselect', this, record, index) !== false){
23409             this.setFromData(index > -1 ? record.data : false);
23410             this.collapse();
23411             this.fireEvent('select', this, record, index);
23412         }
23413     },
23414
23415     /**
23416      * Returns the currently selected field value or empty string if no value is set.
23417      * @return {String} value The selected value
23418      */
23419     getValue : function(){
23420         if(this.valueField){
23421             return typeof this.value != 'undefined' ? this.value : '';
23422         }
23423         return Roo.form.ComboBox.superclass.getValue.call(this);
23424     },
23425
23426     /**
23427      * Clears any text/value currently set in the field
23428      */
23429     clearValue : function(){
23430         if(this.hiddenField){
23431             this.hiddenField.value = '';
23432         }
23433         this.value = '';
23434         this.setRawValue('');
23435         this.lastSelectionText = '';
23436         
23437     },
23438
23439     /**
23440      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23441      * will be displayed in the field.  If the value does not match the data value of an existing item,
23442      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23443      * Otherwise the field will be blank (although the value will still be set).
23444      * @param {String} value The value to match
23445      */
23446     setValue : function(v){
23447         var text = v;
23448         if(this.valueField){
23449             var r = this.findRecord(this.valueField, v);
23450             if(r){
23451                 text = r.data[this.displayField];
23452             }else if(this.valueNotFoundText !== undefined){
23453                 text = this.valueNotFoundText;
23454             }
23455         }
23456         this.lastSelectionText = text;
23457         if(this.hiddenField){
23458             this.hiddenField.value = v;
23459         }
23460         Roo.form.ComboBox.superclass.setValue.call(this, text);
23461         this.value = v;
23462     },
23463     /**
23464      * @property {Object} the last set data for the element
23465      */
23466     
23467     lastData : false,
23468     /**
23469      * Sets the value of the field based on a object which is related to the record format for the store.
23470      * @param {Object} value the value to set as. or false on reset?
23471      */
23472     setFromData : function(o){
23473         var dv = ''; // display value
23474         var vv = ''; // value value..
23475         this.lastData = o;
23476         if (this.displayField) {
23477             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23478         } else {
23479             // this is an error condition!!!
23480             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23481         }
23482         
23483         if(this.valueField){
23484             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23485         }
23486         if(this.hiddenField){
23487             this.hiddenField.value = vv;
23488             
23489             this.lastSelectionText = dv;
23490             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23491             this.value = vv;
23492             return;
23493         }
23494         // no hidden field.. - we store the value in 'value', but still display
23495         // display field!!!!
23496         this.lastSelectionText = dv;
23497         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23498         this.value = vv;
23499         
23500         
23501     },
23502     // private
23503     reset : function(){
23504         // overridden so that last data is reset..
23505         this.setValue(this.resetValue);
23506         this.clearInvalid();
23507         this.lastData = false;
23508         if (this.view) {
23509             this.view.clearSelections();
23510         }
23511     },
23512     // private
23513     findRecord : function(prop, value){
23514         var record;
23515         if(this.store.getCount() > 0){
23516             this.store.each(function(r){
23517                 if(r.data[prop] == value){
23518                     record = r;
23519                     return false;
23520                 }
23521                 return true;
23522             });
23523         }
23524         return record;
23525     },
23526     
23527     getName: function()
23528     {
23529         // returns hidden if it's set..
23530         if (!this.rendered) {return ''};
23531         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23532         
23533     },
23534     // private
23535     onViewMove : function(e, t){
23536         this.inKeyMode = false;
23537     },
23538
23539     // private
23540     onViewOver : function(e, t){
23541         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23542             return;
23543         }
23544         var item = this.view.findItemFromChild(t);
23545         if(item){
23546             var index = this.view.indexOf(item);
23547             this.select(index, false);
23548         }
23549     },
23550
23551     // private
23552     onViewClick : function(doFocus)
23553     {
23554         var index = this.view.getSelectedIndexes()[0];
23555         var r = this.store.getAt(index);
23556         if(r){
23557             this.onSelect(r, index);
23558         }
23559         if(doFocus !== false && !this.blockFocus){
23560             this.el.focus();
23561         }
23562     },
23563
23564     // private
23565     restrictHeight : function(){
23566         this.innerList.dom.style.height = '';
23567         var inner = this.innerList.dom;
23568         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23569         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23570         this.list.beginUpdate();
23571         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23572         this.list.alignTo(this.el, this.listAlign);
23573         this.list.endUpdate();
23574     },
23575
23576     // private
23577     onEmptyResults : function(){
23578         this.collapse();
23579     },
23580
23581     /**
23582      * Returns true if the dropdown list is expanded, else false.
23583      */
23584     isExpanded : function(){
23585         return this.list.isVisible();
23586     },
23587
23588     /**
23589      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23590      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23591      * @param {String} value The data value of the item to select
23592      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23593      * selected item if it is not currently in view (defaults to true)
23594      * @return {Boolean} True if the value matched an item in the list, else false
23595      */
23596     selectByValue : function(v, scrollIntoView){
23597         if(v !== undefined && v !== null){
23598             var r = this.findRecord(this.valueField || this.displayField, v);
23599             if(r){
23600                 this.select(this.store.indexOf(r), scrollIntoView);
23601                 return true;
23602             }
23603         }
23604         return false;
23605     },
23606
23607     /**
23608      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23609      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23610      * @param {Number} index The zero-based index of the list item to select
23611      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23612      * selected item if it is not currently in view (defaults to true)
23613      */
23614     select : function(index, scrollIntoView){
23615         this.selectedIndex = index;
23616         this.view.select(index);
23617         if(scrollIntoView !== false){
23618             var el = this.view.getNode(index);
23619             if(el){
23620                 this.innerList.scrollChildIntoView(el, false);
23621             }
23622         }
23623     },
23624
23625     // private
23626     selectNext : function(){
23627         var ct = this.store.getCount();
23628         if(ct > 0){
23629             if(this.selectedIndex == -1){
23630                 this.select(0);
23631             }else if(this.selectedIndex < ct-1){
23632                 this.select(this.selectedIndex+1);
23633             }
23634         }
23635     },
23636
23637     // private
23638     selectPrev : function(){
23639         var ct = this.store.getCount();
23640         if(ct > 0){
23641             if(this.selectedIndex == -1){
23642                 this.select(0);
23643             }else if(this.selectedIndex != 0){
23644                 this.select(this.selectedIndex-1);
23645             }
23646         }
23647     },
23648
23649     // private
23650     onKeyUp : function(e){
23651         if(this.editable !== false && !e.isSpecialKey()){
23652             this.lastKey = e.getKey();
23653             this.dqTask.delay(this.queryDelay);
23654         }
23655     },
23656
23657     // private
23658     validateBlur : function(){
23659         return !this.list || !this.list.isVisible();   
23660     },
23661
23662     // private
23663     initQuery : function(){
23664         this.doQuery(this.getRawValue());
23665     },
23666
23667     // private
23668     doForce : function(){
23669         if(this.el.dom.value.length > 0){
23670             this.el.dom.value =
23671                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23672              
23673         }
23674     },
23675
23676     /**
23677      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23678      * query allowing the query action to be canceled if needed.
23679      * @param {String} query The SQL query to execute
23680      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23681      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23682      * saved in the current store (defaults to false)
23683      */
23684     doQuery : function(q, forceAll){
23685         if(q === undefined || q === null){
23686             q = '';
23687         }
23688         var qe = {
23689             query: q,
23690             forceAll: forceAll,
23691             combo: this,
23692             cancel:false
23693         };
23694         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23695             return false;
23696         }
23697         q = qe.query;
23698         forceAll = qe.forceAll;
23699         if(forceAll === true || (q.length >= this.minChars)){
23700             if(this.lastQuery != q || this.alwaysQuery){
23701                 this.lastQuery = q;
23702                 if(this.mode == 'local'){
23703                     this.selectedIndex = -1;
23704                     if(forceAll){
23705                         this.store.clearFilter();
23706                     }else{
23707                         this.store.filter(this.displayField, q);
23708                     }
23709                     this.onLoad();
23710                 }else{
23711                     this.store.baseParams[this.queryParam] = q;
23712                     this.store.load({
23713                         params: this.getParams(q)
23714                     });
23715                     this.expand();
23716                 }
23717             }else{
23718                 this.selectedIndex = -1;
23719                 this.onLoad();   
23720             }
23721         }
23722     },
23723
23724     // private
23725     getParams : function(q){
23726         var p = {};
23727         //p[this.queryParam] = q;
23728         if(this.pageSize){
23729             p.start = 0;
23730             p.limit = this.pageSize;
23731         }
23732         return p;
23733     },
23734
23735     /**
23736      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23737      */
23738     collapse : function(){
23739         if(!this.isExpanded()){
23740             return;
23741         }
23742         this.list.hide();
23743         Roo.get(document).un('mousedown', this.collapseIf, this);
23744         Roo.get(document).un('mousewheel', this.collapseIf, this);
23745         if (!this.editable) {
23746             Roo.get(document).un('keydown', this.listKeyPress, this);
23747         }
23748         this.fireEvent('collapse', this);
23749     },
23750
23751     // private
23752     collapseIf : function(e){
23753         if(!e.within(this.wrap) && !e.within(this.list)){
23754             this.collapse();
23755         }
23756     },
23757
23758     /**
23759      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23760      */
23761     expand : function(){
23762         if(this.isExpanded() || !this.hasFocus){
23763             return;
23764         }
23765         this.list.alignTo(this.el, this.listAlign);
23766         this.list.show();
23767         Roo.get(document).on('mousedown', this.collapseIf, this);
23768         Roo.get(document).on('mousewheel', this.collapseIf, this);
23769         if (!this.editable) {
23770             Roo.get(document).on('keydown', this.listKeyPress, this);
23771         }
23772         
23773         this.fireEvent('expand', this);
23774     },
23775
23776     // private
23777     // Implements the default empty TriggerField.onTriggerClick function
23778     onTriggerClick : function(){
23779         if(this.disabled){
23780             return;
23781         }
23782         if(this.isExpanded()){
23783             this.collapse();
23784             if (!this.blockFocus) {
23785                 this.el.focus();
23786             }
23787             
23788         }else {
23789             this.hasFocus = true;
23790             if(this.triggerAction == 'all') {
23791                 this.doQuery(this.allQuery, true);
23792             } else {
23793                 this.doQuery(this.getRawValue());
23794             }
23795             if (!this.blockFocus) {
23796                 this.el.focus();
23797             }
23798         }
23799     },
23800     listKeyPress : function(e)
23801     {
23802         //Roo.log('listkeypress');
23803         // scroll to first matching element based on key pres..
23804         if (e.isSpecialKey()) {
23805             return false;
23806         }
23807         var k = String.fromCharCode(e.getKey()).toUpperCase();
23808         //Roo.log(k);
23809         var match  = false;
23810         var csel = this.view.getSelectedNodes();
23811         var cselitem = false;
23812         if (csel.length) {
23813             var ix = this.view.indexOf(csel[0]);
23814             cselitem  = this.store.getAt(ix);
23815             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23816                 cselitem = false;
23817             }
23818             
23819         }
23820         
23821         this.store.each(function(v) { 
23822             if (cselitem) {
23823                 // start at existing selection.
23824                 if (cselitem.id == v.id) {
23825                     cselitem = false;
23826                 }
23827                 return;
23828             }
23829                 
23830             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23831                 match = this.store.indexOf(v);
23832                 return false;
23833             }
23834         }, this);
23835         
23836         if (match === false) {
23837             return true; // no more action?
23838         }
23839         // scroll to?
23840         this.view.select(match);
23841         var sn = Roo.get(this.view.getSelectedNodes()[0])
23842         sn.scrollIntoView(sn.dom.parentNode, false);
23843     }
23844
23845     /** 
23846     * @cfg {Boolean} grow 
23847     * @hide 
23848     */
23849     /** 
23850     * @cfg {Number} growMin 
23851     * @hide 
23852     */
23853     /** 
23854     * @cfg {Number} growMax 
23855     * @hide 
23856     */
23857     /**
23858      * @hide
23859      * @method autoSize
23860      */
23861 });/*
23862  * Copyright(c) 2010-2012, Roo J Solutions Limited
23863  *
23864  * Licence LGPL
23865  *
23866  */
23867
23868 /**
23869  * @class Roo.form.ComboBoxArray
23870  * @extends Roo.form.TextField
23871  * A facebook style adder... for lists of email / people / countries  etc...
23872  * pick multiple items from a combo box, and shows each one.
23873  *
23874  *  Fred [x]  Brian [x]  [Pick another |v]
23875  *
23876  *
23877  *  For this to work: it needs various extra information
23878  *    - normal combo problay has
23879  *      name, hiddenName
23880  *    + displayField, valueField
23881  *
23882  *    For our purpose...
23883  *
23884  *
23885  *   If we change from 'extends' to wrapping...
23886  *   
23887  *  
23888  *
23889  
23890  
23891  * @constructor
23892  * Create a new ComboBoxArray.
23893  * @param {Object} config Configuration options
23894  */
23895  
23896
23897 Roo.form.ComboBoxArray = function(config)
23898 {
23899     this.addEvents({
23900         /**
23901          * @event beforeremove
23902          * Fires before remove the value from the list
23903              * @param {Roo.form.ComboBoxArray} _self This combo box array
23904              * @param {Roo.form.ComboBoxArray.Item} item removed item
23905              */
23906         'beforeremove' : true,
23907         /**
23908          * @event remove
23909          * Fires when remove the value from the list
23910              * @param {Roo.form.ComboBoxArray} _self This combo box array
23911              * @param {Roo.form.ComboBoxArray.Item} item removed item
23912              */
23913         'remove' : true
23914         
23915         
23916     });
23917     
23918     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23919     
23920     this.items = new Roo.util.MixedCollection(false);
23921     
23922     // construct the child combo...
23923     
23924     
23925     
23926     
23927    
23928     
23929 }
23930
23931  
23932 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23933
23934     /**
23935      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23936      */
23937     
23938     lastData : false,
23939     
23940     // behavies liek a hiddne field
23941     inputType:      'hidden',
23942     /**
23943      * @cfg {Number} width The width of the box that displays the selected element
23944      */ 
23945     width:          300,
23946
23947     
23948     
23949     /**
23950      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
23951      */
23952     name : false,
23953     /**
23954      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
23955      */
23956     hiddenName : false,
23957     
23958     
23959     // private the array of items that are displayed..
23960     items  : false,
23961     // private - the hidden field el.
23962     hiddenEl : false,
23963     // private - the filed el..
23964     el : false,
23965     
23966     //validateValue : function() { return true; }, // all values are ok!
23967     //onAddClick: function() { },
23968     
23969     onRender : function(ct, position) 
23970     {
23971         
23972         // create the standard hidden element
23973         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
23974         
23975         
23976         // give fake names to child combo;
23977         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
23978         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
23979         
23980         this.combo = Roo.factory(this.combo, Roo.form);
23981         this.combo.onRender(ct, position);
23982         if (typeof(this.combo.width) != 'undefined') {
23983             this.combo.onResize(this.combo.width,0);
23984         }
23985         
23986         this.combo.initEvents();
23987         
23988         // assigned so form know we need to do this..
23989         this.store          = this.combo.store;
23990         this.valueField     = this.combo.valueField;
23991         this.displayField   = this.combo.displayField ;
23992         
23993         
23994         this.combo.wrap.addClass('x-cbarray-grp');
23995         
23996         var cbwrap = this.combo.wrap.createChild(
23997             {tag: 'div', cls: 'x-cbarray-cb'},
23998             this.combo.el.dom
23999         );
24000         
24001              
24002         this.hiddenEl = this.combo.wrap.createChild({
24003             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24004         });
24005         this.el = this.combo.wrap.createChild({
24006             tag: 'input',  type:'hidden' , name: this.name, value : ''
24007         });
24008          //   this.el.dom.removeAttribute("name");
24009         
24010         
24011         this.outerWrap = this.combo.wrap;
24012         this.wrap = cbwrap;
24013         
24014         this.outerWrap.setWidth(this.width);
24015         this.outerWrap.dom.removeChild(this.el.dom);
24016         
24017         this.wrap.dom.appendChild(this.el.dom);
24018         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24019         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24020         
24021         this.combo.trigger.setStyle('position','relative');
24022         this.combo.trigger.setStyle('left', '0px');
24023         this.combo.trigger.setStyle('top', '2px');
24024         
24025         this.combo.el.setStyle('vertical-align', 'text-bottom');
24026         
24027         //this.trigger.setStyle('vertical-align', 'top');
24028         
24029         // this should use the code from combo really... on('add' ....)
24030         if (this.adder) {
24031             
24032         
24033             this.adder = this.outerWrap.createChild(
24034                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24035             var _t = this;
24036             this.adder.on('click', function(e) {
24037                 _t.fireEvent('adderclick', this, e);
24038             }, _t);
24039         }
24040         //var _t = this;
24041         //this.adder.on('click', this.onAddClick, _t);
24042         
24043         
24044         this.combo.on('select', function(cb, rec, ix) {
24045             this.addItem(rec.data);
24046             
24047             cb.setValue('');
24048             cb.el.dom.value = '';
24049             //cb.lastData = rec.data;
24050             // add to list
24051             
24052         }, this);
24053         
24054         
24055     },
24056     
24057     
24058     getName: function()
24059     {
24060         // returns hidden if it's set..
24061         if (!this.rendered) {return ''};
24062         return  this.hiddenName ? this.hiddenName : this.name;
24063         
24064     },
24065     
24066     
24067     onResize: function(w, h){
24068         
24069         return;
24070         // not sure if this is needed..
24071         //this.combo.onResize(w,h);
24072         
24073         if(typeof w != 'number'){
24074             // we do not handle it!?!?
24075             return;
24076         }
24077         var tw = this.combo.trigger.getWidth();
24078         tw += this.addicon ? this.addicon.getWidth() : 0;
24079         tw += this.editicon ? this.editicon.getWidth() : 0;
24080         var x = w - tw;
24081         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24082             
24083         this.combo.trigger.setStyle('left', '0px');
24084         
24085         if(this.list && this.listWidth === undefined){
24086             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24087             this.list.setWidth(lw);
24088             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24089         }
24090         
24091     
24092         
24093     },
24094     
24095     addItem: function(rec)
24096     {
24097         var valueField = this.combo.valueField;
24098         var displayField = this.combo.displayField;
24099         if (this.items.indexOfKey(rec[valueField]) > -1) {
24100             //console.log("GOT " + rec.data.id);
24101             return;
24102         }
24103         
24104         var x = new Roo.form.ComboBoxArray.Item({
24105             //id : rec[this.idField],
24106             data : rec,
24107             displayField : displayField ,
24108             tipField : displayField ,
24109             cb : this
24110         });
24111         // use the 
24112         this.items.add(rec[valueField],x);
24113         // add it before the element..
24114         this.updateHiddenEl();
24115         x.render(this.outerWrap, this.wrap.dom);
24116         // add the image handler..
24117     },
24118     
24119     updateHiddenEl : function()
24120     {
24121         this.validate();
24122         if (!this.hiddenEl) {
24123             return;
24124         }
24125         var ar = [];
24126         var idField = this.combo.valueField;
24127         
24128         this.items.each(function(f) {
24129             ar.push(f.data[idField]);
24130            
24131         });
24132         this.hiddenEl.dom.value = ar.join(',');
24133         this.validate();
24134     },
24135     
24136     reset : function()
24137     {
24138         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24139         this.items.each(function(f) {
24140            f.remove(); 
24141         });
24142         this.el.dom.value = '';
24143         if (this.hiddenEl) {
24144             this.hiddenEl.dom.value = '';
24145         }
24146         
24147     },
24148     getValue: function()
24149     {
24150         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24151     },
24152     setValue: function(v) // not a valid action - must use addItems..
24153     {
24154          
24155         this.reset();
24156         
24157         
24158         
24159         if (this.store.isLocal && (typeof(v) == 'string')) {
24160             // then we can use the store to find the values..
24161             // comma seperated at present.. this needs to allow JSON based encoding..
24162             this.hiddenEl.value  = v;
24163             var v_ar = [];
24164             Roo.each(v.split(','), function(k) {
24165                 Roo.log("CHECK " + this.valueField + ',' + k);
24166                 var li = this.store.query(this.valueField, k);
24167                 if (!li.length) {
24168                     return;
24169                 }
24170                 var add = {};
24171                 add[this.valueField] = k;
24172                 add[this.displayField] = li.item(0).data[this.displayField];
24173                 
24174                 this.addItem(add);
24175             }, this) 
24176              
24177         }
24178         if (typeof(v) == 'object' ) {
24179             // then let's assume it's an array of objects..
24180             Roo.each(v, function(l) {
24181                 this.addItem(l);
24182             }, this);
24183              
24184         }
24185         
24186         
24187     },
24188     setFromData: function(v)
24189     {
24190         // this recieves an object, if setValues is called.
24191         this.reset();
24192         this.el.dom.value = v[this.displayField];
24193         this.hiddenEl.dom.value = v[this.valueField];
24194         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24195             return;
24196         }
24197         var kv = v[this.valueField];
24198         var dv = v[this.displayField];
24199         kv = typeof(kv) != 'string' ? '' : kv;
24200         dv = typeof(dv) != 'string' ? '' : dv;
24201         
24202         
24203         var keys = kv.split(',');
24204         var display = dv.split(',');
24205         for (var i = 0 ; i < keys.length; i++) {
24206             
24207             add = {};
24208             add[this.valueField] = keys[i];
24209             add[this.displayField] = display[i];
24210             this.addItem(add);
24211         }
24212       
24213         
24214     },
24215     
24216     /**
24217      * Validates the combox array value
24218      * @return {Boolean} True if the value is valid, else false
24219      */
24220     validate : function(){
24221         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24222             this.clearInvalid();
24223             return true;
24224         }
24225         return false;
24226     },
24227     
24228     validateValue : function(value){
24229         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24230         
24231     },
24232     
24233     /*@
24234      * overide
24235      * 
24236      */
24237     isDirty : function() {
24238         if(this.disabled) {
24239             return false;
24240         }
24241         
24242         try {
24243             var d = Roo.decode(String(this.originalValue));
24244         } catch (e) {
24245             return String(this.getValue()) !== String(this.originalValue);
24246         }
24247         
24248         var originalValue = [];
24249         
24250         for (var i = 0; i < d.length; i++){
24251             originalValue.push(d[i][this.valueField]);
24252         }
24253         
24254         return String(this.getValue()) !== String(originalValue.join(','));
24255         
24256     }
24257     
24258 });
24259
24260
24261
24262 /**
24263  * @class Roo.form.ComboBoxArray.Item
24264  * @extends Roo.BoxComponent
24265  * A selected item in the list
24266  *  Fred [x]  Brian [x]  [Pick another |v]
24267  * 
24268  * @constructor
24269  * Create a new item.
24270  * @param {Object} config Configuration options
24271  */
24272  
24273 Roo.form.ComboBoxArray.Item = function(config) {
24274     config.id = Roo.id();
24275     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24276 }
24277
24278 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24279     data : {},
24280     cb: false,
24281     displayField : false,
24282     tipField : false,
24283     
24284     
24285     defaultAutoCreate : {
24286         tag: 'div',
24287         cls: 'x-cbarray-item',
24288         cn : [ 
24289             { tag: 'div' },
24290             {
24291                 tag: 'img',
24292                 width:16,
24293                 height : 16,
24294                 src : Roo.BLANK_IMAGE_URL ,
24295                 align: 'center'
24296             }
24297         ]
24298         
24299     },
24300     
24301  
24302     onRender : function(ct, position)
24303     {
24304         Roo.form.Field.superclass.onRender.call(this, ct, position);
24305         
24306         if(!this.el){
24307             var cfg = this.getAutoCreate();
24308             this.el = ct.createChild(cfg, position);
24309         }
24310         
24311         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24312         
24313         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24314             this.cb.renderer(this.data) :
24315             String.format('{0}',this.data[this.displayField]);
24316         
24317             
24318         this.el.child('div').dom.setAttribute('qtip',
24319                         String.format('{0}',this.data[this.tipField])
24320         );
24321         
24322         this.el.child('img').on('click', this.remove, this);
24323         
24324     },
24325    
24326     remove : function()
24327     {
24328         if(this.cb.disabled){
24329             return;
24330         }
24331         
24332         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24333             this.cb.items.remove(this);
24334             this.el.child('img').un('click', this.remove, this);
24335             this.el.remove();
24336             this.cb.updateHiddenEl();
24337
24338             this.cb.fireEvent('remove', this.cb, this);
24339         }
24340         
24341     }
24342 });/*
24343  * Based on:
24344  * Ext JS Library 1.1.1
24345  * Copyright(c) 2006-2007, Ext JS, LLC.
24346  *
24347  * Originally Released Under LGPL - original licence link has changed is not relivant.
24348  *
24349  * Fork - LGPL
24350  * <script type="text/javascript">
24351  */
24352 /**
24353  * @class Roo.form.Checkbox
24354  * @extends Roo.form.Field
24355  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24356  * @constructor
24357  * Creates a new Checkbox
24358  * @param {Object} config Configuration options
24359  */
24360 Roo.form.Checkbox = function(config){
24361     Roo.form.Checkbox.superclass.constructor.call(this, config);
24362     this.addEvents({
24363         /**
24364          * @event check
24365          * Fires when the checkbox is checked or unchecked.
24366              * @param {Roo.form.Checkbox} this This checkbox
24367              * @param {Boolean} checked The new checked value
24368              */
24369         check : true
24370     });
24371 };
24372
24373 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24374     /**
24375      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24376      */
24377     focusClass : undefined,
24378     /**
24379      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24380      */
24381     fieldClass: "x-form-field",
24382     /**
24383      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24384      */
24385     checked: false,
24386     /**
24387      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24388      * {tag: "input", type: "checkbox", autocomplete: "off"})
24389      */
24390     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24391     /**
24392      * @cfg {String} boxLabel The text that appears beside the checkbox
24393      */
24394     boxLabel : "",
24395     /**
24396      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24397      */  
24398     inputValue : '1',
24399     /**
24400      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24401      */
24402      valueOff: '0', // value when not checked..
24403
24404     actionMode : 'viewEl', 
24405     //
24406     // private
24407     itemCls : 'x-menu-check-item x-form-item',
24408     groupClass : 'x-menu-group-item',
24409     inputType : 'hidden',
24410     
24411     
24412     inSetChecked: false, // check that we are not calling self...
24413     
24414     inputElement: false, // real input element?
24415     basedOn: false, // ????
24416     
24417     isFormField: true, // not sure where this is needed!!!!
24418
24419     onResize : function(){
24420         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24421         if(!this.boxLabel){
24422             this.el.alignTo(this.wrap, 'c-c');
24423         }
24424     },
24425
24426     initEvents : function(){
24427         Roo.form.Checkbox.superclass.initEvents.call(this);
24428         this.el.on("click", this.onClick,  this);
24429         this.el.on("change", this.onClick,  this);
24430     },
24431
24432
24433     getResizeEl : function(){
24434         return this.wrap;
24435     },
24436
24437     getPositionEl : function(){
24438         return this.wrap;
24439     },
24440
24441     // private
24442     onRender : function(ct, position){
24443         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24444         /*
24445         if(this.inputValue !== undefined){
24446             this.el.dom.value = this.inputValue;
24447         }
24448         */
24449         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24450         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24451         var viewEl = this.wrap.createChild({ 
24452             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24453         this.viewEl = viewEl;   
24454         this.wrap.on('click', this.onClick,  this); 
24455         
24456         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24457         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24458         
24459         
24460         
24461         if(this.boxLabel){
24462             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24463         //    viewEl.on('click', this.onClick,  this); 
24464         }
24465         //if(this.checked){
24466             this.setChecked(this.checked);
24467         //}else{
24468             //this.checked = this.el.dom;
24469         //}
24470
24471     },
24472
24473     // private
24474     initValue : Roo.emptyFn,
24475
24476     /**
24477      * Returns the checked state of the checkbox.
24478      * @return {Boolean} True if checked, else false
24479      */
24480     getValue : function(){
24481         if(this.el){
24482             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24483         }
24484         return this.valueOff;
24485         
24486     },
24487
24488         // private
24489     onClick : function(){ 
24490         if (this.disabled) {
24491             return;
24492         }
24493         this.setChecked(!this.checked);
24494
24495         //if(this.el.dom.checked != this.checked){
24496         //    this.setValue(this.el.dom.checked);
24497        // }
24498     },
24499
24500     /**
24501      * Sets the checked state of the checkbox.
24502      * On is always based on a string comparison between inputValue and the param.
24503      * @param {Boolean/String} value - the value to set 
24504      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24505      */
24506     setValue : function(v,suppressEvent){
24507         
24508         
24509         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24510         //if(this.el && this.el.dom){
24511         //    this.el.dom.checked = this.checked;
24512         //    this.el.dom.defaultChecked = this.checked;
24513         //}
24514         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24515         //this.fireEvent("check", this, this.checked);
24516     },
24517     // private..
24518     setChecked : function(state,suppressEvent)
24519     {
24520         if (this.inSetChecked) {
24521             this.checked = state;
24522             return;
24523         }
24524         
24525     
24526         if(this.wrap){
24527             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24528         }
24529         this.checked = state;
24530         if(suppressEvent !== true){
24531             this.fireEvent('check', this, state);
24532         }
24533         this.inSetChecked = true;
24534         this.el.dom.value = state ? this.inputValue : this.valueOff;
24535         this.inSetChecked = false;
24536         
24537     },
24538     // handle setting of hidden value by some other method!!?!?
24539     setFromHidden: function()
24540     {
24541         if(!this.el){
24542             return;
24543         }
24544         //console.log("SET FROM HIDDEN");
24545         //alert('setFrom hidden');
24546         this.setValue(this.el.dom.value);
24547     },
24548     
24549     onDestroy : function()
24550     {
24551         if(this.viewEl){
24552             Roo.get(this.viewEl).remove();
24553         }
24554          
24555         Roo.form.Checkbox.superclass.onDestroy.call(this);
24556     }
24557
24558 });/*
24559  * Based on:
24560  * Ext JS Library 1.1.1
24561  * Copyright(c) 2006-2007, Ext JS, LLC.
24562  *
24563  * Originally Released Under LGPL - original licence link has changed is not relivant.
24564  *
24565  * Fork - LGPL
24566  * <script type="text/javascript">
24567  */
24568  
24569 /**
24570  * @class Roo.form.Radio
24571  * @extends Roo.form.Checkbox
24572  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24573  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24574  * @constructor
24575  * Creates a new Radio
24576  * @param {Object} config Configuration options
24577  */
24578 Roo.form.Radio = function(){
24579     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24580 };
24581 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24582     inputType: 'radio',
24583
24584     /**
24585      * If this radio is part of a group, it will return the selected value
24586      * @return {String}
24587      */
24588     getGroupValue : function(){
24589         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24590     },
24591     
24592     
24593     onRender : function(ct, position){
24594         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24595         
24596         if(this.inputValue !== undefined){
24597             this.el.dom.value = this.inputValue;
24598         }
24599          
24600         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24601         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24602         //var viewEl = this.wrap.createChild({ 
24603         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24604         //this.viewEl = viewEl;   
24605         //this.wrap.on('click', this.onClick,  this); 
24606         
24607         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24608         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24609         
24610         
24611         
24612         if(this.boxLabel){
24613             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24614         //    viewEl.on('click', this.onClick,  this); 
24615         }
24616          if(this.checked){
24617             this.el.dom.checked =   'checked' ;
24618         }
24619          
24620     } 
24621     
24622     
24623 });//<script type="text/javascript">
24624
24625 /*
24626  * Based  Ext JS Library 1.1.1
24627  * Copyright(c) 2006-2007, Ext JS, LLC.
24628  * LGPL
24629  *
24630  */
24631  
24632 /**
24633  * @class Roo.HtmlEditorCore
24634  * @extends Roo.Component
24635  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24636  *
24637  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24638  */
24639
24640 Roo.HtmlEditorCore = function(config){
24641     
24642     
24643     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24644     
24645     
24646     this.addEvents({
24647         /**
24648          * @event initialize
24649          * Fires when the editor is fully initialized (including the iframe)
24650          * @param {Roo.HtmlEditorCore} this
24651          */
24652         initialize: true,
24653         /**
24654          * @event activate
24655          * Fires when the editor is first receives the focus. Any insertion must wait
24656          * until after this event.
24657          * @param {Roo.HtmlEditorCore} this
24658          */
24659         activate: true,
24660          /**
24661          * @event beforesync
24662          * Fires before the textarea is updated with content from the editor iframe. Return false
24663          * to cancel the sync.
24664          * @param {Roo.HtmlEditorCore} this
24665          * @param {String} html
24666          */
24667         beforesync: true,
24668          /**
24669          * @event beforepush
24670          * Fires before the iframe editor is updated with content from the textarea. Return false
24671          * to cancel the push.
24672          * @param {Roo.HtmlEditorCore} this
24673          * @param {String} html
24674          */
24675         beforepush: true,
24676          /**
24677          * @event sync
24678          * Fires when the textarea is updated with content from the editor iframe.
24679          * @param {Roo.HtmlEditorCore} this
24680          * @param {String} html
24681          */
24682         sync: true,
24683          /**
24684          * @event push
24685          * Fires when the iframe editor is updated with content from the textarea.
24686          * @param {Roo.HtmlEditorCore} this
24687          * @param {String} html
24688          */
24689         push: true,
24690         
24691         /**
24692          * @event editorevent
24693          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24694          * @param {Roo.HtmlEditorCore} this
24695          */
24696         editorevent: true
24697     });
24698     
24699     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24700     
24701     // defaults : white / black...
24702     this.applyBlacklists();
24703     
24704     
24705     
24706 };
24707
24708
24709 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24710
24711
24712      /**
24713      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24714      */
24715     
24716     owner : false,
24717     
24718      /**
24719      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24720      *                        Roo.resizable.
24721      */
24722     resizable : false,
24723      /**
24724      * @cfg {Number} height (in pixels)
24725      */   
24726     height: 300,
24727    /**
24728      * @cfg {Number} width (in pixels)
24729      */   
24730     width: 500,
24731     
24732     /**
24733      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24734      * 
24735      */
24736     stylesheets: false,
24737     
24738     // id of frame..
24739     frameId: false,
24740     
24741     // private properties
24742     validationEvent : false,
24743     deferHeight: true,
24744     initialized : false,
24745     activated : false,
24746     sourceEditMode : false,
24747     onFocus : Roo.emptyFn,
24748     iframePad:3,
24749     hideMode:'offsets',
24750     
24751     clearUp: true,
24752     
24753     // blacklist + whitelisted elements..
24754     black: false,
24755     white: false,
24756      
24757     
24758
24759     /**
24760      * Protected method that will not generally be called directly. It
24761      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24762      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24763      */
24764     getDocMarkup : function(){
24765         // body styles..
24766         var st = '';
24767         Roo.log(this.stylesheets);
24768         
24769         // inherit styels from page...?? 
24770         if (this.stylesheets === false) {
24771             
24772             Roo.get(document.head).select('style').each(function(node) {
24773                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24774             });
24775             
24776             Roo.get(document.head).select('link').each(function(node) { 
24777                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24778             });
24779             
24780         } else if (!this.stylesheets.length) {
24781                 // simple..
24782                 st = '<style type="text/css">' +
24783                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24784                    '</style>';
24785         } else {
24786             Roo.each(this.stylesheets, function(s) {
24787                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24788             });
24789             
24790         }
24791         
24792         st +=  '<style type="text/css">' +
24793             'IMG { cursor: pointer } ' +
24794         '</style>';
24795
24796         
24797         return '<html><head>' + st  +
24798             //<style type="text/css">' +
24799             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24800             //'</style>' +
24801             ' </head><body class="roo-htmleditor-body"></body></html>';
24802     },
24803
24804     // private
24805     onRender : function(ct, position)
24806     {
24807         var _t = this;
24808         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24809         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24810         
24811         
24812         this.el.dom.style.border = '0 none';
24813         this.el.dom.setAttribute('tabIndex', -1);
24814         this.el.addClass('x-hidden hide');
24815         
24816         
24817         
24818         if(Roo.isIE){ // fix IE 1px bogus margin
24819             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24820         }
24821        
24822         
24823         this.frameId = Roo.id();
24824         
24825          
24826         
24827         var iframe = this.owner.wrap.createChild({
24828             tag: 'iframe',
24829             cls: 'form-control', // bootstrap..
24830             id: this.frameId,
24831             name: this.frameId,
24832             frameBorder : 'no',
24833             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24834         }, this.el
24835         );
24836         
24837         
24838         this.iframe = iframe.dom;
24839
24840          this.assignDocWin();
24841         
24842         this.doc.designMode = 'on';
24843        
24844         this.doc.open();
24845         this.doc.write(this.getDocMarkup());
24846         this.doc.close();
24847
24848         
24849         var task = { // must defer to wait for browser to be ready
24850             run : function(){
24851                 //console.log("run task?" + this.doc.readyState);
24852                 this.assignDocWin();
24853                 if(this.doc.body || this.doc.readyState == 'complete'){
24854                     try {
24855                         this.doc.designMode="on";
24856                     } catch (e) {
24857                         return;
24858                     }
24859                     Roo.TaskMgr.stop(task);
24860                     this.initEditor.defer(10, this);
24861                 }
24862             },
24863             interval : 10,
24864             duration: 10000,
24865             scope: this
24866         };
24867         Roo.TaskMgr.start(task);
24868
24869         
24870          
24871     },
24872
24873     // private
24874     onResize : function(w, h)
24875     {
24876          Roo.log('resize: ' +w + ',' + h );
24877         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24878         if(!this.iframe){
24879             return;
24880         }
24881         if(typeof w == 'number'){
24882             
24883             this.iframe.style.width = w + 'px';
24884         }
24885         if(typeof h == 'number'){
24886             
24887             this.iframe.style.height = h + 'px';
24888             if(this.doc){
24889                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24890             }
24891         }
24892         
24893     },
24894
24895     /**
24896      * Toggles the editor between standard and source edit mode.
24897      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24898      */
24899     toggleSourceEdit : function(sourceEditMode){
24900         
24901         this.sourceEditMode = sourceEditMode === true;
24902         
24903         if(this.sourceEditMode){
24904  
24905             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24906             
24907         }else{
24908             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24909             //this.iframe.className = '';
24910             this.deferFocus();
24911         }
24912         //this.setSize(this.owner.wrap.getSize());
24913         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24914     },
24915
24916     
24917   
24918
24919     /**
24920      * Protected method that will not generally be called directly. If you need/want
24921      * custom HTML cleanup, this is the method you should override.
24922      * @param {String} html The HTML to be cleaned
24923      * return {String} The cleaned HTML
24924      */
24925     cleanHtml : function(html){
24926         html = String(html);
24927         if(html.length > 5){
24928             if(Roo.isSafari){ // strip safari nonsense
24929                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24930             }
24931         }
24932         if(html == '&nbsp;'){
24933             html = '';
24934         }
24935         return html;
24936     },
24937
24938     /**
24939      * HTML Editor -> Textarea
24940      * Protected method that will not generally be called directly. Syncs the contents
24941      * of the editor iframe with the textarea.
24942      */
24943     syncValue : function(){
24944         if(this.initialized){
24945             var bd = (this.doc.body || this.doc.documentElement);
24946             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24947             var html = bd.innerHTML;
24948             if(Roo.isSafari){
24949                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24950                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24951                 if(m && m[1]){
24952                     html = '<div style="'+m[0]+'">' + html + '</div>';
24953                 }
24954             }
24955             html = this.cleanHtml(html);
24956             // fix up the special chars.. normaly like back quotes in word...
24957             // however we do not want to do this with chinese..
24958             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24959                 var cc = b.charCodeAt();
24960                 if (
24961                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24962                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24963                     (cc >= 0xf900 && cc < 0xfb00 )
24964                 ) {
24965                         return b;
24966                 }
24967                 return "&#"+cc+";" 
24968             });
24969             if(this.owner.fireEvent('beforesync', this, html) !== false){
24970                 this.el.dom.value = html;
24971                 this.owner.fireEvent('sync', this, html);
24972             }
24973         }
24974     },
24975
24976     /**
24977      * Protected method that will not generally be called directly. Pushes the value of the textarea
24978      * into the iframe editor.
24979      */
24980     pushValue : function(){
24981         if(this.initialized){
24982             var v = this.el.dom.value.trim();
24983             
24984 //            if(v.length < 1){
24985 //                v = '&#160;';
24986 //            }
24987             
24988             if(this.owner.fireEvent('beforepush', this, v) !== false){
24989                 var d = (this.doc.body || this.doc.documentElement);
24990                 d.innerHTML = v;
24991                 this.cleanUpPaste();
24992                 this.el.dom.value = d.innerHTML;
24993                 this.owner.fireEvent('push', this, v);
24994             }
24995         }
24996     },
24997
24998     // private
24999     deferFocus : function(){
25000         this.focus.defer(10, this);
25001     },
25002
25003     // doc'ed in Field
25004     focus : function(){
25005         if(this.win && !this.sourceEditMode){
25006             this.win.focus();
25007         }else{
25008             this.el.focus();
25009         }
25010     },
25011     
25012     assignDocWin: function()
25013     {
25014         var iframe = this.iframe;
25015         
25016          if(Roo.isIE){
25017             this.doc = iframe.contentWindow.document;
25018             this.win = iframe.contentWindow;
25019         } else {
25020 //            if (!Roo.get(this.frameId)) {
25021 //                return;
25022 //            }
25023 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25024 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25025             
25026             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25027                 return;
25028             }
25029             
25030             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25031             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25032         }
25033     },
25034     
25035     // private
25036     initEditor : function(){
25037         //console.log("INIT EDITOR");
25038         this.assignDocWin();
25039         
25040         
25041         
25042         this.doc.designMode="on";
25043         this.doc.open();
25044         this.doc.write(this.getDocMarkup());
25045         this.doc.close();
25046         
25047         var dbody = (this.doc.body || this.doc.documentElement);
25048         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25049         // this copies styles from the containing element into thsi one..
25050         // not sure why we need all of this..
25051         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25052         
25053         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25054         //ss['background-attachment'] = 'fixed'; // w3c
25055         dbody.bgProperties = 'fixed'; // ie
25056         //Roo.DomHelper.applyStyles(dbody, ss);
25057         Roo.EventManager.on(this.doc, {
25058             //'mousedown': this.onEditorEvent,
25059             'mouseup': this.onEditorEvent,
25060             'dblclick': this.onEditorEvent,
25061             'click': this.onEditorEvent,
25062             'keyup': this.onEditorEvent,
25063             buffer:100,
25064             scope: this
25065         });
25066         if(Roo.isGecko){
25067             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25068         }
25069         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25070             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25071         }
25072         this.initialized = true;
25073
25074         this.owner.fireEvent('initialize', this);
25075         this.pushValue();
25076     },
25077
25078     // private
25079     onDestroy : function(){
25080         
25081         
25082         
25083         if(this.rendered){
25084             
25085             //for (var i =0; i < this.toolbars.length;i++) {
25086             //    // fixme - ask toolbars for heights?
25087             //    this.toolbars[i].onDestroy();
25088            // }
25089             
25090             //this.wrap.dom.innerHTML = '';
25091             //this.wrap.remove();
25092         }
25093     },
25094
25095     // private
25096     onFirstFocus : function(){
25097         
25098         this.assignDocWin();
25099         
25100         
25101         this.activated = true;
25102          
25103     
25104         if(Roo.isGecko){ // prevent silly gecko errors
25105             this.win.focus();
25106             var s = this.win.getSelection();
25107             if(!s.focusNode || s.focusNode.nodeType != 3){
25108                 var r = s.getRangeAt(0);
25109                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25110                 r.collapse(true);
25111                 this.deferFocus();
25112             }
25113             try{
25114                 this.execCmd('useCSS', true);
25115                 this.execCmd('styleWithCSS', false);
25116             }catch(e){}
25117         }
25118         this.owner.fireEvent('activate', this);
25119     },
25120
25121     // private
25122     adjustFont: function(btn){
25123         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25124         //if(Roo.isSafari){ // safari
25125         //    adjust *= 2;
25126        // }
25127         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25128         if(Roo.isSafari){ // safari
25129             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25130             v =  (v < 10) ? 10 : v;
25131             v =  (v > 48) ? 48 : v;
25132             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25133             
25134         }
25135         
25136         
25137         v = Math.max(1, v+adjust);
25138         
25139         this.execCmd('FontSize', v  );
25140     },
25141
25142     onEditorEvent : function(e){
25143         this.owner.fireEvent('editorevent', this, e);
25144       //  this.updateToolbar();
25145         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25146     },
25147
25148     insertTag : function(tg)
25149     {
25150         // could be a bit smarter... -> wrap the current selected tRoo..
25151         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25152             
25153             range = this.createRange(this.getSelection());
25154             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25155             wrappingNode.appendChild(range.extractContents());
25156             range.insertNode(wrappingNode);
25157
25158             return;
25159             
25160             
25161             
25162         }
25163         this.execCmd("formatblock",   tg);
25164         
25165     },
25166     
25167     insertText : function(txt)
25168     {
25169         
25170         
25171         var range = this.createRange();
25172         range.deleteContents();
25173                //alert(Sender.getAttribute('label'));
25174                
25175         range.insertNode(this.doc.createTextNode(txt));
25176     } ,
25177     
25178      
25179
25180     /**
25181      * Executes a Midas editor command on the editor document and performs necessary focus and
25182      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25183      * @param {String} cmd The Midas command
25184      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25185      */
25186     relayCmd : function(cmd, value){
25187         this.win.focus();
25188         this.execCmd(cmd, value);
25189         this.owner.fireEvent('editorevent', this);
25190         //this.updateToolbar();
25191         this.owner.deferFocus();
25192     },
25193
25194     /**
25195      * Executes a Midas editor command directly on the editor document.
25196      * For visual commands, you should use {@link #relayCmd} instead.
25197      * <b>This should only be called after the editor is initialized.</b>
25198      * @param {String} cmd The Midas command
25199      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25200      */
25201     execCmd : function(cmd, value){
25202         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25203         this.syncValue();
25204     },
25205  
25206  
25207    
25208     /**
25209      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25210      * to insert tRoo.
25211      * @param {String} text | dom node.. 
25212      */
25213     insertAtCursor : function(text)
25214     {
25215         
25216         
25217         
25218         if(!this.activated){
25219             return;
25220         }
25221         /*
25222         if(Roo.isIE){
25223             this.win.focus();
25224             var r = this.doc.selection.createRange();
25225             if(r){
25226                 r.collapse(true);
25227                 r.pasteHTML(text);
25228                 this.syncValue();
25229                 this.deferFocus();
25230             
25231             }
25232             return;
25233         }
25234         */
25235         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25236             this.win.focus();
25237             
25238             
25239             // from jquery ui (MIT licenced)
25240             var range, node;
25241             var win = this.win;
25242             
25243             if (win.getSelection && win.getSelection().getRangeAt) {
25244                 range = win.getSelection().getRangeAt(0);
25245                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25246                 range.insertNode(node);
25247             } else if (win.document.selection && win.document.selection.createRange) {
25248                 // no firefox support
25249                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25250                 win.document.selection.createRange().pasteHTML(txt);
25251             } else {
25252                 // no firefox support
25253                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25254                 this.execCmd('InsertHTML', txt);
25255             } 
25256             
25257             this.syncValue();
25258             
25259             this.deferFocus();
25260         }
25261     },
25262  // private
25263     mozKeyPress : function(e){
25264         if(e.ctrlKey){
25265             var c = e.getCharCode(), cmd;
25266           
25267             if(c > 0){
25268                 c = String.fromCharCode(c).toLowerCase();
25269                 switch(c){
25270                     case 'b':
25271                         cmd = 'bold';
25272                         break;
25273                     case 'i':
25274                         cmd = 'italic';
25275                         break;
25276                     
25277                     case 'u':
25278                         cmd = 'underline';
25279                         break;
25280                     
25281                     case 'v':
25282                         this.cleanUpPaste.defer(100, this);
25283                         return;
25284                         
25285                 }
25286                 if(cmd){
25287                     this.win.focus();
25288                     this.execCmd(cmd);
25289                     this.deferFocus();
25290                     e.preventDefault();
25291                 }
25292                 
25293             }
25294         }
25295     },
25296
25297     // private
25298     fixKeys : function(){ // load time branching for fastest keydown performance
25299         if(Roo.isIE){
25300             return function(e){
25301                 var k = e.getKey(), r;
25302                 if(k == e.TAB){
25303                     e.stopEvent();
25304                     r = this.doc.selection.createRange();
25305                     if(r){
25306                         r.collapse(true);
25307                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25308                         this.deferFocus();
25309                     }
25310                     return;
25311                 }
25312                 
25313                 if(k == e.ENTER){
25314                     r = this.doc.selection.createRange();
25315                     if(r){
25316                         var target = r.parentElement();
25317                         if(!target || target.tagName.toLowerCase() != 'li'){
25318                             e.stopEvent();
25319                             r.pasteHTML('<br />');
25320                             r.collapse(false);
25321                             r.select();
25322                         }
25323                     }
25324                 }
25325                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25326                     this.cleanUpPaste.defer(100, this);
25327                     return;
25328                 }
25329                 
25330                 
25331             };
25332         }else if(Roo.isOpera){
25333             return function(e){
25334                 var k = e.getKey();
25335                 if(k == e.TAB){
25336                     e.stopEvent();
25337                     this.win.focus();
25338                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25339                     this.deferFocus();
25340                 }
25341                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25342                     this.cleanUpPaste.defer(100, this);
25343                     return;
25344                 }
25345                 
25346             };
25347         }else if(Roo.isSafari){
25348             return function(e){
25349                 var k = e.getKey();
25350                 
25351                 if(k == e.TAB){
25352                     e.stopEvent();
25353                     this.execCmd('InsertText','\t');
25354                     this.deferFocus();
25355                     return;
25356                 }
25357                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25358                     this.cleanUpPaste.defer(100, this);
25359                     return;
25360                 }
25361                 
25362              };
25363         }
25364     }(),
25365     
25366     getAllAncestors: function()
25367     {
25368         var p = this.getSelectedNode();
25369         var a = [];
25370         if (!p) {
25371             a.push(p); // push blank onto stack..
25372             p = this.getParentElement();
25373         }
25374         
25375         
25376         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25377             a.push(p);
25378             p = p.parentNode;
25379         }
25380         a.push(this.doc.body);
25381         return a;
25382     },
25383     lastSel : false,
25384     lastSelNode : false,
25385     
25386     
25387     getSelection : function() 
25388     {
25389         this.assignDocWin();
25390         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25391     },
25392     
25393     getSelectedNode: function() 
25394     {
25395         // this may only work on Gecko!!!
25396         
25397         // should we cache this!!!!
25398         
25399         
25400         
25401          
25402         var range = this.createRange(this.getSelection()).cloneRange();
25403         
25404         if (Roo.isIE) {
25405             var parent = range.parentElement();
25406             while (true) {
25407                 var testRange = range.duplicate();
25408                 testRange.moveToElementText(parent);
25409                 if (testRange.inRange(range)) {
25410                     break;
25411                 }
25412                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25413                     break;
25414                 }
25415                 parent = parent.parentElement;
25416             }
25417             return parent;
25418         }
25419         
25420         // is ancestor a text element.
25421         var ac =  range.commonAncestorContainer;
25422         if (ac.nodeType == 3) {
25423             ac = ac.parentNode;
25424         }
25425         
25426         var ar = ac.childNodes;
25427          
25428         var nodes = [];
25429         var other_nodes = [];
25430         var has_other_nodes = false;
25431         for (var i=0;i<ar.length;i++) {
25432             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25433                 continue;
25434             }
25435             // fullly contained node.
25436             
25437             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25438                 nodes.push(ar[i]);
25439                 continue;
25440             }
25441             
25442             // probably selected..
25443             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25444                 other_nodes.push(ar[i]);
25445                 continue;
25446             }
25447             // outer..
25448             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25449                 continue;
25450             }
25451             
25452             
25453             has_other_nodes = true;
25454         }
25455         if (!nodes.length && other_nodes.length) {
25456             nodes= other_nodes;
25457         }
25458         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25459             return false;
25460         }
25461         
25462         return nodes[0];
25463     },
25464     createRange: function(sel)
25465     {
25466         // this has strange effects when using with 
25467         // top toolbar - not sure if it's a great idea.
25468         //this.editor.contentWindow.focus();
25469         if (typeof sel != "undefined") {
25470             try {
25471                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25472             } catch(e) {
25473                 return this.doc.createRange();
25474             }
25475         } else {
25476             return this.doc.createRange();
25477         }
25478     },
25479     getParentElement: function()
25480     {
25481         
25482         this.assignDocWin();
25483         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25484         
25485         var range = this.createRange(sel);
25486          
25487         try {
25488             var p = range.commonAncestorContainer;
25489             while (p.nodeType == 3) { // text node
25490                 p = p.parentNode;
25491             }
25492             return p;
25493         } catch (e) {
25494             return null;
25495         }
25496     
25497     },
25498     /***
25499      *
25500      * Range intersection.. the hard stuff...
25501      *  '-1' = before
25502      *  '0' = hits..
25503      *  '1' = after.
25504      *         [ -- selected range --- ]
25505      *   [fail]                        [fail]
25506      *
25507      *    basically..
25508      *      if end is before start or  hits it. fail.
25509      *      if start is after end or hits it fail.
25510      *
25511      *   if either hits (but other is outside. - then it's not 
25512      *   
25513      *    
25514      **/
25515     
25516     
25517     // @see http://www.thismuchiknow.co.uk/?p=64.
25518     rangeIntersectsNode : function(range, node)
25519     {
25520         var nodeRange = node.ownerDocument.createRange();
25521         try {
25522             nodeRange.selectNode(node);
25523         } catch (e) {
25524             nodeRange.selectNodeContents(node);
25525         }
25526     
25527         var rangeStartRange = range.cloneRange();
25528         rangeStartRange.collapse(true);
25529     
25530         var rangeEndRange = range.cloneRange();
25531         rangeEndRange.collapse(false);
25532     
25533         var nodeStartRange = nodeRange.cloneRange();
25534         nodeStartRange.collapse(true);
25535     
25536         var nodeEndRange = nodeRange.cloneRange();
25537         nodeEndRange.collapse(false);
25538     
25539         return rangeStartRange.compareBoundaryPoints(
25540                  Range.START_TO_START, nodeEndRange) == -1 &&
25541                rangeEndRange.compareBoundaryPoints(
25542                  Range.START_TO_START, nodeStartRange) == 1;
25543         
25544          
25545     },
25546     rangeCompareNode : function(range, node)
25547     {
25548         var nodeRange = node.ownerDocument.createRange();
25549         try {
25550             nodeRange.selectNode(node);
25551         } catch (e) {
25552             nodeRange.selectNodeContents(node);
25553         }
25554         
25555         
25556         range.collapse(true);
25557     
25558         nodeRange.collapse(true);
25559      
25560         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25561         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25562          
25563         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25564         
25565         var nodeIsBefore   =  ss == 1;
25566         var nodeIsAfter    = ee == -1;
25567         
25568         if (nodeIsBefore && nodeIsAfter)
25569             return 0; // outer
25570         if (!nodeIsBefore && nodeIsAfter)
25571             return 1; //right trailed.
25572         
25573         if (nodeIsBefore && !nodeIsAfter)
25574             return 2;  // left trailed.
25575         // fully contined.
25576         return 3;
25577     },
25578
25579     // private? - in a new class?
25580     cleanUpPaste :  function()
25581     {
25582         // cleans up the whole document..
25583         Roo.log('cleanuppaste');
25584         
25585         this.cleanUpChildren(this.doc.body);
25586         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25587         if (clean != this.doc.body.innerHTML) {
25588             this.doc.body.innerHTML = clean;
25589         }
25590         
25591     },
25592     
25593     cleanWordChars : function(input) {// change the chars to hex code
25594         var he = Roo.HtmlEditorCore;
25595         
25596         var output = input;
25597         Roo.each(he.swapCodes, function(sw) { 
25598             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25599             
25600             output = output.replace(swapper, sw[1]);
25601         });
25602         
25603         return output;
25604     },
25605     
25606     
25607     cleanUpChildren : function (n)
25608     {
25609         if (!n.childNodes.length) {
25610             return;
25611         }
25612         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25613            this.cleanUpChild(n.childNodes[i]);
25614         }
25615     },
25616     
25617     
25618         
25619     
25620     cleanUpChild : function (node)
25621     {
25622         var ed = this;
25623         //console.log(node);
25624         if (node.nodeName == "#text") {
25625             // clean up silly Windows -- stuff?
25626             return; 
25627         }
25628         if (node.nodeName == "#comment") {
25629             node.parentNode.removeChild(node);
25630             // clean up silly Windows -- stuff?
25631             return; 
25632         }
25633         var lcname = node.tagName.toLowerCase();
25634         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25635         // whitelist of tags..
25636         
25637         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25638             // remove node.
25639             node.parentNode.removeChild(node);
25640             return;
25641             
25642         }
25643         
25644         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25645         
25646         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25647         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25648         
25649         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25650         //    remove_keep_children = true;
25651         //}
25652         
25653         if (remove_keep_children) {
25654             this.cleanUpChildren(node);
25655             // inserts everything just before this node...
25656             while (node.childNodes.length) {
25657                 var cn = node.childNodes[0];
25658                 node.removeChild(cn);
25659                 node.parentNode.insertBefore(cn, node);
25660             }
25661             node.parentNode.removeChild(node);
25662             return;
25663         }
25664         
25665         if (!node.attributes || !node.attributes.length) {
25666             this.cleanUpChildren(node);
25667             return;
25668         }
25669         
25670         function cleanAttr(n,v)
25671         {
25672             
25673             if (v.match(/^\./) || v.match(/^\//)) {
25674                 return;
25675             }
25676             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25677                 return;
25678             }
25679             if (v.match(/^#/)) {
25680                 return;
25681             }
25682 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25683             node.removeAttribute(n);
25684             
25685         }
25686         
25687         var cwhite = this.cwhite;
25688         var cblack = this.cblack;
25689             
25690         function cleanStyle(n,v)
25691         {
25692             if (v.match(/expression/)) { //XSS?? should we even bother..
25693                 node.removeAttribute(n);
25694                 return;
25695             }
25696             
25697             var parts = v.split(/;/);
25698             var clean = [];
25699             
25700             Roo.each(parts, function(p) {
25701                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25702                 if (!p.length) {
25703                     return true;
25704                 }
25705                 var l = p.split(':').shift().replace(/\s+/g,'');
25706                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25707                 
25708                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25709 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25710                     //node.removeAttribute(n);
25711                     return true;
25712                 }
25713                 //Roo.log()
25714                 // only allow 'c whitelisted system attributes'
25715                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25716 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25717                     //node.removeAttribute(n);
25718                     return true;
25719                 }
25720                 
25721                 
25722                  
25723                 
25724                 clean.push(p);
25725                 return true;
25726             });
25727             if (clean.length) { 
25728                 node.setAttribute(n, clean.join(';'));
25729             } else {
25730                 node.removeAttribute(n);
25731             }
25732             
25733         }
25734         
25735         
25736         for (var i = node.attributes.length-1; i > -1 ; i--) {
25737             var a = node.attributes[i];
25738             //console.log(a);
25739             
25740             if (a.name.toLowerCase().substr(0,2)=='on')  {
25741                 node.removeAttribute(a.name);
25742                 continue;
25743             }
25744             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25745                 node.removeAttribute(a.name);
25746                 continue;
25747             }
25748             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25749                 cleanAttr(a.name,a.value); // fixme..
25750                 continue;
25751             }
25752             if (a.name == 'style') {
25753                 cleanStyle(a.name,a.value);
25754                 continue;
25755             }
25756             /// clean up MS crap..
25757             // tecnically this should be a list of valid class'es..
25758             
25759             
25760             if (a.name == 'class') {
25761                 if (a.value.match(/^Mso/)) {
25762                     node.className = '';
25763                 }
25764                 
25765                 if (a.value.match(/body/)) {
25766                     node.className = '';
25767                 }
25768                 continue;
25769             }
25770             
25771             // style cleanup!?
25772             // class cleanup?
25773             
25774         }
25775         
25776         
25777         this.cleanUpChildren(node);
25778         
25779         
25780     },
25781     /**
25782      * Clean up MS wordisms...
25783      */
25784     cleanWord : function(node)
25785     {
25786         var _t = this;
25787         var cleanWordChildren = function()
25788         {
25789             if (!node.childNodes.length) {
25790                 return;
25791             }
25792             for (var i = node.childNodes.length-1; i > -1 ; i--) {
25793                _t.cleanWord(node.childNodes[i]);
25794             }
25795         }
25796         
25797         
25798         if (!node) {
25799             this.cleanWord(this.doc.body);
25800             return;
25801         }
25802         if (node.nodeName == "#text") {
25803             // clean up silly Windows -- stuff?
25804             return; 
25805         }
25806         if (node.nodeName == "#comment") {
25807             node.parentNode.removeChild(node);
25808             // clean up silly Windows -- stuff?
25809             return; 
25810         }
25811         
25812         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25813             node.parentNode.removeChild(node);
25814             return;
25815         }
25816         
25817         // remove - but keep children..
25818         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25819             while (node.childNodes.length) {
25820                 var cn = node.childNodes[0];
25821                 node.removeChild(cn);
25822                 node.parentNode.insertBefore(cn, node);
25823             }
25824             node.parentNode.removeChild(node);
25825             cleanWordChildren();
25826             return;
25827         }
25828         // clean styles
25829         if (node.className.length) {
25830             
25831             var cn = node.className.split(/\W+/);
25832             var cna = [];
25833             Roo.each(cn, function(cls) {
25834                 if (cls.match(/Mso[a-zA-Z]+/)) {
25835                     return;
25836                 }
25837                 cna.push(cls);
25838             });
25839             node.className = cna.length ? cna.join(' ') : '';
25840             if (!cna.length) {
25841                 node.removeAttribute("class");
25842             }
25843         }
25844         
25845         if (node.hasAttribute("lang")) {
25846             node.removeAttribute("lang");
25847         }
25848         
25849         if (node.hasAttribute("style")) {
25850             
25851             var styles = node.getAttribute("style").split(";");
25852             var nstyle = [];
25853             Roo.each(styles, function(s) {
25854                 if (!s.match(/:/)) {
25855                     return;
25856                 }
25857                 var kv = s.split(":");
25858                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25859                     return;
25860                 }
25861                 // what ever is left... we allow.
25862                 nstyle.push(s);
25863             });
25864             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25865             if (!nstyle.length) {
25866                 node.removeAttribute('style');
25867             }
25868         }
25869         
25870         cleanWordChildren();
25871         
25872         
25873     },
25874     domToHTML : function(currentElement, depth, nopadtext) {
25875         
25876         depth = depth || 0;
25877         nopadtext = nopadtext || false;
25878     
25879         if (!currentElement) {
25880             return this.domToHTML(this.doc.body);
25881         }
25882         
25883         //Roo.log(currentElement);
25884         var j;
25885         var allText = false;
25886         var nodeName = currentElement.nodeName;
25887         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25888         
25889         if  (nodeName == '#text') {
25890             return currentElement.nodeValue;
25891         }
25892         
25893         
25894         var ret = '';
25895         if (nodeName != 'BODY') {
25896              
25897             var i = 0;
25898             // Prints the node tagName, such as <A>, <IMG>, etc
25899             if (tagName) {
25900                 var attr = [];
25901                 for(i = 0; i < currentElement.attributes.length;i++) {
25902                     // quoting?
25903                     var aname = currentElement.attributes.item(i).name;
25904                     if (!currentElement.attributes.item(i).value.length) {
25905                         continue;
25906                     }
25907                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25908                 }
25909                 
25910                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25911             } 
25912             else {
25913                 
25914                 // eack
25915             }
25916         } else {
25917             tagName = false;
25918         }
25919         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25920             return ret;
25921         }
25922         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25923             nopadtext = true;
25924         }
25925         
25926         
25927         // Traverse the tree
25928         i = 0;
25929         var currentElementChild = currentElement.childNodes.item(i);
25930         var allText = true;
25931         var innerHTML  = '';
25932         lastnode = '';
25933         while (currentElementChild) {
25934             // Formatting code (indent the tree so it looks nice on the screen)
25935             var nopad = nopadtext;
25936             if (lastnode == 'SPAN') {
25937                 nopad  = true;
25938             }
25939             // text
25940             if  (currentElementChild.nodeName == '#text') {
25941                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25942                 if (!nopad && toadd.length > 80) {
25943                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25944                 }
25945                 innerHTML  += toadd;
25946                 
25947                 i++;
25948                 currentElementChild = currentElement.childNodes.item(i);
25949                 lastNode = '';
25950                 continue;
25951             }
25952             allText = false;
25953             
25954             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25955                 
25956             // Recursively traverse the tree structure of the child node
25957             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25958             lastnode = currentElementChild.nodeName;
25959             i++;
25960             currentElementChild=currentElement.childNodes.item(i);
25961         }
25962         
25963         ret += innerHTML;
25964         
25965         if (!allText) {
25966                 // The remaining code is mostly for formatting the tree
25967             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25968         }
25969         
25970         
25971         if (tagName) {
25972             ret+= "</"+tagName+">";
25973         }
25974         return ret;
25975         
25976     },
25977         
25978     applyBlacklists : function()
25979     {
25980         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25981         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25982         
25983         this.white = [];
25984         this.black = [];
25985         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25986             if (b.indexOf(tag) > -1) {
25987                 return;
25988             }
25989             this.white.push(tag);
25990             
25991         }, this);
25992         
25993         Roo.each(w, function(tag) {
25994             if (b.indexOf(tag) > -1) {
25995                 return;
25996             }
25997             if (this.white.indexOf(tag) > -1) {
25998                 return;
25999             }
26000             this.white.push(tag);
26001             
26002         }, this);
26003         
26004         
26005         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26006             if (w.indexOf(tag) > -1) {
26007                 return;
26008             }
26009             this.black.push(tag);
26010             
26011         }, this);
26012         
26013         Roo.each(b, function(tag) {
26014             if (w.indexOf(tag) > -1) {
26015                 return;
26016             }
26017             if (this.black.indexOf(tag) > -1) {
26018                 return;
26019             }
26020             this.black.push(tag);
26021             
26022         }, this);
26023         
26024         
26025         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26026         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26027         
26028         this.cwhite = [];
26029         this.cblack = [];
26030         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26031             if (b.indexOf(tag) > -1) {
26032                 return;
26033             }
26034             this.cwhite.push(tag);
26035             
26036         }, this);
26037         
26038         Roo.each(w, function(tag) {
26039             if (b.indexOf(tag) > -1) {
26040                 return;
26041             }
26042             if (this.cwhite.indexOf(tag) > -1) {
26043                 return;
26044             }
26045             this.cwhite.push(tag);
26046             
26047         }, this);
26048         
26049         
26050         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26051             if (w.indexOf(tag) > -1) {
26052                 return;
26053             }
26054             this.cblack.push(tag);
26055             
26056         }, this);
26057         
26058         Roo.each(b, function(tag) {
26059             if (w.indexOf(tag) > -1) {
26060                 return;
26061             }
26062             if (this.cblack.indexOf(tag) > -1) {
26063                 return;
26064             }
26065             this.cblack.push(tag);
26066             
26067         }, this);
26068     }
26069     
26070     // hide stuff that is not compatible
26071     /**
26072      * @event blur
26073      * @hide
26074      */
26075     /**
26076      * @event change
26077      * @hide
26078      */
26079     /**
26080      * @event focus
26081      * @hide
26082      */
26083     /**
26084      * @event specialkey
26085      * @hide
26086      */
26087     /**
26088      * @cfg {String} fieldClass @hide
26089      */
26090     /**
26091      * @cfg {String} focusClass @hide
26092      */
26093     /**
26094      * @cfg {String} autoCreate @hide
26095      */
26096     /**
26097      * @cfg {String} inputType @hide
26098      */
26099     /**
26100      * @cfg {String} invalidClass @hide
26101      */
26102     /**
26103      * @cfg {String} invalidText @hide
26104      */
26105     /**
26106      * @cfg {String} msgFx @hide
26107      */
26108     /**
26109      * @cfg {String} validateOnBlur @hide
26110      */
26111 });
26112
26113 Roo.HtmlEditorCore.white = [
26114         'area', 'br', 'img', 'input', 'hr', 'wbr',
26115         
26116        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26117        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26118        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26119        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26120        'table',   'ul',         'xmp', 
26121        
26122        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26123       'thead',   'tr', 
26124      
26125       'dir', 'menu', 'ol', 'ul', 'dl',
26126        
26127       'embed',  'object'
26128 ];
26129
26130
26131 Roo.HtmlEditorCore.black = [
26132     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26133         'applet', // 
26134         'base',   'basefont', 'bgsound', 'blink',  'body', 
26135         'frame',  'frameset', 'head',    'html',   'ilayer', 
26136         'iframe', 'layer',  'link',     'meta',    'object',   
26137         'script', 'style' ,'title',  'xml' // clean later..
26138 ];
26139 Roo.HtmlEditorCore.clean = [
26140     'script', 'style', 'title', 'xml'
26141 ];
26142 Roo.HtmlEditorCore.remove = [
26143     'font'
26144 ];
26145 // attributes..
26146
26147 Roo.HtmlEditorCore.ablack = [
26148     'on'
26149 ];
26150     
26151 Roo.HtmlEditorCore.aclean = [ 
26152     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26153 ];
26154
26155 // protocols..
26156 Roo.HtmlEditorCore.pwhite= [
26157         'http',  'https',  'mailto'
26158 ];
26159
26160 // white listed style attributes.
26161 Roo.HtmlEditorCore.cwhite= [
26162       //  'text-align', /// default is to allow most things..
26163       
26164          
26165 //        'font-size'//??
26166 ];
26167
26168 // black listed style attributes.
26169 Roo.HtmlEditorCore.cblack= [
26170       //  'font-size' -- this can be set by the project 
26171 ];
26172
26173
26174 Roo.HtmlEditorCore.swapCodes   =[ 
26175     [    8211, "--" ], 
26176     [    8212, "--" ], 
26177     [    8216,  "'" ],  
26178     [    8217, "'" ],  
26179     [    8220, '"' ],  
26180     [    8221, '"' ],  
26181     [    8226, "*" ],  
26182     [    8230, "..." ]
26183 ]; 
26184
26185     //<script type="text/javascript">
26186
26187 /*
26188  * Ext JS Library 1.1.1
26189  * Copyright(c) 2006-2007, Ext JS, LLC.
26190  * Licence LGPL
26191  * 
26192  */
26193  
26194  
26195 Roo.form.HtmlEditor = function(config){
26196     
26197     
26198     
26199     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26200     
26201     if (!this.toolbars) {
26202         this.toolbars = [];
26203     }
26204     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26205     
26206     
26207 };
26208
26209 /**
26210  * @class Roo.form.HtmlEditor
26211  * @extends Roo.form.Field
26212  * Provides a lightweight HTML Editor component.
26213  *
26214  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26215  * 
26216  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26217  * supported by this editor.</b><br/><br/>
26218  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26219  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26220  */
26221 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26222     /**
26223      * @cfg {Boolean} clearUp
26224      */
26225     clearUp : true,
26226       /**
26227      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26228      */
26229     toolbars : false,
26230    
26231      /**
26232      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26233      *                        Roo.resizable.
26234      */
26235     resizable : false,
26236      /**
26237      * @cfg {Number} height (in pixels)
26238      */   
26239     height: 300,
26240    /**
26241      * @cfg {Number} width (in pixels)
26242      */   
26243     width: 500,
26244     
26245     /**
26246      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26247      * 
26248      */
26249     stylesheets: false,
26250     
26251     
26252      /**
26253      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26254      * 
26255      */
26256     cblack: false,
26257     /**
26258      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26259      * 
26260      */
26261     cwhite: false,
26262     
26263      /**
26264      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26265      * 
26266      */
26267     black: false,
26268     /**
26269      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26270      * 
26271      */
26272     white: false,
26273     
26274     // id of frame..
26275     frameId: false,
26276     
26277     // private properties
26278     validationEvent : false,
26279     deferHeight: true,
26280     initialized : false,
26281     activated : false,
26282     
26283     onFocus : Roo.emptyFn,
26284     iframePad:3,
26285     hideMode:'offsets',
26286     
26287     actionMode : 'container', // defaults to hiding it...
26288     
26289     defaultAutoCreate : { // modified by initCompnoent..
26290         tag: "textarea",
26291         style:"width:500px;height:300px;",
26292         autocomplete: "off"
26293     },
26294
26295     // private
26296     initComponent : function(){
26297         this.addEvents({
26298             /**
26299              * @event initialize
26300              * Fires when the editor is fully initialized (including the iframe)
26301              * @param {HtmlEditor} this
26302              */
26303             initialize: true,
26304             /**
26305              * @event activate
26306              * Fires when the editor is first receives the focus. Any insertion must wait
26307              * until after this event.
26308              * @param {HtmlEditor} this
26309              */
26310             activate: true,
26311              /**
26312              * @event beforesync
26313              * Fires before the textarea is updated with content from the editor iframe. Return false
26314              * to cancel the sync.
26315              * @param {HtmlEditor} this
26316              * @param {String} html
26317              */
26318             beforesync: true,
26319              /**
26320              * @event beforepush
26321              * Fires before the iframe editor is updated with content from the textarea. Return false
26322              * to cancel the push.
26323              * @param {HtmlEditor} this
26324              * @param {String} html
26325              */
26326             beforepush: true,
26327              /**
26328              * @event sync
26329              * Fires when the textarea is updated with content from the editor iframe.
26330              * @param {HtmlEditor} this
26331              * @param {String} html
26332              */
26333             sync: true,
26334              /**
26335              * @event push
26336              * Fires when the iframe editor is updated with content from the textarea.
26337              * @param {HtmlEditor} this
26338              * @param {String} html
26339              */
26340             push: true,
26341              /**
26342              * @event editmodechange
26343              * Fires when the editor switches edit modes
26344              * @param {HtmlEditor} this
26345              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26346              */
26347             editmodechange: true,
26348             /**
26349              * @event editorevent
26350              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26351              * @param {HtmlEditor} this
26352              */
26353             editorevent: true,
26354             /**
26355              * @event firstfocus
26356              * Fires when on first focus - needed by toolbars..
26357              * @param {HtmlEditor} this
26358              */
26359             firstfocus: true,
26360             /**
26361              * @event autosave
26362              * Auto save the htmlEditor value as a file into Events
26363              * @param {HtmlEditor} this
26364              */
26365             autosave: true,
26366             /**
26367              * @event savedpreview
26368              * preview the saved version of htmlEditor
26369              * @param {HtmlEditor} this
26370              */
26371             savedpreview: true
26372         });
26373         this.defaultAutoCreate =  {
26374             tag: "textarea",
26375             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26376             autocomplete: "off"
26377         };
26378     },
26379
26380     /**
26381      * Protected method that will not generally be called directly. It
26382      * is called when the editor creates its toolbar. Override this method if you need to
26383      * add custom toolbar buttons.
26384      * @param {HtmlEditor} editor
26385      */
26386     createToolbar : function(editor){
26387         Roo.log("create toolbars");
26388         if (!editor.toolbars || !editor.toolbars.length) {
26389             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26390         }
26391         
26392         for (var i =0 ; i < editor.toolbars.length;i++) {
26393             editor.toolbars[i] = Roo.factory(
26394                     typeof(editor.toolbars[i]) == 'string' ?
26395                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26396                 Roo.form.HtmlEditor);
26397             editor.toolbars[i].init(editor);
26398         }
26399          
26400         
26401     },
26402
26403      
26404     // private
26405     onRender : function(ct, position)
26406     {
26407         var _t = this;
26408         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26409         
26410         this.wrap = this.el.wrap({
26411             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26412         });
26413         
26414         this.editorcore.onRender(ct, position);
26415          
26416         if (this.resizable) {
26417             this.resizeEl = new Roo.Resizable(this.wrap, {
26418                 pinned : true,
26419                 wrap: true,
26420                 dynamic : true,
26421                 minHeight : this.height,
26422                 height: this.height,
26423                 handles : this.resizable,
26424                 width: this.width,
26425                 listeners : {
26426                     resize : function(r, w, h) {
26427                         _t.onResize(w,h); // -something
26428                     }
26429                 }
26430             });
26431             
26432         }
26433         this.createToolbar(this);
26434        
26435         
26436         if(!this.width){
26437             this.setSize(this.wrap.getSize());
26438         }
26439         if (this.resizeEl) {
26440             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26441             // should trigger onReize..
26442         }
26443         
26444 //        if(this.autosave && this.w){
26445 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26446 //        }
26447     },
26448
26449     // private
26450     onResize : function(w, h)
26451     {
26452         //Roo.log('resize: ' +w + ',' + h );
26453         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26454         var ew = false;
26455         var eh = false;
26456         
26457         if(this.el ){
26458             if(typeof w == 'number'){
26459                 var aw = w - this.wrap.getFrameWidth('lr');
26460                 this.el.setWidth(this.adjustWidth('textarea', aw));
26461                 ew = aw;
26462             }
26463             if(typeof h == 'number'){
26464                 var tbh = 0;
26465                 for (var i =0; i < this.toolbars.length;i++) {
26466                     // fixme - ask toolbars for heights?
26467                     tbh += this.toolbars[i].tb.el.getHeight();
26468                     if (this.toolbars[i].footer) {
26469                         tbh += this.toolbars[i].footer.el.getHeight();
26470                     }
26471                 }
26472                 
26473                 
26474                 
26475                 
26476                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26477                 ah -= 5; // knock a few pixes off for look..
26478                 this.el.setHeight(this.adjustWidth('textarea', ah));
26479                 var eh = ah;
26480             }
26481         }
26482         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26483         this.editorcore.onResize(ew,eh);
26484         
26485     },
26486
26487     /**
26488      * Toggles the editor between standard and source edit mode.
26489      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26490      */
26491     toggleSourceEdit : function(sourceEditMode)
26492     {
26493         this.editorcore.toggleSourceEdit(sourceEditMode);
26494         
26495         if(this.editorcore.sourceEditMode){
26496             Roo.log('editor - showing textarea');
26497             
26498 //            Roo.log('in');
26499 //            Roo.log(this.syncValue());
26500             this.editorcore.syncValue();
26501             this.el.removeClass('x-hidden');
26502             this.el.dom.removeAttribute('tabIndex');
26503             this.el.focus();
26504         }else{
26505             Roo.log('editor - hiding textarea');
26506 //            Roo.log('out')
26507 //            Roo.log(this.pushValue()); 
26508             this.editorcore.pushValue();
26509             
26510             this.el.addClass('x-hidden');
26511             this.el.dom.setAttribute('tabIndex', -1);
26512             //this.deferFocus();
26513         }
26514          
26515         this.setSize(this.wrap.getSize());
26516         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26517     },
26518  
26519     // private (for BoxComponent)
26520     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26521
26522     // private (for BoxComponent)
26523     getResizeEl : function(){
26524         return this.wrap;
26525     },
26526
26527     // private (for BoxComponent)
26528     getPositionEl : function(){
26529         return this.wrap;
26530     },
26531
26532     // private
26533     initEvents : function(){
26534         this.originalValue = this.getValue();
26535     },
26536
26537     /**
26538      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26539      * @method
26540      */
26541     markInvalid : Roo.emptyFn,
26542     /**
26543      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26544      * @method
26545      */
26546     clearInvalid : Roo.emptyFn,
26547
26548     setValue : function(v){
26549         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26550         this.editorcore.pushValue();
26551     },
26552
26553      
26554     // private
26555     deferFocus : function(){
26556         this.focus.defer(10, this);
26557     },
26558
26559     // doc'ed in Field
26560     focus : function(){
26561         this.editorcore.focus();
26562         
26563     },
26564       
26565
26566     // private
26567     onDestroy : function(){
26568         
26569         
26570         
26571         if(this.rendered){
26572             
26573             for (var i =0; i < this.toolbars.length;i++) {
26574                 // fixme - ask toolbars for heights?
26575                 this.toolbars[i].onDestroy();
26576             }
26577             
26578             this.wrap.dom.innerHTML = '';
26579             this.wrap.remove();
26580         }
26581     },
26582
26583     // private
26584     onFirstFocus : function(){
26585         //Roo.log("onFirstFocus");
26586         this.editorcore.onFirstFocus();
26587          for (var i =0; i < this.toolbars.length;i++) {
26588             this.toolbars[i].onFirstFocus();
26589         }
26590         
26591     },
26592     
26593     // private
26594     syncValue : function()
26595     {
26596         this.editorcore.syncValue();
26597     },
26598     
26599     pushValue : function()
26600     {
26601         this.editorcore.pushValue();
26602     }
26603      
26604     
26605     // hide stuff that is not compatible
26606     /**
26607      * @event blur
26608      * @hide
26609      */
26610     /**
26611      * @event change
26612      * @hide
26613      */
26614     /**
26615      * @event focus
26616      * @hide
26617      */
26618     /**
26619      * @event specialkey
26620      * @hide
26621      */
26622     /**
26623      * @cfg {String} fieldClass @hide
26624      */
26625     /**
26626      * @cfg {String} focusClass @hide
26627      */
26628     /**
26629      * @cfg {String} autoCreate @hide
26630      */
26631     /**
26632      * @cfg {String} inputType @hide
26633      */
26634     /**
26635      * @cfg {String} invalidClass @hide
26636      */
26637     /**
26638      * @cfg {String} invalidText @hide
26639      */
26640     /**
26641      * @cfg {String} msgFx @hide
26642      */
26643     /**
26644      * @cfg {String} validateOnBlur @hide
26645      */
26646 });
26647  
26648     // <script type="text/javascript">
26649 /*
26650  * Based on
26651  * Ext JS Library 1.1.1
26652  * Copyright(c) 2006-2007, Ext JS, LLC.
26653  *  
26654  
26655  */
26656
26657 /**
26658  * @class Roo.form.HtmlEditorToolbar1
26659  * Basic Toolbar
26660  * 
26661  * Usage:
26662  *
26663  new Roo.form.HtmlEditor({
26664     ....
26665     toolbars : [
26666         new Roo.form.HtmlEditorToolbar1({
26667             disable : { fonts: 1 , format: 1, ..., ... , ...],
26668             btns : [ .... ]
26669         })
26670     }
26671      
26672  * 
26673  * @cfg {Object} disable List of elements to disable..
26674  * @cfg {Array} btns List of additional buttons.
26675  * 
26676  * 
26677  * NEEDS Extra CSS? 
26678  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26679  */
26680  
26681 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26682 {
26683     
26684     Roo.apply(this, config);
26685     
26686     // default disabled, based on 'good practice'..
26687     this.disable = this.disable || {};
26688     Roo.applyIf(this.disable, {
26689         fontSize : true,
26690         colors : true,
26691         specialElements : true
26692     });
26693     
26694     
26695     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26696     // dont call parent... till later.
26697 }
26698
26699 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26700     
26701     tb: false,
26702     
26703     rendered: false,
26704     
26705     editor : false,
26706     editorcore : false,
26707     /**
26708      * @cfg {Object} disable  List of toolbar elements to disable
26709          
26710      */
26711     disable : false,
26712     
26713     
26714      /**
26715      * @cfg {String} createLinkText The default text for the create link prompt
26716      */
26717     createLinkText : 'Please enter the URL for the link:',
26718     /**
26719      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
26720      */
26721     defaultLinkValue : 'http:/'+'/',
26722    
26723     
26724       /**
26725      * @cfg {Array} fontFamilies An array of available font families
26726      */
26727     fontFamilies : [
26728         'Arial',
26729         'Courier New',
26730         'Tahoma',
26731         'Times New Roman',
26732         'Verdana'
26733     ],
26734     
26735     specialChars : [
26736            "&#169;",
26737           "&#174;",     
26738           "&#8482;",    
26739           "&#163;" ,    
26740          // "&#8212;",    
26741           "&#8230;",    
26742           "&#247;" ,    
26743         //  "&#225;" ,     ?? a acute?
26744            "&#8364;"    , //Euro
26745        //   "&#8220;"    ,
26746         //  "&#8221;"    ,
26747         //  "&#8226;"    ,
26748           "&#176;"  //   , // degrees
26749
26750          // "&#233;"     , // e ecute
26751          // "&#250;"     , // u ecute?
26752     ],
26753     
26754     specialElements : [
26755         {
26756             text: "Insert Table",
26757             xtype: 'MenuItem',
26758             xns : Roo.Menu,
26759             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26760                 
26761         },
26762         {    
26763             text: "Insert Image",
26764             xtype: 'MenuItem',
26765             xns : Roo.Menu,
26766             ihtml : '<img src="about:blank"/>'
26767             
26768         }
26769         
26770          
26771     ],
26772     
26773     
26774     inputElements : [ 
26775             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26776             "input:submit", "input:button", "select", "textarea", "label" ],
26777     formats : [
26778         ["p"] ,  
26779         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26780         ["pre"],[ "code"], 
26781         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26782         ['div'],['span']
26783     ],
26784     
26785     cleanStyles : [
26786         "font-size"
26787     ],
26788      /**
26789      * @cfg {String} defaultFont default font to use.
26790      */
26791     defaultFont: 'tahoma',
26792    
26793     fontSelect : false,
26794     
26795     
26796     formatCombo : false,
26797     
26798     init : function(editor)
26799     {
26800         this.editor = editor;
26801         this.editorcore = editor.editorcore ? editor.editorcore : editor;
26802         var editorcore = this.editorcore;
26803         
26804         var _t = this;
26805         
26806         var fid = editorcore.frameId;
26807         var etb = this;
26808         function btn(id, toggle, handler){
26809             var xid = fid + '-'+ id ;
26810             return {
26811                 id : xid,
26812                 cmd : id,
26813                 cls : 'x-btn-icon x-edit-'+id,
26814                 enableToggle:toggle !== false,
26815                 scope: _t, // was editor...
26816                 handler:handler||_t.relayBtnCmd,
26817                 clickEvent:'mousedown',
26818                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26819                 tabIndex:-1
26820             };
26821         }
26822         
26823         
26824         
26825         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26826         this.tb = tb;
26827          // stop form submits
26828         tb.el.on('click', function(e){
26829             e.preventDefault(); // what does this do?
26830         });
26831
26832         if(!this.disable.font) { // && !Roo.isSafari){
26833             /* why no safari for fonts 
26834             editor.fontSelect = tb.el.createChild({
26835                 tag:'select',
26836                 tabIndex: -1,
26837                 cls:'x-font-select',
26838                 html: this.createFontOptions()
26839             });
26840             
26841             editor.fontSelect.on('change', function(){
26842                 var font = editor.fontSelect.dom.value;
26843                 editor.relayCmd('fontname', font);
26844                 editor.deferFocus();
26845             }, editor);
26846             
26847             tb.add(
26848                 editor.fontSelect.dom,
26849                 '-'
26850             );
26851             */
26852             
26853         };
26854         if(!this.disable.formats){
26855             this.formatCombo = new Roo.form.ComboBox({
26856                 store: new Roo.data.SimpleStore({
26857                     id : 'tag',
26858                     fields: ['tag'],
26859                     data : this.formats // from states.js
26860                 }),
26861                 blockFocus : true,
26862                 name : '',
26863                 //autoCreate : {tag: "div",  size: "20"},
26864                 displayField:'tag',
26865                 typeAhead: false,
26866                 mode: 'local',
26867                 editable : false,
26868                 triggerAction: 'all',
26869                 emptyText:'Add tag',
26870                 selectOnFocus:true,
26871                 width:135,
26872                 listeners : {
26873                     'select': function(c, r, i) {
26874                         editorcore.insertTag(r.get('tag'));
26875                         editor.focus();
26876                     }
26877                 }
26878
26879             });
26880             tb.addField(this.formatCombo);
26881             
26882         }
26883         
26884         if(!this.disable.format){
26885             tb.add(
26886                 btn('bold'),
26887                 btn('italic'),
26888                 btn('underline')
26889             );
26890         };
26891         if(!this.disable.fontSize){
26892             tb.add(
26893                 '-',
26894                 
26895                 
26896                 btn('increasefontsize', false, editorcore.adjustFont),
26897                 btn('decreasefontsize', false, editorcore.adjustFont)
26898             );
26899         };
26900         
26901         
26902         if(!this.disable.colors){
26903             tb.add(
26904                 '-', {
26905                     id:editorcore.frameId +'-forecolor',
26906                     cls:'x-btn-icon x-edit-forecolor',
26907                     clickEvent:'mousedown',
26908                     tooltip: this.buttonTips['forecolor'] || undefined,
26909                     tabIndex:-1,
26910                     menu : new Roo.menu.ColorMenu({
26911                         allowReselect: true,
26912                         focus: Roo.emptyFn,
26913                         value:'000000',
26914                         plain:true,
26915                         selectHandler: function(cp, color){
26916                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26917                             editor.deferFocus();
26918                         },
26919                         scope: editorcore,
26920                         clickEvent:'mousedown'
26921                     })
26922                 }, {
26923                     id:editorcore.frameId +'backcolor',
26924                     cls:'x-btn-icon x-edit-backcolor',
26925                     clickEvent:'mousedown',
26926                     tooltip: this.buttonTips['backcolor'] || undefined,
26927                     tabIndex:-1,
26928                     menu : new Roo.menu.ColorMenu({
26929                         focus: Roo.emptyFn,
26930                         value:'FFFFFF',
26931                         plain:true,
26932                         allowReselect: true,
26933                         selectHandler: function(cp, color){
26934                             if(Roo.isGecko){
26935                                 editorcore.execCmd('useCSS', false);
26936                                 editorcore.execCmd('hilitecolor', color);
26937                                 editorcore.execCmd('useCSS', true);
26938                                 editor.deferFocus();
26939                             }else{
26940                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26941                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26942                                 editor.deferFocus();
26943                             }
26944                         },
26945                         scope:editorcore,
26946                         clickEvent:'mousedown'
26947                     })
26948                 }
26949             );
26950         };
26951         // now add all the items...
26952         
26953
26954         if(!this.disable.alignments){
26955             tb.add(
26956                 '-',
26957                 btn('justifyleft'),
26958                 btn('justifycenter'),
26959                 btn('justifyright')
26960             );
26961         };
26962
26963         //if(!Roo.isSafari){
26964             if(!this.disable.links){
26965                 tb.add(
26966                     '-',
26967                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
26968                 );
26969             };
26970
26971             if(!this.disable.lists){
26972                 tb.add(
26973                     '-',
26974                     btn('insertorderedlist'),
26975                     btn('insertunorderedlist')
26976                 );
26977             }
26978             if(!this.disable.sourceEdit){
26979                 tb.add(
26980                     '-',
26981                     btn('sourceedit', true, function(btn){
26982                         Roo.log(this);
26983                         this.toggleSourceEdit(btn.pressed);
26984                     })
26985                 );
26986             }
26987         //}
26988         
26989         var smenu = { };
26990         // special menu.. - needs to be tidied up..
26991         if (!this.disable.special) {
26992             smenu = {
26993                 text: "&#169;",
26994                 cls: 'x-edit-none',
26995                 
26996                 menu : {
26997                     items : []
26998                 }
26999             };
27000             for (var i =0; i < this.specialChars.length; i++) {
27001                 smenu.menu.items.push({
27002                     
27003                     html: this.specialChars[i],
27004                     handler: function(a,b) {
27005                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27006                         //editor.insertAtCursor(a.html);
27007                         
27008                     },
27009                     tabIndex:-1
27010                 });
27011             }
27012             
27013             
27014             tb.add(smenu);
27015             
27016             
27017         }
27018         
27019         var cmenu = { };
27020         if (!this.disable.cleanStyles) {
27021             cmenu = {
27022                 cls: 'x-btn-icon x-btn-clear',
27023                 
27024                 menu : {
27025                     items : []
27026                 }
27027             };
27028             for (var i =0; i < this.cleanStyles.length; i++) {
27029                 cmenu.menu.items.push({
27030                     actiontype : this.cleanStyles[i],
27031                     html: 'Remove ' + this.cleanStyles[i],
27032                     handler: function(a,b) {
27033                         Roo.log(a);
27034                         Roo.log(b);
27035                         var c = Roo.get(editorcore.doc.body);
27036                         c.select('[style]').each(function(s) {
27037                             s.dom.style.removeProperty(a.actiontype);
27038                         });
27039                         editorcore.syncValue();
27040                     },
27041                     tabIndex:-1
27042                 });
27043             }
27044             cmenu.menu.items.push({
27045                 actiontype : 'word',
27046                 html: 'Remove MS Word Formating',
27047                 handler: function(a,b) {
27048                     editorcore.cleanWord();
27049                     editorcore.syncValue();
27050                 },
27051                 tabIndex:-1
27052             });
27053             
27054             cmenu.menu.items.push({
27055                 actiontype : 'all',
27056                 html: 'Remove All Styles',
27057                 handler: function(a,b) {
27058                     
27059                     var c = Roo.get(editorcore.doc.body);
27060                     c.select('[style]').each(function(s) {
27061                         s.dom.removeAttribute('style');
27062                     });
27063                     editorcore.syncValue();
27064                 },
27065                 tabIndex:-1
27066             });
27067              cmenu.menu.items.push({
27068                 actiontype : 'word',
27069                 html: 'Tidy HTML Source',
27070                 handler: function(a,b) {
27071                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27072                     editorcore.syncValue();
27073                 },
27074                 tabIndex:-1
27075             });
27076             
27077             
27078             tb.add(cmenu);
27079         }
27080          
27081         if (!this.disable.specialElements) {
27082             var semenu = {
27083                 text: "Other;",
27084                 cls: 'x-edit-none',
27085                 menu : {
27086                     items : []
27087                 }
27088             };
27089             for (var i =0; i < this.specialElements.length; i++) {
27090                 semenu.menu.items.push(
27091                     Roo.apply({ 
27092                         handler: function(a,b) {
27093                             editor.insertAtCursor(this.ihtml);
27094                         }
27095                     }, this.specialElements[i])
27096                 );
27097                     
27098             }
27099             
27100             tb.add(semenu);
27101             
27102             
27103         }
27104          
27105         
27106         if (this.btns) {
27107             for(var i =0; i< this.btns.length;i++) {
27108                 var b = Roo.factory(this.btns[i],Roo.form);
27109                 b.cls =  'x-edit-none';
27110                 b.scope = editorcore;
27111                 tb.add(b);
27112             }
27113         
27114         }
27115         
27116         
27117         
27118         // disable everything...
27119         
27120         this.tb.items.each(function(item){
27121            if(item.id != editorcore.frameId+ '-sourceedit'){
27122                 item.disable();
27123             }
27124         });
27125         this.rendered = true;
27126         
27127         // the all the btns;
27128         editor.on('editorevent', this.updateToolbar, this);
27129         // other toolbars need to implement this..
27130         //editor.on('editmodechange', this.updateToolbar, this);
27131     },
27132     
27133     
27134     relayBtnCmd : function(btn) {
27135         this.editorcore.relayCmd(btn.cmd);
27136     },
27137     // private used internally
27138     createLink : function(){
27139         Roo.log("create link?");
27140         var url = prompt(this.createLinkText, this.defaultLinkValue);
27141         if(url && url != 'http:/'+'/'){
27142             this.editorcore.relayCmd('createlink', url);
27143         }
27144     },
27145
27146     
27147     /**
27148      * Protected method that will not generally be called directly. It triggers
27149      * a toolbar update by reading the markup state of the current selection in the editor.
27150      */
27151     updateToolbar: function(){
27152
27153         if(!this.editorcore.activated){
27154             this.editor.onFirstFocus();
27155             return;
27156         }
27157
27158         var btns = this.tb.items.map, 
27159             doc = this.editorcore.doc,
27160             frameId = this.editorcore.frameId;
27161
27162         if(!this.disable.font && !Roo.isSafari){
27163             /*
27164             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27165             if(name != this.fontSelect.dom.value){
27166                 this.fontSelect.dom.value = name;
27167             }
27168             */
27169         }
27170         if(!this.disable.format){
27171             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27172             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27173             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27174         }
27175         if(!this.disable.alignments){
27176             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27177             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27178             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27179         }
27180         if(!Roo.isSafari && !this.disable.lists){
27181             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27182             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27183         }
27184         
27185         var ans = this.editorcore.getAllAncestors();
27186         if (this.formatCombo) {
27187             
27188             
27189             var store = this.formatCombo.store;
27190             this.formatCombo.setValue("");
27191             for (var i =0; i < ans.length;i++) {
27192                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27193                     // select it..
27194                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27195                     break;
27196                 }
27197             }
27198         }
27199         
27200         
27201         
27202         // hides menus... - so this cant be on a menu...
27203         Roo.menu.MenuMgr.hideAll();
27204
27205         //this.editorsyncValue();
27206     },
27207    
27208     
27209     createFontOptions : function(){
27210         var buf = [], fs = this.fontFamilies, ff, lc;
27211         
27212         
27213         
27214         for(var i = 0, len = fs.length; i< len; i++){
27215             ff = fs[i];
27216             lc = ff.toLowerCase();
27217             buf.push(
27218                 '<option value="',lc,'" style="font-family:',ff,';"',
27219                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27220                     ff,
27221                 '</option>'
27222             );
27223         }
27224         return buf.join('');
27225     },
27226     
27227     toggleSourceEdit : function(sourceEditMode){
27228         
27229         Roo.log("toolbar toogle");
27230         if(sourceEditMode === undefined){
27231             sourceEditMode = !this.sourceEditMode;
27232         }
27233         this.sourceEditMode = sourceEditMode === true;
27234         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27235         // just toggle the button?
27236         if(btn.pressed !== this.sourceEditMode){
27237             btn.toggle(this.sourceEditMode);
27238             return;
27239         }
27240         
27241         if(sourceEditMode){
27242             Roo.log("disabling buttons");
27243             this.tb.items.each(function(item){
27244                 if(item.cmd != 'sourceedit'){
27245                     item.disable();
27246                 }
27247             });
27248           
27249         }else{
27250             Roo.log("enabling buttons");
27251             if(this.editorcore.initialized){
27252                 this.tb.items.each(function(item){
27253                     item.enable();
27254                 });
27255             }
27256             
27257         }
27258         Roo.log("calling toggole on editor");
27259         // tell the editor that it's been pressed..
27260         this.editor.toggleSourceEdit(sourceEditMode);
27261        
27262     },
27263      /**
27264      * Object collection of toolbar tooltips for the buttons in the editor. The key
27265      * is the command id associated with that button and the value is a valid QuickTips object.
27266      * For example:
27267 <pre><code>
27268 {
27269     bold : {
27270         title: 'Bold (Ctrl+B)',
27271         text: 'Make the selected text bold.',
27272         cls: 'x-html-editor-tip'
27273     },
27274     italic : {
27275         title: 'Italic (Ctrl+I)',
27276         text: 'Make the selected text italic.',
27277         cls: 'x-html-editor-tip'
27278     },
27279     ...
27280 </code></pre>
27281     * @type Object
27282      */
27283     buttonTips : {
27284         bold : {
27285             title: 'Bold (Ctrl+B)',
27286             text: 'Make the selected text bold.',
27287             cls: 'x-html-editor-tip'
27288         },
27289         italic : {
27290             title: 'Italic (Ctrl+I)',
27291             text: 'Make the selected text italic.',
27292             cls: 'x-html-editor-tip'
27293         },
27294         underline : {
27295             title: 'Underline (Ctrl+U)',
27296             text: 'Underline the selected text.',
27297             cls: 'x-html-editor-tip'
27298         },
27299         increasefontsize : {
27300             title: 'Grow Text',
27301             text: 'Increase the font size.',
27302             cls: 'x-html-editor-tip'
27303         },
27304         decreasefontsize : {
27305             title: 'Shrink Text',
27306             text: 'Decrease the font size.',
27307             cls: 'x-html-editor-tip'
27308         },
27309         backcolor : {
27310             title: 'Text Highlight Color',
27311             text: 'Change the background color of the selected text.',
27312             cls: 'x-html-editor-tip'
27313         },
27314         forecolor : {
27315             title: 'Font Color',
27316             text: 'Change the color of the selected text.',
27317             cls: 'x-html-editor-tip'
27318         },
27319         justifyleft : {
27320             title: 'Align Text Left',
27321             text: 'Align text to the left.',
27322             cls: 'x-html-editor-tip'
27323         },
27324         justifycenter : {
27325             title: 'Center Text',
27326             text: 'Center text in the editor.',
27327             cls: 'x-html-editor-tip'
27328         },
27329         justifyright : {
27330             title: 'Align Text Right',
27331             text: 'Align text to the right.',
27332             cls: 'x-html-editor-tip'
27333         },
27334         insertunorderedlist : {
27335             title: 'Bullet List',
27336             text: 'Start a bulleted list.',
27337             cls: 'x-html-editor-tip'
27338         },
27339         insertorderedlist : {
27340             title: 'Numbered List',
27341             text: 'Start a numbered list.',
27342             cls: 'x-html-editor-tip'
27343         },
27344         createlink : {
27345             title: 'Hyperlink',
27346             text: 'Make the selected text a hyperlink.',
27347             cls: 'x-html-editor-tip'
27348         },
27349         sourceedit : {
27350             title: 'Source Edit',
27351             text: 'Switch to source editing mode.',
27352             cls: 'x-html-editor-tip'
27353         }
27354     },
27355     // private
27356     onDestroy : function(){
27357         if(this.rendered){
27358             
27359             this.tb.items.each(function(item){
27360                 if(item.menu){
27361                     item.menu.removeAll();
27362                     if(item.menu.el){
27363                         item.menu.el.destroy();
27364                     }
27365                 }
27366                 item.destroy();
27367             });
27368              
27369         }
27370     },
27371     onFirstFocus: function() {
27372         this.tb.items.each(function(item){
27373            item.enable();
27374         });
27375     }
27376 });
27377
27378
27379
27380
27381 // <script type="text/javascript">
27382 /*
27383  * Based on
27384  * Ext JS Library 1.1.1
27385  * Copyright(c) 2006-2007, Ext JS, LLC.
27386  *  
27387  
27388  */
27389
27390  
27391 /**
27392  * @class Roo.form.HtmlEditor.ToolbarContext
27393  * Context Toolbar
27394  * 
27395  * Usage:
27396  *
27397  new Roo.form.HtmlEditor({
27398     ....
27399     toolbars : [
27400         { xtype: 'ToolbarStandard', styles : {} }
27401         { xtype: 'ToolbarContext', disable : {} }
27402     ]
27403 })
27404
27405      
27406  * 
27407  * @config : {Object} disable List of elements to disable.. (not done yet.)
27408  * @config : {Object} styles  Map of styles available.
27409  * 
27410  */
27411
27412 Roo.form.HtmlEditor.ToolbarContext = function(config)
27413 {
27414     
27415     Roo.apply(this, config);
27416     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27417     // dont call parent... till later.
27418     this.styles = this.styles || {};
27419 }
27420
27421  
27422
27423 Roo.form.HtmlEditor.ToolbarContext.types = {
27424     'IMG' : {
27425         width : {
27426             title: "Width",
27427             width: 40
27428         },
27429         height:  {
27430             title: "Height",
27431             width: 40
27432         },
27433         align: {
27434             title: "Align",
27435             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27436             width : 80
27437             
27438         },
27439         border: {
27440             title: "Border",
27441             width: 40
27442         },
27443         alt: {
27444             title: "Alt",
27445             width: 120
27446         },
27447         src : {
27448             title: "Src",
27449             width: 220
27450         }
27451         
27452     },
27453     'A' : {
27454         name : {
27455             title: "Name",
27456             width: 50
27457         },
27458         target:  {
27459             title: "Target",
27460             width: 120
27461         },
27462         href:  {
27463             title: "Href",
27464             width: 220
27465         } // border?
27466         
27467     },
27468     'TABLE' : {
27469         rows : {
27470             title: "Rows",
27471             width: 20
27472         },
27473         cols : {
27474             title: "Cols",
27475             width: 20
27476         },
27477         width : {
27478             title: "Width",
27479             width: 40
27480         },
27481         height : {
27482             title: "Height",
27483             width: 40
27484         },
27485         border : {
27486             title: "Border",
27487             width: 20
27488         }
27489     },
27490     'TD' : {
27491         width : {
27492             title: "Width",
27493             width: 40
27494         },
27495         height : {
27496             title: "Height",
27497             width: 40
27498         },   
27499         align: {
27500             title: "Align",
27501             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27502             width: 80
27503         },
27504         valign: {
27505             title: "Valign",
27506             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27507             width: 80
27508         },
27509         colspan: {
27510             title: "Colspan",
27511             width: 20
27512             
27513         },
27514          'font-family'  : {
27515             title : "Font",
27516             style : 'fontFamily',
27517             displayField: 'display',
27518             optname : 'font-family',
27519             width: 140
27520         }
27521     },
27522     'INPUT' : {
27523         name : {
27524             title: "name",
27525             width: 120
27526         },
27527         value : {
27528             title: "Value",
27529             width: 120
27530         },
27531         width : {
27532             title: "Width",
27533             width: 40
27534         }
27535     },
27536     'LABEL' : {
27537         'for' : {
27538             title: "For",
27539             width: 120
27540         }
27541     },
27542     'TEXTAREA' : {
27543           name : {
27544             title: "name",
27545             width: 120
27546         },
27547         rows : {
27548             title: "Rows",
27549             width: 20
27550         },
27551         cols : {
27552             title: "Cols",
27553             width: 20
27554         }
27555     },
27556     'SELECT' : {
27557         name : {
27558             title: "name",
27559             width: 120
27560         },
27561         selectoptions : {
27562             title: "Options",
27563             width: 200
27564         }
27565     },
27566     
27567     // should we really allow this??
27568     // should this just be 
27569     'BODY' : {
27570         title : {
27571             title: "Title",
27572             width: 200,
27573             disabled : true
27574         }
27575     },
27576     'SPAN' : {
27577         'font-family'  : {
27578             title : "Font",
27579             style : 'fontFamily',
27580             displayField: 'display',
27581             optname : 'font-family',
27582             width: 140
27583         }
27584     },
27585     'DIV' : {
27586         'font-family'  : {
27587             title : "Font",
27588             style : 'fontFamily',
27589             displayField: 'display',
27590             optname : 'font-family',
27591             width: 140
27592         }
27593     },
27594      'P' : {
27595         'font-family'  : {
27596             title : "Font",
27597             style : 'fontFamily',
27598             displayField: 'display',
27599             optname : 'font-family',
27600             width: 140
27601         }
27602     },
27603     
27604     '*' : {
27605         // empty..
27606     }
27607
27608 };
27609
27610 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27611 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27612
27613 Roo.form.HtmlEditor.ToolbarContext.options = {
27614         'font-family'  : [ 
27615                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27616                 [ 'Courier New', 'Courier New'],
27617                 [ 'Tahoma', 'Tahoma'],
27618                 [ 'Times New Roman,serif', 'Times'],
27619                 [ 'Verdana','Verdana' ]
27620         ]
27621 };
27622
27623 // fixme - these need to be configurable..
27624  
27625
27626 Roo.form.HtmlEditor.ToolbarContext.types
27627
27628
27629 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27630     
27631     tb: false,
27632     
27633     rendered: false,
27634     
27635     editor : false,
27636     editorcore : false,
27637     /**
27638      * @cfg {Object} disable  List of toolbar elements to disable
27639          
27640      */
27641     disable : false,
27642     /**
27643      * @cfg {Object} styles List of styles 
27644      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27645      *
27646      * These must be defined in the page, so they get rendered correctly..
27647      * .headline { }
27648      * TD.underline { }
27649      * 
27650      */
27651     styles : false,
27652     
27653     options: false,
27654     
27655     toolbars : false,
27656     
27657     init : function(editor)
27658     {
27659         this.editor = editor;
27660         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27661         var editorcore = this.editorcore;
27662         
27663         var fid = editorcore.frameId;
27664         var etb = this;
27665         function btn(id, toggle, handler){
27666             var xid = fid + '-'+ id ;
27667             return {
27668                 id : xid,
27669                 cmd : id,
27670                 cls : 'x-btn-icon x-edit-'+id,
27671                 enableToggle:toggle !== false,
27672                 scope: editorcore, // was editor...
27673                 handler:handler||editorcore.relayBtnCmd,
27674                 clickEvent:'mousedown',
27675                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27676                 tabIndex:-1
27677             };
27678         }
27679         // create a new element.
27680         var wdiv = editor.wrap.createChild({
27681                 tag: 'div'
27682             }, editor.wrap.dom.firstChild.nextSibling, true);
27683         
27684         // can we do this more than once??
27685         
27686          // stop form submits
27687       
27688  
27689         // disable everything...
27690         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27691         this.toolbars = {};
27692            
27693         for (var i in  ty) {
27694           
27695             this.toolbars[i] = this.buildToolbar(ty[i],i);
27696         }
27697         this.tb = this.toolbars.BODY;
27698         this.tb.el.show();
27699         this.buildFooter();
27700         this.footer.show();
27701         editor.on('hide', function( ) { this.footer.hide() }, this);
27702         editor.on('show', function( ) { this.footer.show() }, this);
27703         
27704          
27705         this.rendered = true;
27706         
27707         // the all the btns;
27708         editor.on('editorevent', this.updateToolbar, this);
27709         // other toolbars need to implement this..
27710         //editor.on('editmodechange', this.updateToolbar, this);
27711     },
27712     
27713     
27714     
27715     /**
27716      * Protected method that will not generally be called directly. It triggers
27717      * a toolbar update by reading the markup state of the current selection in the editor.
27718      */
27719     updateToolbar: function(editor,ev,sel){
27720
27721         //Roo.log(ev);
27722         // capture mouse up - this is handy for selecting images..
27723         // perhaps should go somewhere else...
27724         if(!this.editorcore.activated){
27725              this.editor.onFirstFocus();
27726             return;
27727         }
27728         
27729         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27730         // selectNode - might want to handle IE?
27731         if (ev &&
27732             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27733             ev.target && ev.target.tagName == 'IMG') {
27734             // they have click on an image...
27735             // let's see if we can change the selection...
27736             sel = ev.target;
27737          
27738               var nodeRange = sel.ownerDocument.createRange();
27739             try {
27740                 nodeRange.selectNode(sel);
27741             } catch (e) {
27742                 nodeRange.selectNodeContents(sel);
27743             }
27744             //nodeRange.collapse(true);
27745             var s = this.editorcore.win.getSelection();
27746             s.removeAllRanges();
27747             s.addRange(nodeRange);
27748         }  
27749         
27750       
27751         var updateFooter = sel ? false : true;
27752         
27753         
27754         var ans = this.editorcore.getAllAncestors();
27755         
27756         // pick
27757         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27758         
27759         if (!sel) { 
27760             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
27761             sel = sel ? sel : this.editorcore.doc.body;
27762             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
27763             
27764         }
27765         // pick a menu that exists..
27766         var tn = sel.tagName.toUpperCase();
27767         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27768         
27769         tn = sel.tagName.toUpperCase();
27770         
27771         var lastSel = this.tb.selectedNode
27772         
27773         this.tb.selectedNode = sel;
27774         
27775         // if current menu does not match..
27776         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27777                 
27778             this.tb.el.hide();
27779             ///console.log("show: " + tn);
27780             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27781             this.tb.el.show();
27782             // update name
27783             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27784             
27785             
27786             // update attributes
27787             if (this.tb.fields) {
27788                 this.tb.fields.each(function(e) {
27789                     if (e.stylename) {
27790                         e.setValue(sel.style[e.stylename]);
27791                         return;
27792                     } 
27793                    e.setValue(sel.getAttribute(e.attrname));
27794                 });
27795             }
27796             
27797             var hasStyles = false;
27798             for(var i in this.styles) {
27799                 hasStyles = true;
27800                 break;
27801             }
27802             
27803             // update styles
27804             if (hasStyles) { 
27805                 var st = this.tb.fields.item(0);
27806                 
27807                 st.store.removeAll();
27808                
27809                 
27810                 var cn = sel.className.split(/\s+/);
27811                 
27812                 var avs = [];
27813                 if (this.styles['*']) {
27814                     
27815                     Roo.each(this.styles['*'], function(v) {
27816                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27817                     });
27818                 }
27819                 if (this.styles[tn]) { 
27820                     Roo.each(this.styles[tn], function(v) {
27821                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27822                     });
27823                 }
27824                 
27825                 st.store.loadData(avs);
27826                 st.collapse();
27827                 st.setValue(cn);
27828             }
27829             // flag our selected Node.
27830             this.tb.selectedNode = sel;
27831            
27832            
27833             Roo.menu.MenuMgr.hideAll();
27834
27835         }
27836         
27837         if (!updateFooter) {
27838             //this.footDisp.dom.innerHTML = ''; 
27839             return;
27840         }
27841         // update the footer
27842         //
27843         var html = '';
27844         
27845         this.footerEls = ans.reverse();
27846         Roo.each(this.footerEls, function(a,i) {
27847             if (!a) { return; }
27848             html += html.length ? ' &gt; '  :  '';
27849             
27850             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27851             
27852         });
27853        
27854         // 
27855         var sz = this.footDisp.up('td').getSize();
27856         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27857         this.footDisp.dom.style.marginLeft = '5px';
27858         
27859         this.footDisp.dom.style.overflow = 'hidden';
27860         
27861         this.footDisp.dom.innerHTML = html;
27862             
27863         //this.editorsyncValue();
27864     },
27865      
27866     
27867    
27868        
27869     // private
27870     onDestroy : function(){
27871         if(this.rendered){
27872             
27873             this.tb.items.each(function(item){
27874                 if(item.menu){
27875                     item.menu.removeAll();
27876                     if(item.menu.el){
27877                         item.menu.el.destroy();
27878                     }
27879                 }
27880                 item.destroy();
27881             });
27882              
27883         }
27884     },
27885     onFirstFocus: function() {
27886         // need to do this for all the toolbars..
27887         this.tb.items.each(function(item){
27888            item.enable();
27889         });
27890     },
27891     buildToolbar: function(tlist, nm)
27892     {
27893         var editor = this.editor;
27894         var editorcore = this.editorcore;
27895          // create a new element.
27896         var wdiv = editor.wrap.createChild({
27897                 tag: 'div'
27898             }, editor.wrap.dom.firstChild.nextSibling, true);
27899         
27900        
27901         var tb = new Roo.Toolbar(wdiv);
27902         // add the name..
27903         
27904         tb.add(nm+ ":&nbsp;");
27905         
27906         var styles = [];
27907         for(var i in this.styles) {
27908             styles.push(i);
27909         }
27910         
27911         // styles...
27912         if (styles && styles.length) {
27913             
27914             // this needs a multi-select checkbox...
27915             tb.addField( new Roo.form.ComboBox({
27916                 store: new Roo.data.SimpleStore({
27917                     id : 'val',
27918                     fields: ['val', 'selected'],
27919                     data : [] 
27920                 }),
27921                 name : '-roo-edit-className',
27922                 attrname : 'className',
27923                 displayField: 'val',
27924                 typeAhead: false,
27925                 mode: 'local',
27926                 editable : false,
27927                 triggerAction: 'all',
27928                 emptyText:'Select Style',
27929                 selectOnFocus:true,
27930                 width: 130,
27931                 listeners : {
27932                     'select': function(c, r, i) {
27933                         // initial support only for on class per el..
27934                         tb.selectedNode.className =  r ? r.get('val') : '';
27935                         editorcore.syncValue();
27936                     }
27937                 }
27938     
27939             }));
27940         }
27941         
27942         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27943         var tbops = tbc.options;
27944         
27945         for (var i in tlist) {
27946             
27947             var item = tlist[i];
27948             tb.add(item.title + ":&nbsp;");
27949             
27950             
27951             //optname == used so you can configure the options available..
27952             var opts = item.opts ? item.opts : false;
27953             if (item.optname) {
27954                 opts = tbops[item.optname];
27955            
27956             }
27957             
27958             if (opts) {
27959                 // opts == pulldown..
27960                 tb.addField( new Roo.form.ComboBox({
27961                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27962                         id : 'val',
27963                         fields: ['val', 'display'],
27964                         data : opts  
27965                     }),
27966                     name : '-roo-edit-' + i,
27967                     attrname : i,
27968                     stylename : item.style ? item.style : false,
27969                     displayField: item.displayField ? item.displayField : 'val',
27970                     valueField :  'val',
27971                     typeAhead: false,
27972                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27973                     editable : false,
27974                     triggerAction: 'all',
27975                     emptyText:'Select',
27976                     selectOnFocus:true,
27977                     width: item.width ? item.width  : 130,
27978                     listeners : {
27979                         'select': function(c, r, i) {
27980                             if (c.stylename) {
27981                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27982                                 return;
27983                             }
27984                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27985                         }
27986                     }
27987
27988                 }));
27989                 continue;
27990                     
27991                  
27992                 
27993                 tb.addField( new Roo.form.TextField({
27994                     name: i,
27995                     width: 100,
27996                     //allowBlank:false,
27997                     value: ''
27998                 }));
27999                 continue;
28000             }
28001             tb.addField( new Roo.form.TextField({
28002                 name: '-roo-edit-' + i,
28003                 attrname : i,
28004                 
28005                 width: item.width,
28006                 //allowBlank:true,
28007                 value: '',
28008                 listeners: {
28009                     'change' : function(f, nv, ov) {
28010                         tb.selectedNode.setAttribute(f.attrname, nv);
28011                     }
28012                 }
28013             }));
28014              
28015         }
28016         tb.addFill();
28017         var _this = this;
28018         tb.addButton( {
28019             text: 'Remove Tag',
28020     
28021             listeners : {
28022                 click : function ()
28023                 {
28024                     // remove
28025                     // undo does not work.
28026                      
28027                     var sn = tb.selectedNode;
28028                     
28029                     var pn = sn.parentNode;
28030                     
28031                     var stn =  sn.childNodes[0];
28032                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28033                     while (sn.childNodes.length) {
28034                         var node = sn.childNodes[0];
28035                         sn.removeChild(node);
28036                         //Roo.log(node);
28037                         pn.insertBefore(node, sn);
28038                         
28039                     }
28040                     pn.removeChild(sn);
28041                     var range = editorcore.createRange();
28042         
28043                     range.setStart(stn,0);
28044                     range.setEnd(en,0); //????
28045                     //range.selectNode(sel);
28046                     
28047                     
28048                     var selection = editorcore.getSelection();
28049                     selection.removeAllRanges();
28050                     selection.addRange(range);
28051                     
28052                     
28053                     
28054                     //_this.updateToolbar(null, null, pn);
28055                     _this.updateToolbar(null, null, null);
28056                     _this.footDisp.dom.innerHTML = ''; 
28057                 }
28058             }
28059             
28060                     
28061                 
28062             
28063         });
28064         
28065         
28066         tb.el.on('click', function(e){
28067             e.preventDefault(); // what does this do?
28068         });
28069         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28070         tb.el.hide();
28071         tb.name = nm;
28072         // dont need to disable them... as they will get hidden
28073         return tb;
28074          
28075         
28076     },
28077     buildFooter : function()
28078     {
28079         
28080         var fel = this.editor.wrap.createChild();
28081         this.footer = new Roo.Toolbar(fel);
28082         // toolbar has scrolly on left / right?
28083         var footDisp= new Roo.Toolbar.Fill();
28084         var _t = this;
28085         this.footer.add(
28086             {
28087                 text : '&lt;',
28088                 xtype: 'Button',
28089                 handler : function() {
28090                     _t.footDisp.scrollTo('left',0,true)
28091                 }
28092             }
28093         );
28094         this.footer.add( footDisp );
28095         this.footer.add( 
28096             {
28097                 text : '&gt;',
28098                 xtype: 'Button',
28099                 handler : function() {
28100                     // no animation..
28101                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28102                 }
28103             }
28104         );
28105         var fel = Roo.get(footDisp.el);
28106         fel.addClass('x-editor-context');
28107         this.footDispWrap = fel; 
28108         this.footDispWrap.overflow  = 'hidden';
28109         
28110         this.footDisp = fel.createChild();
28111         this.footDispWrap.on('click', this.onContextClick, this)
28112         
28113         
28114     },
28115     onContextClick : function (ev,dom)
28116     {
28117         ev.preventDefault();
28118         var  cn = dom.className;
28119         //Roo.log(cn);
28120         if (!cn.match(/x-ed-loc-/)) {
28121             return;
28122         }
28123         var n = cn.split('-').pop();
28124         var ans = this.footerEls;
28125         var sel = ans[n];
28126         
28127          // pick
28128         var range = this.editorcore.createRange();
28129         
28130         range.selectNodeContents(sel);
28131         //range.selectNode(sel);
28132         
28133         
28134         var selection = this.editorcore.getSelection();
28135         selection.removeAllRanges();
28136         selection.addRange(range);
28137         
28138         
28139         
28140         this.updateToolbar(null, null, sel);
28141         
28142         
28143     }
28144     
28145     
28146     
28147     
28148     
28149 });
28150
28151
28152
28153
28154
28155 /*
28156  * Based on:
28157  * Ext JS Library 1.1.1
28158  * Copyright(c) 2006-2007, Ext JS, LLC.
28159  *
28160  * Originally Released Under LGPL - original licence link has changed is not relivant.
28161  *
28162  * Fork - LGPL
28163  * <script type="text/javascript">
28164  */
28165  
28166 /**
28167  * @class Roo.form.BasicForm
28168  * @extends Roo.util.Observable
28169  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28170  * @constructor
28171  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28172  * @param {Object} config Configuration options
28173  */
28174 Roo.form.BasicForm = function(el, config){
28175     this.allItems = [];
28176     this.childForms = [];
28177     Roo.apply(this, config);
28178     /*
28179      * The Roo.form.Field items in this form.
28180      * @type MixedCollection
28181      */
28182      
28183      
28184     this.items = new Roo.util.MixedCollection(false, function(o){
28185         return o.id || (o.id = Roo.id());
28186     });
28187     this.addEvents({
28188         /**
28189          * @event beforeaction
28190          * Fires before any action is performed. Return false to cancel the action.
28191          * @param {Form} this
28192          * @param {Action} action The action to be performed
28193          */
28194         beforeaction: true,
28195         /**
28196          * @event actionfailed
28197          * Fires when an action fails.
28198          * @param {Form} this
28199          * @param {Action} action The action that failed
28200          */
28201         actionfailed : true,
28202         /**
28203          * @event actioncomplete
28204          * Fires when an action is completed.
28205          * @param {Form} this
28206          * @param {Action} action The action that completed
28207          */
28208         actioncomplete : true
28209     });
28210     if(el){
28211         this.initEl(el);
28212     }
28213     Roo.form.BasicForm.superclass.constructor.call(this);
28214 };
28215
28216 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28217     /**
28218      * @cfg {String} method
28219      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28220      */
28221     /**
28222      * @cfg {DataReader} reader
28223      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28224      * This is optional as there is built-in support for processing JSON.
28225      */
28226     /**
28227      * @cfg {DataReader} errorReader
28228      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28229      * This is completely optional as there is built-in support for processing JSON.
28230      */
28231     /**
28232      * @cfg {String} url
28233      * The URL to use for form actions if one isn't supplied in the action options.
28234      */
28235     /**
28236      * @cfg {Boolean} fileUpload
28237      * Set to true if this form is a file upload.
28238      */
28239      
28240     /**
28241      * @cfg {Object} baseParams
28242      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28243      */
28244      /**
28245      
28246     /**
28247      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28248      */
28249     timeout: 30,
28250
28251     // private
28252     activeAction : null,
28253
28254     /**
28255      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28256      * or setValues() data instead of when the form was first created.
28257      */
28258     trackResetOnLoad : false,
28259     
28260     
28261     /**
28262      * childForms - used for multi-tab forms
28263      * @type {Array}
28264      */
28265     childForms : false,
28266     
28267     /**
28268      * allItems - full list of fields.
28269      * @type {Array}
28270      */
28271     allItems : false,
28272     
28273     /**
28274      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28275      * element by passing it or its id or mask the form itself by passing in true.
28276      * @type Mixed
28277      */
28278     waitMsgTarget : false,
28279
28280     // private
28281     initEl : function(el){
28282         this.el = Roo.get(el);
28283         this.id = this.el.id || Roo.id();
28284         this.el.on('submit', this.onSubmit, this);
28285         this.el.addClass('x-form');
28286     },
28287
28288     // private
28289     onSubmit : function(e){
28290         e.stopEvent();
28291     },
28292
28293     /**
28294      * Returns true if client-side validation on the form is successful.
28295      * @return Boolean
28296      */
28297     isValid : function(){
28298         var valid = true;
28299         this.items.each(function(f){
28300            if(!f.validate()){
28301                valid = false;
28302            }
28303         });
28304         return valid;
28305     },
28306
28307     /**
28308      * Returns true if any fields in this form have changed since their original load.
28309      * @return Boolean
28310      */
28311     isDirty : function(){
28312         var dirty = false;
28313         this.items.each(function(f){
28314            if(f.isDirty()){
28315                dirty = true;
28316                return false;
28317            }
28318         });
28319         return dirty;
28320     },
28321
28322     /**
28323      * Performs a predefined action (submit or load) or custom actions you define on this form.
28324      * @param {String} actionName The name of the action type
28325      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28326      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28327      * accept other config options):
28328      * <pre>
28329 Property          Type             Description
28330 ----------------  ---------------  ----------------------------------------------------------------------------------
28331 url               String           The url for the action (defaults to the form's url)
28332 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28333 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28334 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28335                                    validate the form on the client (defaults to false)
28336      * </pre>
28337      * @return {BasicForm} this
28338      */
28339     doAction : function(action, options){
28340         if(typeof action == 'string'){
28341             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28342         }
28343         if(this.fireEvent('beforeaction', this, action) !== false){
28344             this.beforeAction(action);
28345             action.run.defer(100, action);
28346         }
28347         return this;
28348     },
28349
28350     /**
28351      * Shortcut to do a submit action.
28352      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28353      * @return {BasicForm} this
28354      */
28355     submit : function(options){
28356         this.doAction('submit', options);
28357         return this;
28358     },
28359
28360     /**
28361      * Shortcut to do a load action.
28362      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28363      * @return {BasicForm} this
28364      */
28365     load : function(options){
28366         this.doAction('load', options);
28367         return this;
28368     },
28369
28370     /**
28371      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28372      * @param {Record} record The record to edit
28373      * @return {BasicForm} this
28374      */
28375     updateRecord : function(record){
28376         record.beginEdit();
28377         var fs = record.fields;
28378         fs.each(function(f){
28379             var field = this.findField(f.name);
28380             if(field){
28381                 record.set(f.name, field.getValue());
28382             }
28383         }, this);
28384         record.endEdit();
28385         return this;
28386     },
28387
28388     /**
28389      * Loads an Roo.data.Record into this form.
28390      * @param {Record} record The record to load
28391      * @return {BasicForm} this
28392      */
28393     loadRecord : function(record){
28394         this.setValues(record.data);
28395         return this;
28396     },
28397
28398     // private
28399     beforeAction : function(action){
28400         var o = action.options;
28401         
28402        
28403         if(this.waitMsgTarget === true){
28404             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28405         }else if(this.waitMsgTarget){
28406             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28407             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28408         }else {
28409             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28410         }
28411          
28412     },
28413
28414     // private
28415     afterAction : function(action, success){
28416         this.activeAction = null;
28417         var o = action.options;
28418         
28419         if(this.waitMsgTarget === true){
28420             this.el.unmask();
28421         }else if(this.waitMsgTarget){
28422             this.waitMsgTarget.unmask();
28423         }else{
28424             Roo.MessageBox.updateProgress(1);
28425             Roo.MessageBox.hide();
28426         }
28427          
28428         if(success){
28429             if(o.reset){
28430                 this.reset();
28431             }
28432             Roo.callback(o.success, o.scope, [this, action]);
28433             this.fireEvent('actioncomplete', this, action);
28434             
28435         }else{
28436             
28437             // failure condition..
28438             // we have a scenario where updates need confirming.
28439             // eg. if a locking scenario exists..
28440             // we look for { errors : { needs_confirm : true }} in the response.
28441             if (
28442                 (typeof(action.result) != 'undefined')  &&
28443                 (typeof(action.result.errors) != 'undefined')  &&
28444                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28445            ){
28446                 var _t = this;
28447                 Roo.MessageBox.confirm(
28448                     "Change requires confirmation",
28449                     action.result.errorMsg,
28450                     function(r) {
28451                         if (r != 'yes') {
28452                             return;
28453                         }
28454                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28455                     }
28456                     
28457                 );
28458                 
28459                 
28460                 
28461                 return;
28462             }
28463             
28464             Roo.callback(o.failure, o.scope, [this, action]);
28465             // show an error message if no failed handler is set..
28466             if (!this.hasListener('actionfailed')) {
28467                 Roo.MessageBox.alert("Error",
28468                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28469                         action.result.errorMsg :
28470                         "Saving Failed, please check your entries or try again"
28471                 );
28472             }
28473             
28474             this.fireEvent('actionfailed', this, action);
28475         }
28476         
28477     },
28478
28479     /**
28480      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28481      * @param {String} id The value to search for
28482      * @return Field
28483      */
28484     findField : function(id){
28485         var field = this.items.get(id);
28486         if(!field){
28487             this.items.each(function(f){
28488                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28489                     field = f;
28490                     return false;
28491                 }
28492             });
28493         }
28494         return field || null;
28495     },
28496
28497     /**
28498      * Add a secondary form to this one, 
28499      * Used to provide tabbed forms. One form is primary, with hidden values 
28500      * which mirror the elements from the other forms.
28501      * 
28502      * @param {Roo.form.Form} form to add.
28503      * 
28504      */
28505     addForm : function(form)
28506     {
28507        
28508         if (this.childForms.indexOf(form) > -1) {
28509             // already added..
28510             return;
28511         }
28512         this.childForms.push(form);
28513         var n = '';
28514         Roo.each(form.allItems, function (fe) {
28515             
28516             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28517             if (this.findField(n)) { // already added..
28518                 return;
28519             }
28520             var add = new Roo.form.Hidden({
28521                 name : n
28522             });
28523             add.render(this.el);
28524             
28525             this.add( add );
28526         }, this);
28527         
28528     },
28529     /**
28530      * Mark fields in this form invalid in bulk.
28531      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28532      * @return {BasicForm} this
28533      */
28534     markInvalid : function(errors){
28535         if(errors instanceof Array){
28536             for(var i = 0, len = errors.length; i < len; i++){
28537                 var fieldError = errors[i];
28538                 var f = this.findField(fieldError.id);
28539                 if(f){
28540                     f.markInvalid(fieldError.msg);
28541                 }
28542             }
28543         }else{
28544             var field, id;
28545             for(id in errors){
28546                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28547                     field.markInvalid(errors[id]);
28548                 }
28549             }
28550         }
28551         Roo.each(this.childForms || [], function (f) {
28552             f.markInvalid(errors);
28553         });
28554         
28555         return this;
28556     },
28557
28558     /**
28559      * Set values for fields in this form in bulk.
28560      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28561      * @return {BasicForm} this
28562      */
28563     setValues : function(values){
28564         if(values instanceof Array){ // array of objects
28565             for(var i = 0, len = values.length; i < len; i++){
28566                 var v = values[i];
28567                 var f = this.findField(v.id);
28568                 if(f){
28569                     f.setValue(v.value);
28570                     if(this.trackResetOnLoad){
28571                         f.originalValue = f.getValue();
28572                     }
28573                 }
28574             }
28575         }else{ // object hash
28576             var field, id;
28577             for(id in values){
28578                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28579                     
28580                     if (field.setFromData && 
28581                         field.valueField && 
28582                         field.displayField &&
28583                         // combos' with local stores can 
28584                         // be queried via setValue()
28585                         // to set their value..
28586                         (field.store && !field.store.isLocal)
28587                         ) {
28588                         // it's a combo
28589                         var sd = { };
28590                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28591                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28592                         field.setFromData(sd);
28593                         
28594                     } else {
28595                         field.setValue(values[id]);
28596                     }
28597                     
28598                     
28599                     if(this.trackResetOnLoad){
28600                         field.originalValue = field.getValue();
28601                     }
28602                 }
28603             }
28604         }
28605          
28606         Roo.each(this.childForms || [], function (f) {
28607             f.setValues(values);
28608         });
28609                 
28610         return this;
28611     },
28612
28613     /**
28614      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28615      * they are returned as an array.
28616      * @param {Boolean} asString
28617      * @return {Object}
28618      */
28619     getValues : function(asString){
28620         if (this.childForms) {
28621             // copy values from the child forms
28622             Roo.each(this.childForms, function (f) {
28623                 this.setValues(f.getValues());
28624             }, this);
28625         }
28626         
28627         
28628         
28629         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28630         if(asString === true){
28631             return fs;
28632         }
28633         return Roo.urlDecode(fs);
28634     },
28635     
28636     /**
28637      * Returns the fields in this form as an object with key/value pairs. 
28638      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28639      * @return {Object}
28640      */
28641     getFieldValues : function(with_hidden)
28642     {
28643         if (this.childForms) {
28644             // copy values from the child forms
28645             // should this call getFieldValues - probably not as we do not currently copy
28646             // hidden fields when we generate..
28647             Roo.each(this.childForms, function (f) {
28648                 this.setValues(f.getValues());
28649             }, this);
28650         }
28651         
28652         var ret = {};
28653         this.items.each(function(f){
28654             if (!f.getName()) {
28655                 return;
28656             }
28657             var v = f.getValue();
28658             if (f.inputType =='radio') {
28659                 if (typeof(ret[f.getName()]) == 'undefined') {
28660                     ret[f.getName()] = ''; // empty..
28661                 }
28662                 
28663                 if (!f.el.dom.checked) {
28664                     return;
28665                     
28666                 }
28667                 v = f.el.dom.value;
28668                 
28669             }
28670             
28671             // not sure if this supported any more..
28672             if ((typeof(v) == 'object') && f.getRawValue) {
28673                 v = f.getRawValue() ; // dates..
28674             }
28675             // combo boxes where name != hiddenName...
28676             if (f.name != f.getName()) {
28677                 ret[f.name] = f.getRawValue();
28678             }
28679             ret[f.getName()] = v;
28680         });
28681         
28682         return ret;
28683     },
28684
28685     /**
28686      * Clears all invalid messages in this form.
28687      * @return {BasicForm} this
28688      */
28689     clearInvalid : function(){
28690         this.items.each(function(f){
28691            f.clearInvalid();
28692         });
28693         
28694         Roo.each(this.childForms || [], function (f) {
28695             f.clearInvalid();
28696         });
28697         
28698         
28699         return this;
28700     },
28701
28702     /**
28703      * Resets this form.
28704      * @return {BasicForm} this
28705      */
28706     reset : function(){
28707         this.items.each(function(f){
28708             f.reset();
28709         });
28710         
28711         Roo.each(this.childForms || [], function (f) {
28712             f.reset();
28713         });
28714        
28715         
28716         return this;
28717     },
28718
28719     /**
28720      * Add Roo.form components to this form.
28721      * @param {Field} field1
28722      * @param {Field} field2 (optional)
28723      * @param {Field} etc (optional)
28724      * @return {BasicForm} this
28725      */
28726     add : function(){
28727         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28728         return this;
28729     },
28730
28731
28732     /**
28733      * Removes a field from the items collection (does NOT remove its markup).
28734      * @param {Field} field
28735      * @return {BasicForm} this
28736      */
28737     remove : function(field){
28738         this.items.remove(field);
28739         return this;
28740     },
28741
28742     /**
28743      * Looks at the fields in this form, checks them for an id attribute,
28744      * and calls applyTo on the existing dom element with that id.
28745      * @return {BasicForm} this
28746      */
28747     render : function(){
28748         this.items.each(function(f){
28749             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28750                 f.applyTo(f.id);
28751             }
28752         });
28753         return this;
28754     },
28755
28756     /**
28757      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28758      * @param {Object} values
28759      * @return {BasicForm} this
28760      */
28761     applyToFields : function(o){
28762         this.items.each(function(f){
28763            Roo.apply(f, o);
28764         });
28765         return this;
28766     },
28767
28768     /**
28769      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28770      * @param {Object} values
28771      * @return {BasicForm} this
28772      */
28773     applyIfToFields : function(o){
28774         this.items.each(function(f){
28775            Roo.applyIf(f, o);
28776         });
28777         return this;
28778     }
28779 });
28780
28781 // back compat
28782 Roo.BasicForm = Roo.form.BasicForm;/*
28783  * Based on:
28784  * Ext JS Library 1.1.1
28785  * Copyright(c) 2006-2007, Ext JS, LLC.
28786  *
28787  * Originally Released Under LGPL - original licence link has changed is not relivant.
28788  *
28789  * Fork - LGPL
28790  * <script type="text/javascript">
28791  */
28792
28793 /**
28794  * @class Roo.form.Form
28795  * @extends Roo.form.BasicForm
28796  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28797  * @constructor
28798  * @param {Object} config Configuration options
28799  */
28800 Roo.form.Form = function(config){
28801     var xitems =  [];
28802     if (config.items) {
28803         xitems = config.items;
28804         delete config.items;
28805     }
28806    
28807     
28808     Roo.form.Form.superclass.constructor.call(this, null, config);
28809     this.url = this.url || this.action;
28810     if(!this.root){
28811         this.root = new Roo.form.Layout(Roo.applyIf({
28812             id: Roo.id()
28813         }, config));
28814     }
28815     this.active = this.root;
28816     /**
28817      * Array of all the buttons that have been added to this form via {@link addButton}
28818      * @type Array
28819      */
28820     this.buttons = [];
28821     this.allItems = [];
28822     this.addEvents({
28823         /**
28824          * @event clientvalidation
28825          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28826          * @param {Form} this
28827          * @param {Boolean} valid true if the form has passed client-side validation
28828          */
28829         clientvalidation: true,
28830         /**
28831          * @event rendered
28832          * Fires when the form is rendered
28833          * @param {Roo.form.Form} form
28834          */
28835         rendered : true
28836     });
28837     
28838     if (this.progressUrl) {
28839             // push a hidden field onto the list of fields..
28840             this.addxtype( {
28841                     xns: Roo.form, 
28842                     xtype : 'Hidden', 
28843                     name : 'UPLOAD_IDENTIFIER' 
28844             });
28845         }
28846         
28847     
28848     Roo.each(xitems, this.addxtype, this);
28849     
28850     
28851     
28852 };
28853
28854 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28855     /**
28856      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28857      */
28858     /**
28859      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28860      */
28861     /**
28862      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28863      */
28864     buttonAlign:'center',
28865
28866     /**
28867      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28868      */
28869     minButtonWidth:75,
28870
28871     /**
28872      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28873      * This property cascades to child containers if not set.
28874      */
28875     labelAlign:'left',
28876
28877     /**
28878      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28879      * fires a looping event with that state. This is required to bind buttons to the valid
28880      * state using the config value formBind:true on the button.
28881      */
28882     monitorValid : false,
28883
28884     /**
28885      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28886      */
28887     monitorPoll : 200,
28888     
28889     /**
28890      * @cfg {String} progressUrl - Url to return progress data 
28891      */
28892     
28893     progressUrl : false,
28894   
28895     /**
28896      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28897      * fields are added and the column is closed. If no fields are passed the column remains open
28898      * until end() is called.
28899      * @param {Object} config The config to pass to the column
28900      * @param {Field} field1 (optional)
28901      * @param {Field} field2 (optional)
28902      * @param {Field} etc (optional)
28903      * @return Column The column container object
28904      */
28905     column : function(c){
28906         var col = new Roo.form.Column(c);
28907         this.start(col);
28908         if(arguments.length > 1){ // duplicate code required because of Opera
28909             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28910             this.end();
28911         }
28912         return col;
28913     },
28914
28915     /**
28916      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28917      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28918      * until end() is called.
28919      * @param {Object} config The config to pass to the fieldset
28920      * @param {Field} field1 (optional)
28921      * @param {Field} field2 (optional)
28922      * @param {Field} etc (optional)
28923      * @return FieldSet The fieldset container object
28924      */
28925     fieldset : function(c){
28926         var fs = new Roo.form.FieldSet(c);
28927         this.start(fs);
28928         if(arguments.length > 1){ // duplicate code required because of Opera
28929             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28930             this.end();
28931         }
28932         return fs;
28933     },
28934
28935     /**
28936      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28937      * fields are added and the container is closed. If no fields are passed the container remains open
28938      * until end() is called.
28939      * @param {Object} config The config to pass to the Layout
28940      * @param {Field} field1 (optional)
28941      * @param {Field} field2 (optional)
28942      * @param {Field} etc (optional)
28943      * @return Layout The container object
28944      */
28945     container : function(c){
28946         var l = new Roo.form.Layout(c);
28947         this.start(l);
28948         if(arguments.length > 1){ // duplicate code required because of Opera
28949             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28950             this.end();
28951         }
28952         return l;
28953     },
28954
28955     /**
28956      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28957      * @param {Object} container A Roo.form.Layout or subclass of Layout
28958      * @return {Form} this
28959      */
28960     start : function(c){
28961         // cascade label info
28962         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28963         this.active.stack.push(c);
28964         c.ownerCt = this.active;
28965         this.active = c;
28966         return this;
28967     },
28968
28969     /**
28970      * Closes the current open container
28971      * @return {Form} this
28972      */
28973     end : function(){
28974         if(this.active == this.root){
28975             return this;
28976         }
28977         this.active = this.active.ownerCt;
28978         return this;
28979     },
28980
28981     /**
28982      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28983      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28984      * as the label of the field.
28985      * @param {Field} field1
28986      * @param {Field} field2 (optional)
28987      * @param {Field} etc. (optional)
28988      * @return {Form} this
28989      */
28990     add : function(){
28991         this.active.stack.push.apply(this.active.stack, arguments);
28992         this.allItems.push.apply(this.allItems,arguments);
28993         var r = [];
28994         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28995             if(a[i].isFormField){
28996                 r.push(a[i]);
28997             }
28998         }
28999         if(r.length > 0){
29000             Roo.form.Form.superclass.add.apply(this, r);
29001         }
29002         return this;
29003     },
29004     
29005
29006     
29007     
29008     
29009      /**
29010      * Find any element that has been added to a form, using it's ID or name
29011      * This can include framesets, columns etc. along with regular fields..
29012      * @param {String} id - id or name to find.
29013      
29014      * @return {Element} e - or false if nothing found.
29015      */
29016     findbyId : function(id)
29017     {
29018         var ret = false;
29019         if (!id) {
29020             return ret;
29021         }
29022         Roo.each(this.allItems, function(f){
29023             if (f.id == id || f.name == id ){
29024                 ret = f;
29025                 return false;
29026             }
29027         });
29028         return ret;
29029     },
29030
29031     
29032     
29033     /**
29034      * Render this form into the passed container. This should only be called once!
29035      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29036      * @return {Form} this
29037      */
29038     render : function(ct)
29039     {
29040         
29041         
29042         
29043         ct = Roo.get(ct);
29044         var o = this.autoCreate || {
29045             tag: 'form',
29046             method : this.method || 'POST',
29047             id : this.id || Roo.id()
29048         };
29049         this.initEl(ct.createChild(o));
29050
29051         this.root.render(this.el);
29052         
29053        
29054              
29055         this.items.each(function(f){
29056             f.render('x-form-el-'+f.id);
29057         });
29058
29059         if(this.buttons.length > 0){
29060             // tables are required to maintain order and for correct IE layout
29061             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29062                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29063                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29064             }}, null, true);
29065             var tr = tb.getElementsByTagName('tr')[0];
29066             for(var i = 0, len = this.buttons.length; i < len; i++) {
29067                 var b = this.buttons[i];
29068                 var td = document.createElement('td');
29069                 td.className = 'x-form-btn-td';
29070                 b.render(tr.appendChild(td));
29071             }
29072         }
29073         if(this.monitorValid){ // initialize after render
29074             this.startMonitoring();
29075         }
29076         this.fireEvent('rendered', this);
29077         return this;
29078     },
29079
29080     /**
29081      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29082      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29083      * object or a valid Roo.DomHelper element config
29084      * @param {Function} handler The function called when the button is clicked
29085      * @param {Object} scope (optional) The scope of the handler function
29086      * @return {Roo.Button}
29087      */
29088     addButton : function(config, handler, scope){
29089         var bc = {
29090             handler: handler,
29091             scope: scope,
29092             minWidth: this.minButtonWidth,
29093             hideParent:true
29094         };
29095         if(typeof config == "string"){
29096             bc.text = config;
29097         }else{
29098             Roo.apply(bc, config);
29099         }
29100         var btn = new Roo.Button(null, bc);
29101         this.buttons.push(btn);
29102         return btn;
29103     },
29104
29105      /**
29106      * Adds a series of form elements (using the xtype property as the factory method.
29107      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29108      * @param {Object} config 
29109      */
29110     
29111     addxtype : function()
29112     {
29113         var ar = Array.prototype.slice.call(arguments, 0);
29114         var ret = false;
29115         for(var i = 0; i < ar.length; i++) {
29116             if (!ar[i]) {
29117                 continue; // skip -- if this happends something invalid got sent, we 
29118                 // should ignore it, as basically that interface element will not show up
29119                 // and that should be pretty obvious!!
29120             }
29121             
29122             if (Roo.form[ar[i].xtype]) {
29123                 ar[i].form = this;
29124                 var fe = Roo.factory(ar[i], Roo.form);
29125                 if (!ret) {
29126                     ret = fe;
29127                 }
29128                 fe.form = this;
29129                 if (fe.store) {
29130                     fe.store.form = this;
29131                 }
29132                 if (fe.isLayout) {  
29133                          
29134                     this.start(fe);
29135                     this.allItems.push(fe);
29136                     if (fe.items && fe.addxtype) {
29137                         fe.addxtype.apply(fe, fe.items);
29138                         delete fe.items;
29139                     }
29140                      this.end();
29141                     continue;
29142                 }
29143                 
29144                 
29145                  
29146                 this.add(fe);
29147               //  console.log('adding ' + ar[i].xtype);
29148             }
29149             if (ar[i].xtype == 'Button') {  
29150                 //console.log('adding button');
29151                 //console.log(ar[i]);
29152                 this.addButton(ar[i]);
29153                 this.allItems.push(fe);
29154                 continue;
29155             }
29156             
29157             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29158                 alert('end is not supported on xtype any more, use items');
29159             //    this.end();
29160             //    //console.log('adding end');
29161             }
29162             
29163         }
29164         return ret;
29165     },
29166     
29167     /**
29168      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29169      * option "monitorValid"
29170      */
29171     startMonitoring : function(){
29172         if(!this.bound){
29173             this.bound = true;
29174             Roo.TaskMgr.start({
29175                 run : this.bindHandler,
29176                 interval : this.monitorPoll || 200,
29177                 scope: this
29178             });
29179         }
29180     },
29181
29182     /**
29183      * Stops monitoring of the valid state of this form
29184      */
29185     stopMonitoring : function(){
29186         this.bound = false;
29187     },
29188
29189     // private
29190     bindHandler : function(){
29191         if(!this.bound){
29192             return false; // stops binding
29193         }
29194         var valid = true;
29195         this.items.each(function(f){
29196             if(!f.isValid(true)){
29197                 valid = false;
29198                 return false;
29199             }
29200         });
29201         for(var i = 0, len = this.buttons.length; i < len; i++){
29202             var btn = this.buttons[i];
29203             if(btn.formBind === true && btn.disabled === valid){
29204                 btn.setDisabled(!valid);
29205             }
29206         }
29207         this.fireEvent('clientvalidation', this, valid);
29208     }
29209     
29210     
29211     
29212     
29213     
29214     
29215     
29216     
29217 });
29218
29219
29220 // back compat
29221 Roo.Form = Roo.form.Form;
29222 /*
29223  * Based on:
29224  * Ext JS Library 1.1.1
29225  * Copyright(c) 2006-2007, Ext JS, LLC.
29226  *
29227  * Originally Released Under LGPL - original licence link has changed is not relivant.
29228  *
29229  * Fork - LGPL
29230  * <script type="text/javascript">
29231  */
29232
29233 // as we use this in bootstrap.
29234 Roo.namespace('Roo.form');
29235  /**
29236  * @class Roo.form.Action
29237  * Internal Class used to handle form actions
29238  * @constructor
29239  * @param {Roo.form.BasicForm} el The form element or its id
29240  * @param {Object} config Configuration options
29241  */
29242
29243  
29244  
29245 // define the action interface
29246 Roo.form.Action = function(form, options){
29247     this.form = form;
29248     this.options = options || {};
29249 };
29250 /**
29251  * Client Validation Failed
29252  * @const 
29253  */
29254 Roo.form.Action.CLIENT_INVALID = 'client';
29255 /**
29256  * Server Validation Failed
29257  * @const 
29258  */
29259 Roo.form.Action.SERVER_INVALID = 'server';
29260  /**
29261  * Connect to Server Failed
29262  * @const 
29263  */
29264 Roo.form.Action.CONNECT_FAILURE = 'connect';
29265 /**
29266  * Reading Data from Server Failed
29267  * @const 
29268  */
29269 Roo.form.Action.LOAD_FAILURE = 'load';
29270
29271 Roo.form.Action.prototype = {
29272     type : 'default',
29273     failureType : undefined,
29274     response : undefined,
29275     result : undefined,
29276
29277     // interface method
29278     run : function(options){
29279
29280     },
29281
29282     // interface method
29283     success : function(response){
29284
29285     },
29286
29287     // interface method
29288     handleResponse : function(response){
29289
29290     },
29291
29292     // default connection failure
29293     failure : function(response){
29294         
29295         this.response = response;
29296         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29297         this.form.afterAction(this, false);
29298     },
29299
29300     processResponse : function(response){
29301         this.response = response;
29302         if(!response.responseText){
29303             return true;
29304         }
29305         this.result = this.handleResponse(response);
29306         return this.result;
29307     },
29308
29309     // utility functions used internally
29310     getUrl : function(appendParams){
29311         var url = this.options.url || this.form.url || this.form.el.dom.action;
29312         if(appendParams){
29313             var p = this.getParams();
29314             if(p){
29315                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29316             }
29317         }
29318         return url;
29319     },
29320
29321     getMethod : function(){
29322         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29323     },
29324
29325     getParams : function(){
29326         var bp = this.form.baseParams;
29327         var p = this.options.params;
29328         if(p){
29329             if(typeof p == "object"){
29330                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29331             }else if(typeof p == 'string' && bp){
29332                 p += '&' + Roo.urlEncode(bp);
29333             }
29334         }else if(bp){
29335             p = Roo.urlEncode(bp);
29336         }
29337         return p;
29338     },
29339
29340     createCallback : function(){
29341         return {
29342             success: this.success,
29343             failure: this.failure,
29344             scope: this,
29345             timeout: (this.form.timeout*1000),
29346             upload: this.form.fileUpload ? this.success : undefined
29347         };
29348     }
29349 };
29350
29351 Roo.form.Action.Submit = function(form, options){
29352     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29353 };
29354
29355 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29356     type : 'submit',
29357
29358     haveProgress : false,
29359     uploadComplete : false,
29360     
29361     // uploadProgress indicator.
29362     uploadProgress : function()
29363     {
29364         if (!this.form.progressUrl) {
29365             return;
29366         }
29367         
29368         if (!this.haveProgress) {
29369             Roo.MessageBox.progress("Uploading", "Uploading");
29370         }
29371         if (this.uploadComplete) {
29372            Roo.MessageBox.hide();
29373            return;
29374         }
29375         
29376         this.haveProgress = true;
29377    
29378         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29379         
29380         var c = new Roo.data.Connection();
29381         c.request({
29382             url : this.form.progressUrl,
29383             params: {
29384                 id : uid
29385             },
29386             method: 'GET',
29387             success : function(req){
29388                //console.log(data);
29389                 var rdata = false;
29390                 var edata;
29391                 try  {
29392                    rdata = Roo.decode(req.responseText)
29393                 } catch (e) {
29394                     Roo.log("Invalid data from server..");
29395                     Roo.log(edata);
29396                     return;
29397                 }
29398                 if (!rdata || !rdata.success) {
29399                     Roo.log(rdata);
29400                     Roo.MessageBox.alert(Roo.encode(rdata));
29401                     return;
29402                 }
29403                 var data = rdata.data;
29404                 
29405                 if (this.uploadComplete) {
29406                    Roo.MessageBox.hide();
29407                    return;
29408                 }
29409                    
29410                 if (data){
29411                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29412                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29413                     );
29414                 }
29415                 this.uploadProgress.defer(2000,this);
29416             },
29417        
29418             failure: function(data) {
29419                 Roo.log('progress url failed ');
29420                 Roo.log(data);
29421             },
29422             scope : this
29423         });
29424            
29425     },
29426     
29427     
29428     run : function()
29429     {
29430         // run get Values on the form, so it syncs any secondary forms.
29431         this.form.getValues();
29432         
29433         var o = this.options;
29434         var method = this.getMethod();
29435         var isPost = method == 'POST';
29436         if(o.clientValidation === false || this.form.isValid()){
29437             
29438             if (this.form.progressUrl) {
29439                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29440                     (new Date() * 1) + '' + Math.random());
29441                     
29442             } 
29443             
29444             
29445             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29446                 form:this.form.el.dom,
29447                 url:this.getUrl(!isPost),
29448                 method: method,
29449                 params:isPost ? this.getParams() : null,
29450                 isUpload: this.form.fileUpload
29451             }));
29452             
29453             this.uploadProgress();
29454
29455         }else if (o.clientValidation !== false){ // client validation failed
29456             this.failureType = Roo.form.Action.CLIENT_INVALID;
29457             this.form.afterAction(this, false);
29458         }
29459     },
29460
29461     success : function(response)
29462     {
29463         this.uploadComplete= true;
29464         if (this.haveProgress) {
29465             Roo.MessageBox.hide();
29466         }
29467         
29468         
29469         var result = this.processResponse(response);
29470         if(result === true || result.success){
29471             this.form.afterAction(this, true);
29472             return;
29473         }
29474         if(result.errors){
29475             this.form.markInvalid(result.errors);
29476             this.failureType = Roo.form.Action.SERVER_INVALID;
29477         }
29478         this.form.afterAction(this, false);
29479     },
29480     failure : function(response)
29481     {
29482         this.uploadComplete= true;
29483         if (this.haveProgress) {
29484             Roo.MessageBox.hide();
29485         }
29486         
29487         this.response = response;
29488         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29489         this.form.afterAction(this, false);
29490     },
29491     
29492     handleResponse : function(response){
29493         if(this.form.errorReader){
29494             var rs = this.form.errorReader.read(response);
29495             var errors = [];
29496             if(rs.records){
29497                 for(var i = 0, len = rs.records.length; i < len; i++) {
29498                     var r = rs.records[i];
29499                     errors[i] = r.data;
29500                 }
29501             }
29502             if(errors.length < 1){
29503                 errors = null;
29504             }
29505             return {
29506                 success : rs.success,
29507                 errors : errors
29508             };
29509         }
29510         var ret = false;
29511         try {
29512             ret = Roo.decode(response.responseText);
29513         } catch (e) {
29514             ret = {
29515                 success: false,
29516                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29517                 errors : []
29518             };
29519         }
29520         return ret;
29521         
29522     }
29523 });
29524
29525
29526 Roo.form.Action.Load = function(form, options){
29527     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29528     this.reader = this.form.reader;
29529 };
29530
29531 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29532     type : 'load',
29533
29534     run : function(){
29535         
29536         Roo.Ajax.request(Roo.apply(
29537                 this.createCallback(), {
29538                     method:this.getMethod(),
29539                     url:this.getUrl(false),
29540                     params:this.getParams()
29541         }));
29542     },
29543
29544     success : function(response){
29545         
29546         var result = this.processResponse(response);
29547         if(result === true || !result.success || !result.data){
29548             this.failureType = Roo.form.Action.LOAD_FAILURE;
29549             this.form.afterAction(this, false);
29550             return;
29551         }
29552         this.form.clearInvalid();
29553         this.form.setValues(result.data);
29554         this.form.afterAction(this, true);
29555     },
29556
29557     handleResponse : function(response){
29558         if(this.form.reader){
29559             var rs = this.form.reader.read(response);
29560             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29561             return {
29562                 success : rs.success,
29563                 data : data
29564             };
29565         }
29566         return Roo.decode(response.responseText);
29567     }
29568 });
29569
29570 Roo.form.Action.ACTION_TYPES = {
29571     'load' : Roo.form.Action.Load,
29572     'submit' : Roo.form.Action.Submit
29573 };/*
29574  * Based on:
29575  * Ext JS Library 1.1.1
29576  * Copyright(c) 2006-2007, Ext JS, LLC.
29577  *
29578  * Originally Released Under LGPL - original licence link has changed is not relivant.
29579  *
29580  * Fork - LGPL
29581  * <script type="text/javascript">
29582  */
29583  
29584 /**
29585  * @class Roo.form.Layout
29586  * @extends Roo.Component
29587  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29588  * @constructor
29589  * @param {Object} config Configuration options
29590  */
29591 Roo.form.Layout = function(config){
29592     var xitems = [];
29593     if (config.items) {
29594         xitems = config.items;
29595         delete config.items;
29596     }
29597     Roo.form.Layout.superclass.constructor.call(this, config);
29598     this.stack = [];
29599     Roo.each(xitems, this.addxtype, this);
29600      
29601 };
29602
29603 Roo.extend(Roo.form.Layout, Roo.Component, {
29604     /**
29605      * @cfg {String/Object} autoCreate
29606      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29607      */
29608     /**
29609      * @cfg {String/Object/Function} style
29610      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29611      * a function which returns such a specification.
29612      */
29613     /**
29614      * @cfg {String} labelAlign
29615      * Valid values are "left," "top" and "right" (defaults to "left")
29616      */
29617     /**
29618      * @cfg {Number} labelWidth
29619      * Fixed width in pixels of all field labels (defaults to undefined)
29620      */
29621     /**
29622      * @cfg {Boolean} clear
29623      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29624      */
29625     clear : true,
29626     /**
29627      * @cfg {String} labelSeparator
29628      * The separator to use after field labels (defaults to ':')
29629      */
29630     labelSeparator : ':',
29631     /**
29632      * @cfg {Boolean} hideLabels
29633      * True to suppress the display of field labels in this layout (defaults to false)
29634      */
29635     hideLabels : false,
29636
29637     // private
29638     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29639     
29640     isLayout : true,
29641     
29642     // private
29643     onRender : function(ct, position){
29644         if(this.el){ // from markup
29645             this.el = Roo.get(this.el);
29646         }else {  // generate
29647             var cfg = this.getAutoCreate();
29648             this.el = ct.createChild(cfg, position);
29649         }
29650         if(this.style){
29651             this.el.applyStyles(this.style);
29652         }
29653         if(this.labelAlign){
29654             this.el.addClass('x-form-label-'+this.labelAlign);
29655         }
29656         if(this.hideLabels){
29657             this.labelStyle = "display:none";
29658             this.elementStyle = "padding-left:0;";
29659         }else{
29660             if(typeof this.labelWidth == 'number'){
29661                 this.labelStyle = "width:"+this.labelWidth+"px;";
29662                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29663             }
29664             if(this.labelAlign == 'top'){
29665                 this.labelStyle = "width:auto;";
29666                 this.elementStyle = "padding-left:0;";
29667             }
29668         }
29669         var stack = this.stack;
29670         var slen = stack.length;
29671         if(slen > 0){
29672             if(!this.fieldTpl){
29673                 var t = new Roo.Template(
29674                     '<div class="x-form-item {5}">',
29675                         '<label for="{0}" style="{2}">{1}{4}</label>',
29676                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29677                         '</div>',
29678                     '</div><div class="x-form-clear-left"></div>'
29679                 );
29680                 t.disableFormats = true;
29681                 t.compile();
29682                 Roo.form.Layout.prototype.fieldTpl = t;
29683             }
29684             for(var i = 0; i < slen; i++) {
29685                 if(stack[i].isFormField){
29686                     this.renderField(stack[i]);
29687                 }else{
29688                     this.renderComponent(stack[i]);
29689                 }
29690             }
29691         }
29692         if(this.clear){
29693             this.el.createChild({cls:'x-form-clear'});
29694         }
29695     },
29696
29697     // private
29698     renderField : function(f){
29699         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29700                f.id, //0
29701                f.fieldLabel, //1
29702                f.labelStyle||this.labelStyle||'', //2
29703                this.elementStyle||'', //3
29704                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29705                f.itemCls||this.itemCls||''  //5
29706        ], true).getPrevSibling());
29707     },
29708
29709     // private
29710     renderComponent : function(c){
29711         c.render(c.isLayout ? this.el : this.el.createChild());    
29712     },
29713     /**
29714      * Adds a object form elements (using the xtype property as the factory method.)
29715      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29716      * @param {Object} config 
29717      */
29718     addxtype : function(o)
29719     {
29720         // create the lement.
29721         o.form = this.form;
29722         var fe = Roo.factory(o, Roo.form);
29723         this.form.allItems.push(fe);
29724         this.stack.push(fe);
29725         
29726         if (fe.isFormField) {
29727             this.form.items.add(fe);
29728         }
29729          
29730         return fe;
29731     }
29732 });
29733
29734 /**
29735  * @class Roo.form.Column
29736  * @extends Roo.form.Layout
29737  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29738  * @constructor
29739  * @param {Object} config Configuration options
29740  */
29741 Roo.form.Column = function(config){
29742     Roo.form.Column.superclass.constructor.call(this, config);
29743 };
29744
29745 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29746     /**
29747      * @cfg {Number/String} width
29748      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29749      */
29750     /**
29751      * @cfg {String/Object} autoCreate
29752      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29753      */
29754
29755     // private
29756     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29757
29758     // private
29759     onRender : function(ct, position){
29760         Roo.form.Column.superclass.onRender.call(this, ct, position);
29761         if(this.width){
29762             this.el.setWidth(this.width);
29763         }
29764     }
29765 });
29766
29767
29768 /**
29769  * @class Roo.form.Row
29770  * @extends Roo.form.Layout
29771  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29772  * @constructor
29773  * @param {Object} config Configuration options
29774  */
29775
29776  
29777 Roo.form.Row = function(config){
29778     Roo.form.Row.superclass.constructor.call(this, config);
29779 };
29780  
29781 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29782       /**
29783      * @cfg {Number/String} width
29784      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29785      */
29786     /**
29787      * @cfg {Number/String} height
29788      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29789      */
29790     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29791     
29792     padWidth : 20,
29793     // private
29794     onRender : function(ct, position){
29795         //console.log('row render');
29796         if(!this.rowTpl){
29797             var t = new Roo.Template(
29798                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29799                     '<label for="{0}" style="{2}">{1}{4}</label>',
29800                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29801                     '</div>',
29802                 '</div>'
29803             );
29804             t.disableFormats = true;
29805             t.compile();
29806             Roo.form.Layout.prototype.rowTpl = t;
29807         }
29808         this.fieldTpl = this.rowTpl;
29809         
29810         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29811         var labelWidth = 100;
29812         
29813         if ((this.labelAlign != 'top')) {
29814             if (typeof this.labelWidth == 'number') {
29815                 labelWidth = this.labelWidth
29816             }
29817             this.padWidth =  20 + labelWidth;
29818             
29819         }
29820         
29821         Roo.form.Column.superclass.onRender.call(this, ct, position);
29822         if(this.width){
29823             this.el.setWidth(this.width);
29824         }
29825         if(this.height){
29826             this.el.setHeight(this.height);
29827         }
29828     },
29829     
29830     // private
29831     renderField : function(f){
29832         f.fieldEl = this.fieldTpl.append(this.el, [
29833                f.id, f.fieldLabel,
29834                f.labelStyle||this.labelStyle||'',
29835                this.elementStyle||'',
29836                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29837                f.itemCls||this.itemCls||'',
29838                f.width ? f.width + this.padWidth : 160 + this.padWidth
29839        ],true);
29840     }
29841 });
29842  
29843
29844 /**
29845  * @class Roo.form.FieldSet
29846  * @extends Roo.form.Layout
29847  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29848  * @constructor
29849  * @param {Object} config Configuration options
29850  */
29851 Roo.form.FieldSet = function(config){
29852     Roo.form.FieldSet.superclass.constructor.call(this, config);
29853 };
29854
29855 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29856     /**
29857      * @cfg {String} legend
29858      * The text to display as the legend for the FieldSet (defaults to '')
29859      */
29860     /**
29861      * @cfg {String/Object} autoCreate
29862      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29863      */
29864
29865     // private
29866     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29867
29868     // private
29869     onRender : function(ct, position){
29870         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29871         if(this.legend){
29872             this.setLegend(this.legend);
29873         }
29874     },
29875
29876     // private
29877     setLegend : function(text){
29878         if(this.rendered){
29879             this.el.child('legend').update(text);
29880         }
29881     }
29882 });/*
29883  * Based on:
29884  * Ext JS Library 1.1.1
29885  * Copyright(c) 2006-2007, Ext JS, LLC.
29886  *
29887  * Originally Released Under LGPL - original licence link has changed is not relivant.
29888  *
29889  * Fork - LGPL
29890  * <script type="text/javascript">
29891  */
29892 /**
29893  * @class Roo.form.VTypes
29894  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29895  * @singleton
29896  */
29897 Roo.form.VTypes = function(){
29898     // closure these in so they are only created once.
29899     var alpha = /^[a-zA-Z_]+$/;
29900     var alphanum = /^[a-zA-Z0-9_]+$/;
29901     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29902     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29903
29904     // All these messages and functions are configurable
29905     return {
29906         /**
29907          * The function used to validate email addresses
29908          * @param {String} value The email address
29909          */
29910         'email' : function(v){
29911             return email.test(v);
29912         },
29913         /**
29914          * The error text to display when the email validation function returns false
29915          * @type String
29916          */
29917         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29918         /**
29919          * The keystroke filter mask to be applied on email input
29920          * @type RegExp
29921          */
29922         'emailMask' : /[a-z0-9_\.\-@]/i,
29923
29924         /**
29925          * The function used to validate URLs
29926          * @param {String} value The URL
29927          */
29928         'url' : function(v){
29929             return url.test(v);
29930         },
29931         /**
29932          * The error text to display when the url validation function returns false
29933          * @type String
29934          */
29935         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29936         
29937         /**
29938          * The function used to validate alpha values
29939          * @param {String} value The value
29940          */
29941         'alpha' : function(v){
29942             return alpha.test(v);
29943         },
29944         /**
29945          * The error text to display when the alpha validation function returns false
29946          * @type String
29947          */
29948         'alphaText' : 'This field should only contain letters and _',
29949         /**
29950          * The keystroke filter mask to be applied on alpha input
29951          * @type RegExp
29952          */
29953         'alphaMask' : /[a-z_]/i,
29954
29955         /**
29956          * The function used to validate alphanumeric values
29957          * @param {String} value The value
29958          */
29959         'alphanum' : function(v){
29960             return alphanum.test(v);
29961         },
29962         /**
29963          * The error text to display when the alphanumeric validation function returns false
29964          * @type String
29965          */
29966         'alphanumText' : 'This field should only contain letters, numbers and _',
29967         /**
29968          * The keystroke filter mask to be applied on alphanumeric input
29969          * @type RegExp
29970          */
29971         'alphanumMask' : /[a-z0-9_]/i
29972     };
29973 }();//<script type="text/javascript">
29974
29975 /**
29976  * @class Roo.form.FCKeditor
29977  * @extends Roo.form.TextArea
29978  * Wrapper around the FCKEditor http://www.fckeditor.net
29979  * @constructor
29980  * Creates a new FCKeditor
29981  * @param {Object} config Configuration options
29982  */
29983 Roo.form.FCKeditor = function(config){
29984     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29985     this.addEvents({
29986          /**
29987          * @event editorinit
29988          * Fired when the editor is initialized - you can add extra handlers here..
29989          * @param {FCKeditor} this
29990          * @param {Object} the FCK object.
29991          */
29992         editorinit : true
29993     });
29994     
29995     
29996 };
29997 Roo.form.FCKeditor.editors = { };
29998 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29999 {
30000     //defaultAutoCreate : {
30001     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30002     //},
30003     // private
30004     /**
30005      * @cfg {Object} fck options - see fck manual for details.
30006      */
30007     fckconfig : false,
30008     
30009     /**
30010      * @cfg {Object} fck toolbar set (Basic or Default)
30011      */
30012     toolbarSet : 'Basic',
30013     /**
30014      * @cfg {Object} fck BasePath
30015      */ 
30016     basePath : '/fckeditor/',
30017     
30018     
30019     frame : false,
30020     
30021     value : '',
30022     
30023    
30024     onRender : function(ct, position)
30025     {
30026         if(!this.el){
30027             this.defaultAutoCreate = {
30028                 tag: "textarea",
30029                 style:"width:300px;height:60px;",
30030                 autocomplete: "off"
30031             };
30032         }
30033         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30034         /*
30035         if(this.grow){
30036             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30037             if(this.preventScrollbars){
30038                 this.el.setStyle("overflow", "hidden");
30039             }
30040             this.el.setHeight(this.growMin);
30041         }
30042         */
30043         //console.log('onrender' + this.getId() );
30044         Roo.form.FCKeditor.editors[this.getId()] = this;
30045          
30046
30047         this.replaceTextarea() ;
30048         
30049     },
30050     
30051     getEditor : function() {
30052         return this.fckEditor;
30053     },
30054     /**
30055      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30056      * @param {Mixed} value The value to set
30057      */
30058     
30059     
30060     setValue : function(value)
30061     {
30062         //console.log('setValue: ' + value);
30063         
30064         if(typeof(value) == 'undefined') { // not sure why this is happending...
30065             return;
30066         }
30067         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30068         
30069         //if(!this.el || !this.getEditor()) {
30070         //    this.value = value;
30071             //this.setValue.defer(100,this,[value]);    
30072         //    return;
30073         //} 
30074         
30075         if(!this.getEditor()) {
30076             return;
30077         }
30078         
30079         this.getEditor().SetData(value);
30080         
30081         //
30082
30083     },
30084
30085     /**
30086      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30087      * @return {Mixed} value The field value
30088      */
30089     getValue : function()
30090     {
30091         
30092         if (this.frame && this.frame.dom.style.display == 'none') {
30093             return Roo.form.FCKeditor.superclass.getValue.call(this);
30094         }
30095         
30096         if(!this.el || !this.getEditor()) {
30097            
30098            // this.getValue.defer(100,this); 
30099             return this.value;
30100         }
30101        
30102         
30103         var value=this.getEditor().GetData();
30104         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30105         return Roo.form.FCKeditor.superclass.getValue.call(this);
30106         
30107
30108     },
30109
30110     /**
30111      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30112      * @return {Mixed} value The field value
30113      */
30114     getRawValue : function()
30115     {
30116         if (this.frame && this.frame.dom.style.display == 'none') {
30117             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30118         }
30119         
30120         if(!this.el || !this.getEditor()) {
30121             //this.getRawValue.defer(100,this); 
30122             return this.value;
30123             return;
30124         }
30125         
30126         
30127         
30128         var value=this.getEditor().GetData();
30129         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30130         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30131          
30132     },
30133     
30134     setSize : function(w,h) {
30135         
30136         
30137         
30138         //if (this.frame && this.frame.dom.style.display == 'none') {
30139         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30140         //    return;
30141         //}
30142         //if(!this.el || !this.getEditor()) {
30143         //    this.setSize.defer(100,this, [w,h]); 
30144         //    return;
30145         //}
30146         
30147         
30148         
30149         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30150         
30151         this.frame.dom.setAttribute('width', w);
30152         this.frame.dom.setAttribute('height', h);
30153         this.frame.setSize(w,h);
30154         
30155     },
30156     
30157     toggleSourceEdit : function(value) {
30158         
30159       
30160          
30161         this.el.dom.style.display = value ? '' : 'none';
30162         this.frame.dom.style.display = value ?  'none' : '';
30163         
30164     },
30165     
30166     
30167     focus: function(tag)
30168     {
30169         if (this.frame.dom.style.display == 'none') {
30170             return Roo.form.FCKeditor.superclass.focus.call(this);
30171         }
30172         if(!this.el || !this.getEditor()) {
30173             this.focus.defer(100,this, [tag]); 
30174             return;
30175         }
30176         
30177         
30178         
30179         
30180         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30181         this.getEditor().Focus();
30182         if (tgs.length) {
30183             if (!this.getEditor().Selection.GetSelection()) {
30184                 this.focus.defer(100,this, [tag]); 
30185                 return;
30186             }
30187             
30188             
30189             var r = this.getEditor().EditorDocument.createRange();
30190             r.setStart(tgs[0],0);
30191             r.setEnd(tgs[0],0);
30192             this.getEditor().Selection.GetSelection().removeAllRanges();
30193             this.getEditor().Selection.GetSelection().addRange(r);
30194             this.getEditor().Focus();
30195         }
30196         
30197     },
30198     
30199     
30200     
30201     replaceTextarea : function()
30202     {
30203         if ( document.getElementById( this.getId() + '___Frame' ) )
30204             return ;
30205         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30206         //{
30207             // We must check the elements firstly using the Id and then the name.
30208         var oTextarea = document.getElementById( this.getId() );
30209         
30210         var colElementsByName = document.getElementsByName( this.getId() ) ;
30211          
30212         oTextarea.style.display = 'none' ;
30213
30214         if ( oTextarea.tabIndex ) {            
30215             this.TabIndex = oTextarea.tabIndex ;
30216         }
30217         
30218         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30219         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30220         this.frame = Roo.get(this.getId() + '___Frame')
30221     },
30222     
30223     _getConfigHtml : function()
30224     {
30225         var sConfig = '' ;
30226
30227         for ( var o in this.fckconfig ) {
30228             sConfig += sConfig.length > 0  ? '&amp;' : '';
30229             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30230         }
30231
30232         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30233     },
30234     
30235     
30236     _getIFrameHtml : function()
30237     {
30238         var sFile = 'fckeditor.html' ;
30239         /* no idea what this is about..
30240         try
30241         {
30242             if ( (/fcksource=true/i).test( window.top.location.search ) )
30243                 sFile = 'fckeditor.original.html' ;
30244         }
30245         catch (e) { 
30246         */
30247
30248         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30249         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30250         
30251         
30252         var html = '<iframe id="' + this.getId() +
30253             '___Frame" src="' + sLink +
30254             '" width="' + this.width +
30255             '" height="' + this.height + '"' +
30256             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30257             ' frameborder="0" scrolling="no"></iframe>' ;
30258
30259         return html ;
30260     },
30261     
30262     _insertHtmlBefore : function( html, element )
30263     {
30264         if ( element.insertAdjacentHTML )       {
30265             // IE
30266             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30267         } else { // Gecko
30268             var oRange = document.createRange() ;
30269             oRange.setStartBefore( element ) ;
30270             var oFragment = oRange.createContextualFragment( html );
30271             element.parentNode.insertBefore( oFragment, element ) ;
30272         }
30273     }
30274     
30275     
30276   
30277     
30278     
30279     
30280     
30281
30282 });
30283
30284 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30285
30286 function FCKeditor_OnComplete(editorInstance){
30287     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30288     f.fckEditor = editorInstance;
30289     //console.log("loaded");
30290     f.fireEvent('editorinit', f, editorInstance);
30291
30292   
30293
30294  
30295
30296
30297
30298
30299
30300
30301
30302
30303
30304
30305
30306
30307
30308
30309
30310 //<script type="text/javascript">
30311 /**
30312  * @class Roo.form.GridField
30313  * @extends Roo.form.Field
30314  * Embed a grid (or editable grid into a form)
30315  * STATUS ALPHA
30316  * 
30317  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30318  * it needs 
30319  * xgrid.store = Roo.data.Store
30320  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30321  * xgrid.store.reader = Roo.data.JsonReader 
30322  * 
30323  * 
30324  * @constructor
30325  * Creates a new GridField
30326  * @param {Object} config Configuration options
30327  */
30328 Roo.form.GridField = function(config){
30329     Roo.form.GridField.superclass.constructor.call(this, config);
30330      
30331 };
30332
30333 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30334     /**
30335      * @cfg {Number} width  - used to restrict width of grid..
30336      */
30337     width : 100,
30338     /**
30339      * @cfg {Number} height - used to restrict height of grid..
30340      */
30341     height : 50,
30342      /**
30343      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30344          * 
30345          *}
30346      */
30347     xgrid : false, 
30348     /**
30349      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30350      * {tag: "input", type: "checkbox", autocomplete: "off"})
30351      */
30352    // defaultAutoCreate : { tag: 'div' },
30353     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30354     /**
30355      * @cfg {String} addTitle Text to include for adding a title.
30356      */
30357     addTitle : false,
30358     //
30359     onResize : function(){
30360         Roo.form.Field.superclass.onResize.apply(this, arguments);
30361     },
30362
30363     initEvents : function(){
30364         // Roo.form.Checkbox.superclass.initEvents.call(this);
30365         // has no events...
30366        
30367     },
30368
30369
30370     getResizeEl : function(){
30371         return this.wrap;
30372     },
30373
30374     getPositionEl : function(){
30375         return this.wrap;
30376     },
30377
30378     // private
30379     onRender : function(ct, position){
30380         
30381         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30382         var style = this.style;
30383         delete this.style;
30384         
30385         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30386         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30387         this.viewEl = this.wrap.createChild({ tag: 'div' });
30388         if (style) {
30389             this.viewEl.applyStyles(style);
30390         }
30391         if (this.width) {
30392             this.viewEl.setWidth(this.width);
30393         }
30394         if (this.height) {
30395             this.viewEl.setHeight(this.height);
30396         }
30397         //if(this.inputValue !== undefined){
30398         //this.setValue(this.value);
30399         
30400         
30401         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30402         
30403         
30404         this.grid.render();
30405         this.grid.getDataSource().on('remove', this.refreshValue, this);
30406         this.grid.getDataSource().on('update', this.refreshValue, this);
30407         this.grid.on('afteredit', this.refreshValue, this);
30408  
30409     },
30410      
30411     
30412     /**
30413      * Sets the value of the item. 
30414      * @param {String} either an object  or a string..
30415      */
30416     setValue : function(v){
30417         //this.value = v;
30418         v = v || []; // empty set..
30419         // this does not seem smart - it really only affects memoryproxy grids..
30420         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30421             var ds = this.grid.getDataSource();
30422             // assumes a json reader..
30423             var data = {}
30424             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30425             ds.loadData( data);
30426         }
30427         // clear selection so it does not get stale.
30428         if (this.grid.sm) { 
30429             this.grid.sm.clearSelections();
30430         }
30431         
30432         Roo.form.GridField.superclass.setValue.call(this, v);
30433         this.refreshValue();
30434         // should load data in the grid really....
30435     },
30436     
30437     // private
30438     refreshValue: function() {
30439          var val = [];
30440         this.grid.getDataSource().each(function(r) {
30441             val.push(r.data);
30442         });
30443         this.el.dom.value = Roo.encode(val);
30444     }
30445     
30446      
30447     
30448     
30449 });/*
30450  * Based on:
30451  * Ext JS Library 1.1.1
30452  * Copyright(c) 2006-2007, Ext JS, LLC.
30453  *
30454  * Originally Released Under LGPL - original licence link has changed is not relivant.
30455  *
30456  * Fork - LGPL
30457  * <script type="text/javascript">
30458  */
30459 /**
30460  * @class Roo.form.DisplayField
30461  * @extends Roo.form.Field
30462  * A generic Field to display non-editable data.
30463  * @constructor
30464  * Creates a new Display Field item.
30465  * @param {Object} config Configuration options
30466  */
30467 Roo.form.DisplayField = function(config){
30468     Roo.form.DisplayField.superclass.constructor.call(this, config);
30469     
30470 };
30471
30472 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30473     inputType:      'hidden',
30474     allowBlank:     true,
30475     readOnly:         true,
30476     
30477  
30478     /**
30479      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30480      */
30481     focusClass : undefined,
30482     /**
30483      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30484      */
30485     fieldClass: 'x-form-field',
30486     
30487      /**
30488      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30489      */
30490     valueRenderer: undefined,
30491     
30492     width: 100,
30493     /**
30494      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30495      * {tag: "input", type: "checkbox", autocomplete: "off"})
30496      */
30497      
30498  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30499
30500     onResize : function(){
30501         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30502         
30503     },
30504
30505     initEvents : function(){
30506         // Roo.form.Checkbox.superclass.initEvents.call(this);
30507         // has no events...
30508        
30509     },
30510
30511
30512     getResizeEl : function(){
30513         return this.wrap;
30514     },
30515
30516     getPositionEl : function(){
30517         return this.wrap;
30518     },
30519
30520     // private
30521     onRender : function(ct, position){
30522         
30523         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30524         //if(this.inputValue !== undefined){
30525         this.wrap = this.el.wrap();
30526         
30527         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30528         
30529         if (this.bodyStyle) {
30530             this.viewEl.applyStyles(this.bodyStyle);
30531         }
30532         //this.viewEl.setStyle('padding', '2px');
30533         
30534         this.setValue(this.value);
30535         
30536     },
30537 /*
30538     // private
30539     initValue : Roo.emptyFn,
30540
30541   */
30542
30543         // private
30544     onClick : function(){
30545         
30546     },
30547
30548     /**
30549      * Sets the checked state of the checkbox.
30550      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30551      */
30552     setValue : function(v){
30553         this.value = v;
30554         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30555         // this might be called before we have a dom element..
30556         if (!this.viewEl) {
30557             return;
30558         }
30559         this.viewEl.dom.innerHTML = html;
30560         Roo.form.DisplayField.superclass.setValue.call(this, v);
30561
30562     }
30563 });/*
30564  * 
30565  * Licence- LGPL
30566  * 
30567  */
30568
30569 /**
30570  * @class Roo.form.DayPicker
30571  * @extends Roo.form.Field
30572  * A Day picker show [M] [T] [W] ....
30573  * @constructor
30574  * Creates a new Day Picker
30575  * @param {Object} config Configuration options
30576  */
30577 Roo.form.DayPicker= function(config){
30578     Roo.form.DayPicker.superclass.constructor.call(this, config);
30579      
30580 };
30581
30582 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30583     /**
30584      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30585      */
30586     focusClass : undefined,
30587     /**
30588      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30589      */
30590     fieldClass: "x-form-field",
30591    
30592     /**
30593      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30594      * {tag: "input", type: "checkbox", autocomplete: "off"})
30595      */
30596     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30597     
30598    
30599     actionMode : 'viewEl', 
30600     //
30601     // private
30602  
30603     inputType : 'hidden',
30604     
30605      
30606     inputElement: false, // real input element?
30607     basedOn: false, // ????
30608     
30609     isFormField: true, // not sure where this is needed!!!!
30610
30611     onResize : function(){
30612         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30613         if(!this.boxLabel){
30614             this.el.alignTo(this.wrap, 'c-c');
30615         }
30616     },
30617
30618     initEvents : function(){
30619         Roo.form.Checkbox.superclass.initEvents.call(this);
30620         this.el.on("click", this.onClick,  this);
30621         this.el.on("change", this.onClick,  this);
30622     },
30623
30624
30625     getResizeEl : function(){
30626         return this.wrap;
30627     },
30628
30629     getPositionEl : function(){
30630         return this.wrap;
30631     },
30632
30633     
30634     // private
30635     onRender : function(ct, position){
30636         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30637        
30638         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30639         
30640         var r1 = '<table><tr>';
30641         var r2 = '<tr class="x-form-daypick-icons">';
30642         for (var i=0; i < 7; i++) {
30643             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30644             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30645         }
30646         
30647         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30648         viewEl.select('img').on('click', this.onClick, this);
30649         this.viewEl = viewEl;   
30650         
30651         
30652         // this will not work on Chrome!!!
30653         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30654         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30655         
30656         
30657           
30658
30659     },
30660
30661     // private
30662     initValue : Roo.emptyFn,
30663
30664     /**
30665      * Returns the checked state of the checkbox.
30666      * @return {Boolean} True if checked, else false
30667      */
30668     getValue : function(){
30669         return this.el.dom.value;
30670         
30671     },
30672
30673         // private
30674     onClick : function(e){ 
30675         //this.setChecked(!this.checked);
30676         Roo.get(e.target).toggleClass('x-menu-item-checked');
30677         this.refreshValue();
30678         //if(this.el.dom.checked != this.checked){
30679         //    this.setValue(this.el.dom.checked);
30680        // }
30681     },
30682     
30683     // private
30684     refreshValue : function()
30685     {
30686         var val = '';
30687         this.viewEl.select('img',true).each(function(e,i,n)  {
30688             val += e.is(".x-menu-item-checked") ? String(n) : '';
30689         });
30690         this.setValue(val, true);
30691     },
30692
30693     /**
30694      * Sets the checked state of the checkbox.
30695      * On is always based on a string comparison between inputValue and the param.
30696      * @param {Boolean/String} value - the value to set 
30697      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30698      */
30699     setValue : function(v,suppressEvent){
30700         if (!this.el.dom) {
30701             return;
30702         }
30703         var old = this.el.dom.value ;
30704         this.el.dom.value = v;
30705         if (suppressEvent) {
30706             return ;
30707         }
30708          
30709         // update display..
30710         this.viewEl.select('img',true).each(function(e,i,n)  {
30711             
30712             var on = e.is(".x-menu-item-checked");
30713             var newv = v.indexOf(String(n)) > -1;
30714             if (on != newv) {
30715                 e.toggleClass('x-menu-item-checked');
30716             }
30717             
30718         });
30719         
30720         
30721         this.fireEvent('change', this, v, old);
30722         
30723         
30724     },
30725    
30726     // handle setting of hidden value by some other method!!?!?
30727     setFromHidden: function()
30728     {
30729         if(!this.el){
30730             return;
30731         }
30732         //console.log("SET FROM HIDDEN");
30733         //alert('setFrom hidden');
30734         this.setValue(this.el.dom.value);
30735     },
30736     
30737     onDestroy : function()
30738     {
30739         if(this.viewEl){
30740             Roo.get(this.viewEl).remove();
30741         }
30742          
30743         Roo.form.DayPicker.superclass.onDestroy.call(this);
30744     }
30745
30746 });/*
30747  * RooJS Library 1.1.1
30748  * Copyright(c) 2008-2011  Alan Knowles
30749  *
30750  * License - LGPL
30751  */
30752  
30753
30754 /**
30755  * @class Roo.form.ComboCheck
30756  * @extends Roo.form.ComboBox
30757  * A combobox for multiple select items.
30758  *
30759  * FIXME - could do with a reset button..
30760  * 
30761  * @constructor
30762  * Create a new ComboCheck
30763  * @param {Object} config Configuration options
30764  */
30765 Roo.form.ComboCheck = function(config){
30766     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30767     // should verify some data...
30768     // like
30769     // hiddenName = required..
30770     // displayField = required
30771     // valudField == required
30772     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30773     var _t = this;
30774     Roo.each(req, function(e) {
30775         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30776             throw "Roo.form.ComboCheck : missing value for: " + e;
30777         }
30778     });
30779     
30780     
30781 };
30782
30783 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30784      
30785      
30786     editable : false,
30787      
30788     selectedClass: 'x-menu-item-checked', 
30789     
30790     // private
30791     onRender : function(ct, position){
30792         var _t = this;
30793         
30794         
30795         
30796         if(!this.tpl){
30797             var cls = 'x-combo-list';
30798
30799             
30800             this.tpl =  new Roo.Template({
30801                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30802                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30803                    '<span>{' + this.displayField + '}</span>' +
30804                     '</div>' 
30805                 
30806             });
30807         }
30808  
30809         
30810         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30811         this.view.singleSelect = false;
30812         this.view.multiSelect = true;
30813         this.view.toggleSelect = true;
30814         this.pageTb.add(new Roo.Toolbar.Fill(), {
30815             
30816             text: 'Done',
30817             handler: function()
30818             {
30819                 _t.collapse();
30820             }
30821         });
30822     },
30823     
30824     onViewOver : function(e, t){
30825         // do nothing...
30826         return;
30827         
30828     },
30829     
30830     onViewClick : function(doFocus,index){
30831         return;
30832         
30833     },
30834     select: function () {
30835         //Roo.log("SELECT CALLED");
30836     },
30837      
30838     selectByValue : function(xv, scrollIntoView){
30839         var ar = this.getValueArray();
30840         var sels = [];
30841         
30842         Roo.each(ar, function(v) {
30843             if(v === undefined || v === null){
30844                 return;
30845             }
30846             var r = this.findRecord(this.valueField, v);
30847             if(r){
30848                 sels.push(this.store.indexOf(r))
30849                 
30850             }
30851         },this);
30852         this.view.select(sels);
30853         return false;
30854     },
30855     
30856     
30857     
30858     onSelect : function(record, index){
30859        // Roo.log("onselect Called");
30860        // this is only called by the clear button now..
30861         this.view.clearSelections();
30862         this.setValue('[]');
30863         if (this.value != this.valueBefore) {
30864             this.fireEvent('change', this, this.value, this.valueBefore);
30865             this.valueBefore = this.value;
30866         }
30867     },
30868     getValueArray : function()
30869     {
30870         var ar = [] ;
30871         
30872         try {
30873             //Roo.log(this.value);
30874             if (typeof(this.value) == 'undefined') {
30875                 return [];
30876             }
30877             var ar = Roo.decode(this.value);
30878             return  ar instanceof Array ? ar : []; //?? valid?
30879             
30880         } catch(e) {
30881             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30882             return [];
30883         }
30884          
30885     },
30886     expand : function ()
30887     {
30888         
30889         Roo.form.ComboCheck.superclass.expand.call(this);
30890         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30891         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30892         
30893
30894     },
30895     
30896     collapse : function(){
30897         Roo.form.ComboCheck.superclass.collapse.call(this);
30898         var sl = this.view.getSelectedIndexes();
30899         var st = this.store;
30900         var nv = [];
30901         var tv = [];
30902         var r;
30903         Roo.each(sl, function(i) {
30904             r = st.getAt(i);
30905             nv.push(r.get(this.valueField));
30906         },this);
30907         this.setValue(Roo.encode(nv));
30908         if (this.value != this.valueBefore) {
30909
30910             this.fireEvent('change', this, this.value, this.valueBefore);
30911             this.valueBefore = this.value;
30912         }
30913         
30914     },
30915     
30916     setValue : function(v){
30917         // Roo.log(v);
30918         this.value = v;
30919         
30920         var vals = this.getValueArray();
30921         var tv = [];
30922         Roo.each(vals, function(k) {
30923             var r = this.findRecord(this.valueField, k);
30924             if(r){
30925                 tv.push(r.data[this.displayField]);
30926             }else if(this.valueNotFoundText !== undefined){
30927                 tv.push( this.valueNotFoundText );
30928             }
30929         },this);
30930        // Roo.log(tv);
30931         
30932         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30933         this.hiddenField.value = v;
30934         this.value = v;
30935     }
30936     
30937 });/*
30938  * Based on:
30939  * Ext JS Library 1.1.1
30940  * Copyright(c) 2006-2007, Ext JS, LLC.
30941  *
30942  * Originally Released Under LGPL - original licence link has changed is not relivant.
30943  *
30944  * Fork - LGPL
30945  * <script type="text/javascript">
30946  */
30947  
30948 /**
30949  * @class Roo.form.Signature
30950  * @extends Roo.form.Field
30951  * Signature field.  
30952  * @constructor
30953  * 
30954  * @param {Object} config Configuration options
30955  */
30956
30957 Roo.form.Signature = function(config){
30958     Roo.form.Signature.superclass.constructor.call(this, config);
30959     
30960     this.addEvents({// not in used??
30961          /**
30962          * @event confirm
30963          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30964              * @param {Roo.form.Signature} combo This combo box
30965              */
30966         'confirm' : true,
30967         /**
30968          * @event reset
30969          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30970              * @param {Roo.form.ComboBox} combo This combo box
30971              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30972              */
30973         'reset' : true
30974     });
30975 };
30976
30977 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30978     /**
30979      * @cfg {Object} labels Label to use when rendering a form.
30980      * defaults to 
30981      * labels : { 
30982      *      clear : "Clear",
30983      *      confirm : "Confirm"
30984      *  }
30985      */
30986     labels : { 
30987         clear : "Clear",
30988         confirm : "Confirm"
30989     },
30990     /**
30991      * @cfg {Number} width The signature panel width (defaults to 300)
30992      */
30993     width: 300,
30994     /**
30995      * @cfg {Number} height The signature panel height (defaults to 100)
30996      */
30997     height : 100,
30998     /**
30999      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31000      */
31001     allowBlank : false,
31002     
31003     //private
31004     // {Object} signPanel The signature SVG panel element (defaults to {})
31005     signPanel : {},
31006     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31007     isMouseDown : false,
31008     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31009     isConfirmed : false,
31010     // {String} signatureTmp SVG mapping string (defaults to empty string)
31011     signatureTmp : '',
31012     
31013     
31014     defaultAutoCreate : { // modified by initCompnoent..
31015         tag: "input",
31016         type:"hidden"
31017     },
31018
31019     // private
31020     onRender : function(ct, position){
31021         
31022         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31023         
31024         this.wrap = this.el.wrap({
31025             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31026         });
31027         
31028         this.createToolbar(this);
31029         this.signPanel = this.wrap.createChild({
31030                 tag: 'div',
31031                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31032             }, this.el
31033         );
31034             
31035         this.svgID = Roo.id();
31036         this.svgEl = this.signPanel.createChild({
31037               xmlns : 'http://www.w3.org/2000/svg',
31038               tag : 'svg',
31039               id : this.svgID + "-svg",
31040               width: this.width,
31041               height: this.height,
31042               viewBox: '0 0 '+this.width+' '+this.height,
31043               cn : [
31044                 {
31045                     tag: "rect",
31046                     id: this.svgID + "-svg-r",
31047                     width: this.width,
31048                     height: this.height,
31049                     fill: "#ffa"
31050                 },
31051                 {
31052                     tag: "line",
31053                     id: this.svgID + "-svg-l",
31054                     x1: "0", // start
31055                     y1: (this.height*0.8), // start set the line in 80% of height
31056                     x2: this.width, // end
31057                     y2: (this.height*0.8), // end set the line in 80% of height
31058                     'stroke': "#666",
31059                     'stroke-width': "1",
31060                     'stroke-dasharray': "3",
31061                     'shape-rendering': "crispEdges",
31062                     'pointer-events': "none"
31063                 },
31064                 {
31065                     tag: "path",
31066                     id: this.svgID + "-svg-p",
31067                     'stroke': "navy",
31068                     'stroke-width': "3",
31069                     'fill': "none",
31070                     'pointer-events': 'none'
31071                 }
31072               ]
31073         });
31074         this.createSVG();
31075         this.svgBox = this.svgEl.dom.getScreenCTM();
31076     },
31077     createSVG : function(){ 
31078         var svg = this.signPanel;
31079         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31080         var t = this;
31081
31082         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31083         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31084         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31085         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31086         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31087         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31088         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31089         
31090     },
31091     isTouchEvent : function(e){
31092         return e.type.match(/^touch/);
31093     },
31094     getCoords : function (e) {
31095         var pt    = this.svgEl.dom.createSVGPoint();
31096         pt.x = e.clientX; 
31097         pt.y = e.clientY;
31098         if (this.isTouchEvent(e)) {
31099             pt.x =  e.targetTouches[0].clientX 
31100             pt.y = e.targetTouches[0].clientY;
31101         }
31102         var a = this.svgEl.dom.getScreenCTM();
31103         var b = a.inverse();
31104         var mx = pt.matrixTransform(b);
31105         return mx.x + ',' + mx.y;
31106     },
31107     //mouse event headler 
31108     down : function (e) {
31109         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31110         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31111         
31112         this.isMouseDown = true;
31113         
31114         e.preventDefault();
31115     },
31116     move : function (e) {
31117         if (this.isMouseDown) {
31118             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31119             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31120         }
31121         
31122         e.preventDefault();
31123     },
31124     up : function (e) {
31125         this.isMouseDown = false;
31126         var sp = this.signatureTmp.split(' ');
31127         
31128         if(sp.length > 1){
31129             if(!sp[sp.length-2].match(/^L/)){
31130                 sp.pop();
31131                 sp.pop();
31132                 sp.push("");
31133                 this.signatureTmp = sp.join(" ");
31134             }
31135         }
31136         if(this.getValue() != this.signatureTmp){
31137             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31138             this.isConfirmed = false;
31139         }
31140         e.preventDefault();
31141     },
31142     
31143     /**
31144      * Protected method that will not generally be called directly. It
31145      * is called when the editor creates its toolbar. Override this method if you need to
31146      * add custom toolbar buttons.
31147      * @param {HtmlEditor} editor
31148      */
31149     createToolbar : function(editor){
31150          function btn(id, toggle, handler){
31151             var xid = fid + '-'+ id ;
31152             return {
31153                 id : xid,
31154                 cmd : id,
31155                 cls : 'x-btn-icon x-edit-'+id,
31156                 enableToggle:toggle !== false,
31157                 scope: editor, // was editor...
31158                 handler:handler||editor.relayBtnCmd,
31159                 clickEvent:'mousedown',
31160                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31161                 tabIndex:-1
31162             };
31163         }
31164         
31165         
31166         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31167         this.tb = tb;
31168         this.tb.add(
31169            {
31170                 cls : ' x-signature-btn x-signature-'+id,
31171                 scope: editor, // was editor...
31172                 handler: this.reset,
31173                 clickEvent:'mousedown',
31174                 text: this.labels.clear
31175             },
31176             {
31177                  xtype : 'Fill',
31178                  xns: Roo.Toolbar
31179             }, 
31180             {
31181                 cls : '  x-signature-btn x-signature-'+id,
31182                 scope: editor, // was editor...
31183                 handler: this.confirmHandler,
31184                 clickEvent:'mousedown',
31185                 text: this.labels.confirm
31186             }
31187         );
31188     
31189     },
31190     //public
31191     /**
31192      * when user is clicked confirm then show this image.....
31193      * 
31194      * @return {String} Image Data URI
31195      */
31196     getImageDataURI : function(){
31197         var svg = this.svgEl.dom.parentNode.innerHTML;
31198         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31199         return src; 
31200     },
31201     /**
31202      * 
31203      * @return {Boolean} this.isConfirmed
31204      */
31205     getConfirmed : function(){
31206         return this.isConfirmed;
31207     },
31208     /**
31209      * 
31210      * @return {Number} this.width
31211      */
31212     getWidth : function(){
31213         return this.width;
31214     },
31215     /**
31216      * 
31217      * @return {Number} this.height
31218      */
31219     getHeight : function(){
31220         return this.height;
31221     },
31222     // private
31223     getSignature : function(){
31224         return this.signatureTmp;
31225     },
31226     // private
31227     reset : function(){
31228         this.signatureTmp = '';
31229         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31230         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31231         this.isConfirmed = false;
31232         Roo.form.Signature.superclass.reset.call(this);
31233     },
31234     setSignature : function(s){
31235         this.signatureTmp = s;
31236         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31237         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31238         this.setValue(s);
31239         this.isConfirmed = false;
31240         Roo.form.Signature.superclass.reset.call(this);
31241     }, 
31242     test : function(){
31243 //        Roo.log(this.signPanel.dom.contentWindow.up())
31244     },
31245     //private
31246     setConfirmed : function(){
31247         
31248         
31249         
31250 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31251     },
31252     // private
31253     confirmHandler : function(){
31254         if(!this.getSignature()){
31255             return;
31256         }
31257         
31258         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31259         this.setValue(this.getSignature());
31260         this.isConfirmed = true;
31261         
31262         this.fireEvent('confirm', this);
31263     },
31264     // private
31265     // Subclasses should provide the validation implementation by overriding this
31266     validateValue : function(value){
31267         if(this.allowBlank){
31268             return true;
31269         }
31270         
31271         if(this.isConfirmed){
31272             return true;
31273         }
31274         return false;
31275     }
31276 });/*
31277  * Based on:
31278  * Ext JS Library 1.1.1
31279  * Copyright(c) 2006-2007, Ext JS, LLC.
31280  *
31281  * Originally Released Under LGPL - original licence link has changed is not relivant.
31282  *
31283  * Fork - LGPL
31284  * <script type="text/javascript">
31285  */
31286  
31287
31288 /**
31289  * @class Roo.form.ComboBox
31290  * @extends Roo.form.TriggerField
31291  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31292  * @constructor
31293  * Create a new ComboBox.
31294  * @param {Object} config Configuration options
31295  */
31296 Roo.form.Select = function(config){
31297     Roo.form.Select.superclass.constructor.call(this, config);
31298      
31299 };
31300
31301 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31302     /**
31303      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31304      */
31305     /**
31306      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31307      * rendering into an Roo.Editor, defaults to false)
31308      */
31309     /**
31310      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31311      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31312      */
31313     /**
31314      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31315      */
31316     /**
31317      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31318      * the dropdown list (defaults to undefined, with no header element)
31319      */
31320
31321      /**
31322      * @cfg {String/Roo.Template} tpl The template to use to render the output
31323      */
31324      
31325     // private
31326     defaultAutoCreate : {tag: "select"  },
31327     /**
31328      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31329      */
31330     listWidth: undefined,
31331     /**
31332      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31333      * mode = 'remote' or 'text' if mode = 'local')
31334      */
31335     displayField: undefined,
31336     /**
31337      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31338      * mode = 'remote' or 'value' if mode = 'local'). 
31339      * Note: use of a valueField requires the user make a selection
31340      * in order for a value to be mapped.
31341      */
31342     valueField: undefined,
31343     
31344     
31345     /**
31346      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31347      * field's data value (defaults to the underlying DOM element's name)
31348      */
31349     hiddenName: undefined,
31350     /**
31351      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31352      */
31353     listClass: '',
31354     /**
31355      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31356      */
31357     selectedClass: 'x-combo-selected',
31358     /**
31359      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31360      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31361      * which displays a downward arrow icon).
31362      */
31363     triggerClass : 'x-form-arrow-trigger',
31364     /**
31365      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31366      */
31367     shadow:'sides',
31368     /**
31369      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31370      * anchor positions (defaults to 'tl-bl')
31371      */
31372     listAlign: 'tl-bl?',
31373     /**
31374      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31375      */
31376     maxHeight: 300,
31377     /**
31378      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31379      * query specified by the allQuery config option (defaults to 'query')
31380      */
31381     triggerAction: 'query',
31382     /**
31383      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31384      * (defaults to 4, does not apply if editable = false)
31385      */
31386     minChars : 4,
31387     /**
31388      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31389      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31390      */
31391     typeAhead: false,
31392     /**
31393      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31394      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31395      */
31396     queryDelay: 500,
31397     /**
31398      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31399      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31400      */
31401     pageSize: 0,
31402     /**
31403      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31404      * when editable = true (defaults to false)
31405      */
31406     selectOnFocus:false,
31407     /**
31408      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31409      */
31410     queryParam: 'query',
31411     /**
31412      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31413      * when mode = 'remote' (defaults to 'Loading...')
31414      */
31415     loadingText: 'Loading...',
31416     /**
31417      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31418      */
31419     resizable: false,
31420     /**
31421      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31422      */
31423     handleHeight : 8,
31424     /**
31425      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31426      * traditional select (defaults to true)
31427      */
31428     editable: true,
31429     /**
31430      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31431      */
31432     allQuery: '',
31433     /**
31434      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31435      */
31436     mode: 'remote',
31437     /**
31438      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31439      * listWidth has a higher value)
31440      */
31441     minListWidth : 70,
31442     /**
31443      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31444      * allow the user to set arbitrary text into the field (defaults to false)
31445      */
31446     forceSelection:false,
31447     /**
31448      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31449      * if typeAhead = true (defaults to 250)
31450      */
31451     typeAheadDelay : 250,
31452     /**
31453      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31454      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31455      */
31456     valueNotFoundText : undefined,
31457     
31458     /**
31459      * @cfg {String} defaultValue The value displayed after loading the store.
31460      */
31461     defaultValue: '',
31462     
31463     /**
31464      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31465      */
31466     blockFocus : false,
31467     
31468     /**
31469      * @cfg {Boolean} disableClear Disable showing of clear button.
31470      */
31471     disableClear : false,
31472     /**
31473      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31474      */
31475     alwaysQuery : false,
31476     
31477     //private
31478     addicon : false,
31479     editicon: false,
31480     
31481     // element that contains real text value.. (when hidden is used..)
31482      
31483     // private
31484     onRender : function(ct, position){
31485         Roo.form.Field.prototype.onRender.call(this, ct, position);
31486         
31487         if(this.store){
31488             this.store.on('beforeload', this.onBeforeLoad, this);
31489             this.store.on('load', this.onLoad, this);
31490             this.store.on('loadexception', this.onLoadException, this);
31491             this.store.load({});
31492         }
31493         
31494         
31495         
31496     },
31497
31498     // private
31499     initEvents : function(){
31500         //Roo.form.ComboBox.superclass.initEvents.call(this);
31501  
31502     },
31503
31504     onDestroy : function(){
31505        
31506         if(this.store){
31507             this.store.un('beforeload', this.onBeforeLoad, this);
31508             this.store.un('load', this.onLoad, this);
31509             this.store.un('loadexception', this.onLoadException, this);
31510         }
31511         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31512     },
31513
31514     // private
31515     fireKey : function(e){
31516         if(e.isNavKeyPress() && !this.list.isVisible()){
31517             this.fireEvent("specialkey", this, e);
31518         }
31519     },
31520
31521     // private
31522     onResize: function(w, h){
31523         
31524         return; 
31525     
31526         
31527     },
31528
31529     /**
31530      * Allow or prevent the user from directly editing the field text.  If false is passed,
31531      * the user will only be able to select from the items defined in the dropdown list.  This method
31532      * is the runtime equivalent of setting the 'editable' config option at config time.
31533      * @param {Boolean} value True to allow the user to directly edit the field text
31534      */
31535     setEditable : function(value){
31536          
31537     },
31538
31539     // private
31540     onBeforeLoad : function(){
31541         
31542         Roo.log("Select before load");
31543         return;
31544     
31545         this.innerList.update(this.loadingText ?
31546                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31547         //this.restrictHeight();
31548         this.selectedIndex = -1;
31549     },
31550
31551     // private
31552     onLoad : function(){
31553
31554     
31555         var dom = this.el.dom;
31556         dom.innerHTML = '';
31557          var od = dom.ownerDocument;
31558          
31559         if (this.emptyText) {
31560             var op = od.createElement('option');
31561             op.setAttribute('value', '');
31562             op.innerHTML = String.format('{0}', this.emptyText);
31563             dom.appendChild(op);
31564         }
31565         if(this.store.getCount() > 0){
31566            
31567             var vf = this.valueField;
31568             var df = this.displayField;
31569             this.store.data.each(function(r) {
31570                 // which colmsn to use... testing - cdoe / title..
31571                 var op = od.createElement('option');
31572                 op.setAttribute('value', r.data[vf]);
31573                 op.innerHTML = String.format('{0}', r.data[df]);
31574                 dom.appendChild(op);
31575             });
31576             if (typeof(this.defaultValue != 'undefined')) {
31577                 this.setValue(this.defaultValue);
31578             }
31579             
31580              
31581         }else{
31582             //this.onEmptyResults();
31583         }
31584         //this.el.focus();
31585     },
31586     // private
31587     onLoadException : function()
31588     {
31589         dom.innerHTML = '';
31590             
31591         Roo.log("Select on load exception");
31592         return;
31593     
31594         this.collapse();
31595         Roo.log(this.store.reader.jsonData);
31596         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31597             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31598         }
31599         
31600         
31601     },
31602     // private
31603     onTypeAhead : function(){
31604          
31605     },
31606
31607     // private
31608     onSelect : function(record, index){
31609         Roo.log('on select?');
31610         return;
31611         if(this.fireEvent('beforeselect', this, record, index) !== false){
31612             this.setFromData(index > -1 ? record.data : false);
31613             this.collapse();
31614             this.fireEvent('select', this, record, index);
31615         }
31616     },
31617
31618     /**
31619      * Returns the currently selected field value or empty string if no value is set.
31620      * @return {String} value The selected value
31621      */
31622     getValue : function(){
31623         var dom = this.el.dom;
31624         this.value = dom.options[dom.selectedIndex].value;
31625         return this.value;
31626         
31627     },
31628
31629     /**
31630      * Clears any text/value currently set in the field
31631      */
31632     clearValue : function(){
31633         this.value = '';
31634         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31635         
31636     },
31637
31638     /**
31639      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31640      * will be displayed in the field.  If the value does not match the data value of an existing item,
31641      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31642      * Otherwise the field will be blank (although the value will still be set).
31643      * @param {String} value The value to match
31644      */
31645     setValue : function(v){
31646         var d = this.el.dom;
31647         for (var i =0; i < d.options.length;i++) {
31648             if (v == d.options[i].value) {
31649                 d.selectedIndex = i;
31650                 this.value = v;
31651                 return;
31652             }
31653         }
31654         this.clearValue();
31655     },
31656     /**
31657      * @property {Object} the last set data for the element
31658      */
31659     
31660     lastData : false,
31661     /**
31662      * Sets the value of the field based on a object which is related to the record format for the store.
31663      * @param {Object} value the value to set as. or false on reset?
31664      */
31665     setFromData : function(o){
31666         Roo.log('setfrom data?');
31667          
31668         
31669         
31670     },
31671     // private
31672     reset : function(){
31673         this.clearValue();
31674     },
31675     // private
31676     findRecord : function(prop, value){
31677         
31678         return false;
31679     
31680         var record;
31681         if(this.store.getCount() > 0){
31682             this.store.each(function(r){
31683                 if(r.data[prop] == value){
31684                     record = r;
31685                     return false;
31686                 }
31687                 return true;
31688             });
31689         }
31690         return record;
31691     },
31692     
31693     getName: function()
31694     {
31695         // returns hidden if it's set..
31696         if (!this.rendered) {return ''};
31697         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
31698         
31699     },
31700      
31701
31702     
31703
31704     // private
31705     onEmptyResults : function(){
31706         Roo.log('empty results');
31707         //this.collapse();
31708     },
31709
31710     /**
31711      * Returns true if the dropdown list is expanded, else false.
31712      */
31713     isExpanded : function(){
31714         return false;
31715     },
31716
31717     /**
31718      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
31719      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31720      * @param {String} value The data value of the item to select
31721      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31722      * selected item if it is not currently in view (defaults to true)
31723      * @return {Boolean} True if the value matched an item in the list, else false
31724      */
31725     selectByValue : function(v, scrollIntoView){
31726         Roo.log('select By Value');
31727         return false;
31728     
31729         if(v !== undefined && v !== null){
31730             var r = this.findRecord(this.valueField || this.displayField, v);
31731             if(r){
31732                 this.select(this.store.indexOf(r), scrollIntoView);
31733                 return true;
31734             }
31735         }
31736         return false;
31737     },
31738
31739     /**
31740      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
31741      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
31742      * @param {Number} index The zero-based index of the list item to select
31743      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
31744      * selected item if it is not currently in view (defaults to true)
31745      */
31746     select : function(index, scrollIntoView){
31747         Roo.log('select ');
31748         return  ;
31749         
31750         this.selectedIndex = index;
31751         this.view.select(index);
31752         if(scrollIntoView !== false){
31753             var el = this.view.getNode(index);
31754             if(el){
31755                 this.innerList.scrollChildIntoView(el, false);
31756             }
31757         }
31758     },
31759
31760       
31761
31762     // private
31763     validateBlur : function(){
31764         
31765         return;
31766         
31767     },
31768
31769     // private
31770     initQuery : function(){
31771         this.doQuery(this.getRawValue());
31772     },
31773
31774     // private
31775     doForce : function(){
31776         if(this.el.dom.value.length > 0){
31777             this.el.dom.value =
31778                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
31779              
31780         }
31781     },
31782
31783     /**
31784      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
31785      * query allowing the query action to be canceled if needed.
31786      * @param {String} query The SQL query to execute
31787      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
31788      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
31789      * saved in the current store (defaults to false)
31790      */
31791     doQuery : function(q, forceAll){
31792         
31793         Roo.log('doQuery?');
31794         if(q === undefined || q === null){
31795             q = '';
31796         }
31797         var qe = {
31798             query: q,
31799             forceAll: forceAll,
31800             combo: this,
31801             cancel:false
31802         };
31803         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
31804             return false;
31805         }
31806         q = qe.query;
31807         forceAll = qe.forceAll;
31808         if(forceAll === true || (q.length >= this.minChars)){
31809             if(this.lastQuery != q || this.alwaysQuery){
31810                 this.lastQuery = q;
31811                 if(this.mode == 'local'){
31812                     this.selectedIndex = -1;
31813                     if(forceAll){
31814                         this.store.clearFilter();
31815                     }else{
31816                         this.store.filter(this.displayField, q);
31817                     }
31818                     this.onLoad();
31819                 }else{
31820                     this.store.baseParams[this.queryParam] = q;
31821                     this.store.load({
31822                         params: this.getParams(q)
31823                     });
31824                     this.expand();
31825                 }
31826             }else{
31827                 this.selectedIndex = -1;
31828                 this.onLoad();   
31829             }
31830         }
31831     },
31832
31833     // private
31834     getParams : function(q){
31835         var p = {};
31836         //p[this.queryParam] = q;
31837         if(this.pageSize){
31838             p.start = 0;
31839             p.limit = this.pageSize;
31840         }
31841         return p;
31842     },
31843
31844     /**
31845      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
31846      */
31847     collapse : function(){
31848         
31849     },
31850
31851     // private
31852     collapseIf : function(e){
31853         
31854     },
31855
31856     /**
31857      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
31858      */
31859     expand : function(){
31860         
31861     } ,
31862
31863     // private
31864      
31865
31866     /** 
31867     * @cfg {Boolean} grow 
31868     * @hide 
31869     */
31870     /** 
31871     * @cfg {Number} growMin 
31872     * @hide 
31873     */
31874     /** 
31875     * @cfg {Number} growMax 
31876     * @hide 
31877     */
31878     /**
31879      * @hide
31880      * @method autoSize
31881      */
31882     
31883     setWidth : function()
31884     {
31885         
31886     },
31887     getResizeEl : function(){
31888         return this.el;
31889     }
31890 });//<script type="text/javasscript">
31891  
31892
31893 /**
31894  * @class Roo.DDView
31895  * A DnD enabled version of Roo.View.
31896  * @param {Element/String} container The Element in which to create the View.
31897  * @param {String} tpl The template string used to create the markup for each element of the View
31898  * @param {Object} config The configuration properties. These include all the config options of
31899  * {@link Roo.View} plus some specific to this class.<br>
31900  * <p>
31901  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31902  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31903  * <p>
31904  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31905 .x-view-drag-insert-above {
31906         border-top:1px dotted #3366cc;
31907 }
31908 .x-view-drag-insert-below {
31909         border-bottom:1px dotted #3366cc;
31910 }
31911 </code></pre>
31912  * 
31913  */
31914  
31915 Roo.DDView = function(container, tpl, config) {
31916     Roo.DDView.superclass.constructor.apply(this, arguments);
31917     this.getEl().setStyle("outline", "0px none");
31918     this.getEl().unselectable();
31919     if (this.dragGroup) {
31920                 this.setDraggable(this.dragGroup.split(","));
31921     }
31922     if (this.dropGroup) {
31923                 this.setDroppable(this.dropGroup.split(","));
31924     }
31925     if (this.deletable) {
31926         this.setDeletable();
31927     }
31928     this.isDirtyFlag = false;
31929         this.addEvents({
31930                 "drop" : true
31931         });
31932 };
31933
31934 Roo.extend(Roo.DDView, Roo.View, {
31935 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31936 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31937 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31938 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31939
31940         isFormField: true,
31941
31942         reset: Roo.emptyFn,
31943         
31944         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31945
31946         validate: function() {
31947                 return true;
31948         },
31949         
31950         destroy: function() {
31951                 this.purgeListeners();
31952                 this.getEl.removeAllListeners();
31953                 this.getEl().remove();
31954                 if (this.dragZone) {
31955                         if (this.dragZone.destroy) {
31956                                 this.dragZone.destroy();
31957                         }
31958                 }
31959                 if (this.dropZone) {
31960                         if (this.dropZone.destroy) {
31961                                 this.dropZone.destroy();
31962                         }
31963                 }
31964         },
31965
31966 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31967         getName: function() {
31968                 return this.name;
31969         },
31970
31971 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31972         setValue: function(v) {
31973                 if (!this.store) {
31974                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31975                 }
31976                 var data = {};
31977                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31978                 this.store.proxy = new Roo.data.MemoryProxy(data);
31979                 this.store.load();
31980         },
31981
31982 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31983         getValue: function() {
31984                 var result = '(';
31985                 this.store.each(function(rec) {
31986                         result += rec.id + ',';
31987                 });
31988                 return result.substr(0, result.length - 1) + ')';
31989         },
31990         
31991         getIds: function() {
31992                 var i = 0, result = new Array(this.store.getCount());
31993                 this.store.each(function(rec) {
31994                         result[i++] = rec.id;
31995                 });
31996                 return result;
31997         },
31998         
31999         isDirty: function() {
32000                 return this.isDirtyFlag;
32001         },
32002
32003 /**
32004  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32005  *      whole Element becomes the target, and this causes the drop gesture to append.
32006  */
32007     getTargetFromEvent : function(e) {
32008                 var target = e.getTarget();
32009                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32010                 target = target.parentNode;
32011                 }
32012                 if (!target) {
32013                         target = this.el.dom.lastChild || this.el.dom;
32014                 }
32015                 return target;
32016     },
32017
32018 /**
32019  *      Create the drag data which consists of an object which has the property "ddel" as
32020  *      the drag proxy element. 
32021  */
32022     getDragData : function(e) {
32023         var target = this.findItemFromChild(e.getTarget());
32024                 if(target) {
32025                         this.handleSelection(e);
32026                         var selNodes = this.getSelectedNodes();
32027             var dragData = {
32028                 source: this,
32029                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32030                 nodes: selNodes,
32031                 records: []
32032                         };
32033                         var selectedIndices = this.getSelectedIndexes();
32034                         for (var i = 0; i < selectedIndices.length; i++) {
32035                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32036                         }
32037                         if (selNodes.length == 1) {
32038                                 dragData.ddel = target.cloneNode(true); // the div element
32039                         } else {
32040                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32041                                 div.className = 'multi-proxy';
32042                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32043                                         div.appendChild(selNodes[i].cloneNode(true));
32044                                 }
32045                                 dragData.ddel = div;
32046                         }
32047             //console.log(dragData)
32048             //console.log(dragData.ddel.innerHTML)
32049                         return dragData;
32050                 }
32051         //console.log('nodragData')
32052                 return false;
32053     },
32054     
32055 /**     Specify to which ddGroup items in this DDView may be dragged. */
32056     setDraggable: function(ddGroup) {
32057         if (ddGroup instanceof Array) {
32058                 Roo.each(ddGroup, this.setDraggable, this);
32059                 return;
32060         }
32061         if (this.dragZone) {
32062                 this.dragZone.addToGroup(ddGroup);
32063         } else {
32064                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32065                                 containerScroll: true,
32066                                 ddGroup: ddGroup 
32067
32068                         });
32069 //                      Draggability implies selection. DragZone's mousedown selects the element.
32070                         if (!this.multiSelect) { this.singleSelect = true; }
32071
32072 //                      Wire the DragZone's handlers up to methods in *this*
32073                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32074                 }
32075     },
32076
32077 /**     Specify from which ddGroup this DDView accepts drops. */
32078     setDroppable: function(ddGroup) {
32079         if (ddGroup instanceof Array) {
32080                 Roo.each(ddGroup, this.setDroppable, this);
32081                 return;
32082         }
32083         if (this.dropZone) {
32084                 this.dropZone.addToGroup(ddGroup);
32085         } else {
32086                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32087                                 containerScroll: true,
32088                                 ddGroup: ddGroup
32089                         });
32090
32091 //                      Wire the DropZone's handlers up to methods in *this*
32092                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32093                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32094                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32095                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32096                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32097                 }
32098     },
32099
32100 /**     Decide whether to drop above or below a View node. */
32101     getDropPoint : function(e, n, dd){
32102         if (n == this.el.dom) { return "above"; }
32103                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32104                 var c = t + (b - t) / 2;
32105                 var y = Roo.lib.Event.getPageY(e);
32106                 if(y <= c) {
32107                         return "above";
32108                 }else{
32109                         return "below";
32110                 }
32111     },
32112
32113     onNodeEnter : function(n, dd, e, data){
32114                 return false;
32115     },
32116     
32117     onNodeOver : function(n, dd, e, data){
32118                 var pt = this.getDropPoint(e, n, dd);
32119                 // set the insert point style on the target node
32120                 var dragElClass = this.dropNotAllowed;
32121                 if (pt) {
32122                         var targetElClass;
32123                         if (pt == "above"){
32124                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32125                                 targetElClass = "x-view-drag-insert-above";
32126                         } else {
32127                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32128                                 targetElClass = "x-view-drag-insert-below";
32129                         }
32130                         if (this.lastInsertClass != targetElClass){
32131                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32132                                 this.lastInsertClass = targetElClass;
32133                         }
32134                 }
32135                 return dragElClass;
32136         },
32137
32138     onNodeOut : function(n, dd, e, data){
32139                 this.removeDropIndicators(n);
32140     },
32141
32142     onNodeDrop : function(n, dd, e, data){
32143         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32144                 return false;
32145         }
32146         var pt = this.getDropPoint(e, n, dd);
32147                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32148                 if (pt == "below") { insertAt++; }
32149                 for (var i = 0; i < data.records.length; i++) {
32150                         var r = data.records[i];
32151                         var dup = this.store.getById(r.id);
32152                         if (dup && (dd != this.dragZone)) {
32153                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32154                         } else {
32155                                 if (data.copy) {
32156                                         this.store.insert(insertAt++, r.copy());
32157                                 } else {
32158                                         data.source.isDirtyFlag = true;
32159                                         r.store.remove(r);
32160                                         this.store.insert(insertAt++, r);
32161                                 }
32162                                 this.isDirtyFlag = true;
32163                         }
32164                 }
32165                 this.dragZone.cachedTarget = null;
32166                 return true;
32167     },
32168
32169     removeDropIndicators : function(n){
32170                 if(n){
32171                         Roo.fly(n).removeClass([
32172                                 "x-view-drag-insert-above",
32173                                 "x-view-drag-insert-below"]);
32174                         this.lastInsertClass = "_noclass";
32175                 }
32176     },
32177
32178 /**
32179  *      Utility method. Add a delete option to the DDView's context menu.
32180  *      @param {String} imageUrl The URL of the "delete" icon image.
32181  */
32182         setDeletable: function(imageUrl) {
32183                 if (!this.singleSelect && !this.multiSelect) {
32184                         this.singleSelect = true;
32185                 }
32186                 var c = this.getContextMenu();
32187                 this.contextMenu.on("itemclick", function(item) {
32188                         switch (item.id) {
32189                                 case "delete":
32190                                         this.remove(this.getSelectedIndexes());
32191                                         break;
32192                         }
32193                 }, this);
32194                 this.contextMenu.add({
32195                         icon: imageUrl,
32196                         id: "delete",
32197                         text: 'Delete'
32198                 });
32199         },
32200         
32201 /**     Return the context menu for this DDView. */
32202         getContextMenu: function() {
32203                 if (!this.contextMenu) {
32204 //                      Create the View's context menu
32205                         this.contextMenu = new Roo.menu.Menu({
32206                                 id: this.id + "-contextmenu"
32207                         });
32208                         this.el.on("contextmenu", this.showContextMenu, this);
32209                 }
32210                 return this.contextMenu;
32211         },
32212         
32213         disableContextMenu: function() {
32214                 if (this.contextMenu) {
32215                         this.el.un("contextmenu", this.showContextMenu, this);
32216                 }
32217         },
32218
32219         showContextMenu: function(e, item) {
32220         item = this.findItemFromChild(e.getTarget());
32221                 if (item) {
32222                         e.stopEvent();
32223                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32224                         this.contextMenu.showAt(e.getXY());
32225             }
32226     },
32227
32228 /**
32229  *      Remove {@link Roo.data.Record}s at the specified indices.
32230  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32231  */
32232     remove: function(selectedIndices) {
32233                 selectedIndices = [].concat(selectedIndices);
32234                 for (var i = 0; i < selectedIndices.length; i++) {
32235                         var rec = this.store.getAt(selectedIndices[i]);
32236                         this.store.remove(rec);
32237                 }
32238     },
32239
32240 /**
32241  *      Double click fires the event, but also, if this is draggable, and there is only one other
32242  *      related DropZone, it transfers the selected node.
32243  */
32244     onDblClick : function(e){
32245         var item = this.findItemFromChild(e.getTarget());
32246         if(item){
32247             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32248                 return false;
32249             }
32250             if (this.dragGroup) {
32251                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32252                     while (targets.indexOf(this.dropZone) > -1) {
32253                             targets.remove(this.dropZone);
32254                                 }
32255                     if (targets.length == 1) {
32256                                         this.dragZone.cachedTarget = null;
32257                         var el = Roo.get(targets[0].getEl());
32258                         var box = el.getBox(true);
32259                         targets[0].onNodeDrop(el.dom, {
32260                                 target: el.dom,
32261                                 xy: [box.x, box.y + box.height - 1]
32262                         }, null, this.getDragData(e));
32263                     }
32264                 }
32265         }
32266     },
32267     
32268     handleSelection: function(e) {
32269                 this.dragZone.cachedTarget = null;
32270         var item = this.findItemFromChild(e.getTarget());
32271         if (!item) {
32272                 this.clearSelections(true);
32273                 return;
32274         }
32275                 if (item && (this.multiSelect || this.singleSelect)){
32276                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32277                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32278                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32279                                 this.unselect(item);
32280                         } else {
32281                                 this.select(item, this.multiSelect && e.ctrlKey);
32282                                 this.lastSelection = item;
32283                         }
32284                 }
32285     },
32286
32287     onItemClick : function(item, index, e){
32288                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32289                         return false;
32290                 }
32291                 return true;
32292     },
32293
32294     unselect : function(nodeInfo, suppressEvent){
32295                 var node = this.getNode(nodeInfo);
32296                 if(node && this.isSelected(node)){
32297                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32298                                 Roo.fly(node).removeClass(this.selectedClass);
32299                                 this.selections.remove(node);
32300                                 if(!suppressEvent){
32301                                         this.fireEvent("selectionchange", this, this.selections);
32302                                 }
32303                         }
32304                 }
32305     }
32306 });
32307 /*
32308  * Based on:
32309  * Ext JS Library 1.1.1
32310  * Copyright(c) 2006-2007, Ext JS, LLC.
32311  *
32312  * Originally Released Under LGPL - original licence link has changed is not relivant.
32313  *
32314  * Fork - LGPL
32315  * <script type="text/javascript">
32316  */
32317  
32318 /**
32319  * @class Roo.LayoutManager
32320  * @extends Roo.util.Observable
32321  * Base class for layout managers.
32322  */
32323 Roo.LayoutManager = function(container, config){
32324     Roo.LayoutManager.superclass.constructor.call(this);
32325     this.el = Roo.get(container);
32326     // ie scrollbar fix
32327     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32328         document.body.scroll = "no";
32329     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32330         this.el.position('relative');
32331     }
32332     this.id = this.el.id;
32333     this.el.addClass("x-layout-container");
32334     /** false to disable window resize monitoring @type Boolean */
32335     this.monitorWindowResize = true;
32336     this.regions = {};
32337     this.addEvents({
32338         /**
32339          * @event layout
32340          * Fires when a layout is performed. 
32341          * @param {Roo.LayoutManager} this
32342          */
32343         "layout" : true,
32344         /**
32345          * @event regionresized
32346          * Fires when the user resizes a region. 
32347          * @param {Roo.LayoutRegion} region The resized region
32348          * @param {Number} newSize The new size (width for east/west, height for north/south)
32349          */
32350         "regionresized" : true,
32351         /**
32352          * @event regioncollapsed
32353          * Fires when a region is collapsed. 
32354          * @param {Roo.LayoutRegion} region The collapsed region
32355          */
32356         "regioncollapsed" : true,
32357         /**
32358          * @event regionexpanded
32359          * Fires when a region is expanded.  
32360          * @param {Roo.LayoutRegion} region The expanded region
32361          */
32362         "regionexpanded" : true
32363     });
32364     this.updating = false;
32365     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32366 };
32367
32368 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32369     /**
32370      * Returns true if this layout is currently being updated
32371      * @return {Boolean}
32372      */
32373     isUpdating : function(){
32374         return this.updating; 
32375     },
32376     
32377     /**
32378      * Suspend the LayoutManager from doing auto-layouts while
32379      * making multiple add or remove calls
32380      */
32381     beginUpdate : function(){
32382         this.updating = true;    
32383     },
32384     
32385     /**
32386      * Restore auto-layouts and optionally disable the manager from performing a layout
32387      * @param {Boolean} noLayout true to disable a layout update 
32388      */
32389     endUpdate : function(noLayout){
32390         this.updating = false;
32391         if(!noLayout){
32392             this.layout();
32393         }    
32394     },
32395     
32396     layout: function(){
32397         
32398     },
32399     
32400     onRegionResized : function(region, newSize){
32401         this.fireEvent("regionresized", region, newSize);
32402         this.layout();
32403     },
32404     
32405     onRegionCollapsed : function(region){
32406         this.fireEvent("regioncollapsed", region);
32407     },
32408     
32409     onRegionExpanded : function(region){
32410         this.fireEvent("regionexpanded", region);
32411     },
32412         
32413     /**
32414      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32415      * performs box-model adjustments.
32416      * @return {Object} The size as an object {width: (the width), height: (the height)}
32417      */
32418     getViewSize : function(){
32419         var size;
32420         if(this.el.dom != document.body){
32421             size = this.el.getSize();
32422         }else{
32423             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32424         }
32425         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32426         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32427         return size;
32428     },
32429     
32430     /**
32431      * Returns the Element this layout is bound to.
32432      * @return {Roo.Element}
32433      */
32434     getEl : function(){
32435         return this.el;
32436     },
32437     
32438     /**
32439      * Returns the specified region.
32440      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32441      * @return {Roo.LayoutRegion}
32442      */
32443     getRegion : function(target){
32444         return this.regions[target.toLowerCase()];
32445     },
32446     
32447     onWindowResize : function(){
32448         if(this.monitorWindowResize){
32449             this.layout();
32450         }
32451     }
32452 });/*
32453  * Based on:
32454  * Ext JS Library 1.1.1
32455  * Copyright(c) 2006-2007, Ext JS, LLC.
32456  *
32457  * Originally Released Under LGPL - original licence link has changed is not relivant.
32458  *
32459  * Fork - LGPL
32460  * <script type="text/javascript">
32461  */
32462 /**
32463  * @class Roo.BorderLayout
32464  * @extends Roo.LayoutManager
32465  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32466  * please see: <br><br>
32467  * <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>
32468  * <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>
32469  * Example:
32470  <pre><code>
32471  var layout = new Roo.BorderLayout(document.body, {
32472     north: {
32473         initialSize: 25,
32474         titlebar: false
32475     },
32476     west: {
32477         split:true,
32478         initialSize: 200,
32479         minSize: 175,
32480         maxSize: 400,
32481         titlebar: true,
32482         collapsible: true
32483     },
32484     east: {
32485         split:true,
32486         initialSize: 202,
32487         minSize: 175,
32488         maxSize: 400,
32489         titlebar: true,
32490         collapsible: true
32491     },
32492     south: {
32493         split:true,
32494         initialSize: 100,
32495         minSize: 100,
32496         maxSize: 200,
32497         titlebar: true,
32498         collapsible: true
32499     },
32500     center: {
32501         titlebar: true,
32502         autoScroll:true,
32503         resizeTabs: true,
32504         minTabWidth: 50,
32505         preferredTabWidth: 150
32506     }
32507 });
32508
32509 // shorthand
32510 var CP = Roo.ContentPanel;
32511
32512 layout.beginUpdate();
32513 layout.add("north", new CP("north", "North"));
32514 layout.add("south", new CP("south", {title: "South", closable: true}));
32515 layout.add("west", new CP("west", {title: "West"}));
32516 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32517 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32518 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32519 layout.getRegion("center").showPanel("center1");
32520 layout.endUpdate();
32521 </code></pre>
32522
32523 <b>The container the layout is rendered into can be either the body element or any other element.
32524 If it is not the body element, the container needs to either be an absolute positioned element,
32525 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32526 the container size if it is not the body element.</b>
32527
32528 * @constructor
32529 * Create a new BorderLayout
32530 * @param {String/HTMLElement/Element} container The container this layout is bound to
32531 * @param {Object} config Configuration options
32532  */
32533 Roo.BorderLayout = function(container, config){
32534     config = config || {};
32535     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32536     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32537     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32538         var target = this.factory.validRegions[i];
32539         if(config[target]){
32540             this.addRegion(target, config[target]);
32541         }
32542     }
32543 };
32544
32545 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32546     /**
32547      * Creates and adds a new region if it doesn't already exist.
32548      * @param {String} target The target region key (north, south, east, west or center).
32549      * @param {Object} config The regions config object
32550      * @return {BorderLayoutRegion} The new region
32551      */
32552     addRegion : function(target, config){
32553         if(!this.regions[target]){
32554             var r = this.factory.create(target, this, config);
32555             this.bindRegion(target, r);
32556         }
32557         return this.regions[target];
32558     },
32559
32560     // private (kinda)
32561     bindRegion : function(name, r){
32562         this.regions[name] = r;
32563         r.on("visibilitychange", this.layout, this);
32564         r.on("paneladded", this.layout, this);
32565         r.on("panelremoved", this.layout, this);
32566         r.on("invalidated", this.layout, this);
32567         r.on("resized", this.onRegionResized, this);
32568         r.on("collapsed", this.onRegionCollapsed, this);
32569         r.on("expanded", this.onRegionExpanded, this);
32570     },
32571
32572     /**
32573      * Performs a layout update.
32574      */
32575     layout : function(){
32576         if(this.updating) return;
32577         var size = this.getViewSize();
32578         var w = size.width;
32579         var h = size.height;
32580         var centerW = w;
32581         var centerH = h;
32582         var centerY = 0;
32583         var centerX = 0;
32584         //var x = 0, y = 0;
32585
32586         var rs = this.regions;
32587         var north = rs["north"];
32588         var south = rs["south"]; 
32589         var west = rs["west"];
32590         var east = rs["east"];
32591         var center = rs["center"];
32592         //if(this.hideOnLayout){ // not supported anymore
32593             //c.el.setStyle("display", "none");
32594         //}
32595         if(north && north.isVisible()){
32596             var b = north.getBox();
32597             var m = north.getMargins();
32598             b.width = w - (m.left+m.right);
32599             b.x = m.left;
32600             b.y = m.top;
32601             centerY = b.height + b.y + m.bottom;
32602             centerH -= centerY;
32603             north.updateBox(this.safeBox(b));
32604         }
32605         if(south && south.isVisible()){
32606             var b = south.getBox();
32607             var m = south.getMargins();
32608             b.width = w - (m.left+m.right);
32609             b.x = m.left;
32610             var totalHeight = (b.height + m.top + m.bottom);
32611             b.y = h - totalHeight + m.top;
32612             centerH -= totalHeight;
32613             south.updateBox(this.safeBox(b));
32614         }
32615         if(west && west.isVisible()){
32616             var b = west.getBox();
32617             var m = west.getMargins();
32618             b.height = centerH - (m.top+m.bottom);
32619             b.x = m.left;
32620             b.y = centerY + m.top;
32621             var totalWidth = (b.width + m.left + m.right);
32622             centerX += totalWidth;
32623             centerW -= totalWidth;
32624             west.updateBox(this.safeBox(b));
32625         }
32626         if(east && east.isVisible()){
32627             var b = east.getBox();
32628             var m = east.getMargins();
32629             b.height = centerH - (m.top+m.bottom);
32630             var totalWidth = (b.width + m.left + m.right);
32631             b.x = w - totalWidth + m.left;
32632             b.y = centerY + m.top;
32633             centerW -= totalWidth;
32634             east.updateBox(this.safeBox(b));
32635         }
32636         if(center){
32637             var m = center.getMargins();
32638             var centerBox = {
32639                 x: centerX + m.left,
32640                 y: centerY + m.top,
32641                 width: centerW - (m.left+m.right),
32642                 height: centerH - (m.top+m.bottom)
32643             };
32644             //if(this.hideOnLayout){
32645                 //center.el.setStyle("display", "block");
32646             //}
32647             center.updateBox(this.safeBox(centerBox));
32648         }
32649         this.el.repaint();
32650         this.fireEvent("layout", this);
32651     },
32652
32653     // private
32654     safeBox : function(box){
32655         box.width = Math.max(0, box.width);
32656         box.height = Math.max(0, box.height);
32657         return box;
32658     },
32659
32660     /**
32661      * Adds a ContentPanel (or subclass) to this layout.
32662      * @param {String} target The target region key (north, south, east, west or center).
32663      * @param {Roo.ContentPanel} panel The panel to add
32664      * @return {Roo.ContentPanel} The added panel
32665      */
32666     add : function(target, panel){
32667          
32668         target = target.toLowerCase();
32669         return this.regions[target].add(panel);
32670     },
32671
32672     /**
32673      * Remove a ContentPanel (or subclass) to this layout.
32674      * @param {String} target The target region key (north, south, east, west or center).
32675      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32676      * @return {Roo.ContentPanel} The removed panel
32677      */
32678     remove : function(target, panel){
32679         target = target.toLowerCase();
32680         return this.regions[target].remove(panel);
32681     },
32682
32683     /**
32684      * Searches all regions for a panel with the specified id
32685      * @param {String} panelId
32686      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32687      */
32688     findPanel : function(panelId){
32689         var rs = this.regions;
32690         for(var target in rs){
32691             if(typeof rs[target] != "function"){
32692                 var p = rs[target].getPanel(panelId);
32693                 if(p){
32694                     return p;
32695                 }
32696             }
32697         }
32698         return null;
32699     },
32700
32701     /**
32702      * Searches all regions for a panel with the specified id and activates (shows) it.
32703      * @param {String/ContentPanel} panelId The panels id or the panel itself
32704      * @return {Roo.ContentPanel} The shown panel or null
32705      */
32706     showPanel : function(panelId) {
32707       var rs = this.regions;
32708       for(var target in rs){
32709          var r = rs[target];
32710          if(typeof r != "function"){
32711             if(r.hasPanel(panelId)){
32712                return r.showPanel(panelId);
32713             }
32714          }
32715       }
32716       return null;
32717    },
32718
32719    /**
32720      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32721      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32722      */
32723     restoreState : function(provider){
32724         if(!provider){
32725             provider = Roo.state.Manager;
32726         }
32727         var sm = new Roo.LayoutStateManager();
32728         sm.init(this, provider);
32729     },
32730
32731     /**
32732      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
32733      * object should contain properties for each region to add ContentPanels to, and each property's value should be
32734      * a valid ContentPanel config object.  Example:
32735      * <pre><code>
32736 // Create the main layout
32737 var layout = new Roo.BorderLayout('main-ct', {
32738     west: {
32739         split:true,
32740         minSize: 175,
32741         titlebar: true
32742     },
32743     center: {
32744         title:'Components'
32745     }
32746 }, 'main-ct');
32747
32748 // Create and add multiple ContentPanels at once via configs
32749 layout.batchAdd({
32750    west: {
32751        id: 'source-files',
32752        autoCreate:true,
32753        title:'Ext Source Files',
32754        autoScroll:true,
32755        fitToFrame:true
32756    },
32757    center : {
32758        el: cview,
32759        autoScroll:true,
32760        fitToFrame:true,
32761        toolbar: tb,
32762        resizeEl:'cbody'
32763    }
32764 });
32765 </code></pre>
32766      * @param {Object} regions An object containing ContentPanel configs by region name
32767      */
32768     batchAdd : function(regions){
32769         this.beginUpdate();
32770         for(var rname in regions){
32771             var lr = this.regions[rname];
32772             if(lr){
32773                 this.addTypedPanels(lr, regions[rname]);
32774             }
32775         }
32776         this.endUpdate();
32777     },
32778
32779     // private
32780     addTypedPanels : function(lr, ps){
32781         if(typeof ps == 'string'){
32782             lr.add(new Roo.ContentPanel(ps));
32783         }
32784         else if(ps instanceof Array){
32785             for(var i =0, len = ps.length; i < len; i++){
32786                 this.addTypedPanels(lr, ps[i]);
32787             }
32788         }
32789         else if(!ps.events){ // raw config?
32790             var el = ps.el;
32791             delete ps.el; // prevent conflict
32792             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32793         }
32794         else {  // panel object assumed!
32795             lr.add(ps);
32796         }
32797     },
32798     /**
32799      * Adds a xtype elements to the layout.
32800      * <pre><code>
32801
32802 layout.addxtype({
32803        xtype : 'ContentPanel',
32804        region: 'west',
32805        items: [ .... ]
32806    }
32807 );
32808
32809 layout.addxtype({
32810         xtype : 'NestedLayoutPanel',
32811         region: 'west',
32812         layout: {
32813            center: { },
32814            west: { }   
32815         },
32816         items : [ ... list of content panels or nested layout panels.. ]
32817    }
32818 );
32819 </code></pre>
32820      * @param {Object} cfg Xtype definition of item to add.
32821      */
32822     addxtype : function(cfg)
32823     {
32824         // basically accepts a pannel...
32825         // can accept a layout region..!?!?
32826         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32827         
32828         if (!cfg.xtype.match(/Panel$/)) {
32829             return false;
32830         }
32831         var ret = false;
32832         
32833         if (typeof(cfg.region) == 'undefined') {
32834             Roo.log("Failed to add Panel, region was not set");
32835             Roo.log(cfg);
32836             return false;
32837         }
32838         var region = cfg.region;
32839         delete cfg.region;
32840         
32841           
32842         var xitems = [];
32843         if (cfg.items) {
32844             xitems = cfg.items;
32845             delete cfg.items;
32846         }
32847         var nb = false;
32848         
32849         switch(cfg.xtype) 
32850         {
32851             case 'ContentPanel':  // ContentPanel (el, cfg)
32852             case 'ScrollPanel':  // ContentPanel (el, cfg)
32853             case 'ViewPanel': 
32854                 if(cfg.autoCreate) {
32855                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32856                 } else {
32857                     var el = this.el.createChild();
32858                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32859                 }
32860                 
32861                 this.add(region, ret);
32862                 break;
32863             
32864             
32865             case 'TreePanel': // our new panel!
32866                 cfg.el = this.el.createChild();
32867                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32868                 this.add(region, ret);
32869                 break;
32870             
32871             case 'NestedLayoutPanel': 
32872                 // create a new Layout (which is  a Border Layout...
32873                 var el = this.el.createChild();
32874                 var clayout = cfg.layout;
32875                 delete cfg.layout;
32876                 clayout.items   = clayout.items  || [];
32877                 // replace this exitems with the clayout ones..
32878                 xitems = clayout.items;
32879                  
32880                 
32881                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32882                     cfg.background = false;
32883                 }
32884                 var layout = new Roo.BorderLayout(el, clayout);
32885                 
32886                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32887                 //console.log('adding nested layout panel '  + cfg.toSource());
32888                 this.add(region, ret);
32889                 nb = {}; /// find first...
32890                 break;
32891                 
32892             case 'GridPanel': 
32893             
32894                 // needs grid and region
32895                 
32896                 //var el = this.getRegion(region).el.createChild();
32897                 var el = this.el.createChild();
32898                 // create the grid first...
32899                 
32900                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32901                 delete cfg.grid;
32902                 if (region == 'center' && this.active ) {
32903                     cfg.background = false;
32904                 }
32905                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32906                 
32907                 this.add(region, ret);
32908                 if (cfg.background) {
32909                     ret.on('activate', function(gp) {
32910                         if (!gp.grid.rendered) {
32911                             gp.grid.render();
32912                         }
32913                     });
32914                 } else {
32915                     grid.render();
32916                 }
32917                 break;
32918            
32919            
32920            
32921                 
32922                 
32923                 
32924             default:
32925                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32926                     
32927                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32928                     this.add(region, ret);
32929                 } else {
32930                 
32931                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32932                     return null;
32933                 }
32934                 
32935              // GridPanel (grid, cfg)
32936             
32937         }
32938         this.beginUpdate();
32939         // add children..
32940         var region = '';
32941         var abn = {};
32942         Roo.each(xitems, function(i)  {
32943             region = nb && i.region ? i.region : false;
32944             
32945             var add = ret.addxtype(i);
32946            
32947             if (region) {
32948                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32949                 if (!i.background) {
32950                     abn[region] = nb[region] ;
32951                 }
32952             }
32953             
32954         });
32955         this.endUpdate();
32956
32957         // make the last non-background panel active..
32958         //if (nb) { Roo.log(abn); }
32959         if (nb) {
32960             
32961             for(var r in abn) {
32962                 region = this.getRegion(r);
32963                 if (region) {
32964                     // tried using nb[r], but it does not work..
32965                      
32966                     region.showPanel(abn[r]);
32967                    
32968                 }
32969             }
32970         }
32971         return ret;
32972         
32973     }
32974 });
32975
32976 /**
32977  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32978  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32979  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32980  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32981  * <pre><code>
32982 // shorthand
32983 var CP = Roo.ContentPanel;
32984
32985 var layout = Roo.BorderLayout.create({
32986     north: {
32987         initialSize: 25,
32988         titlebar: false,
32989         panels: [new CP("north", "North")]
32990     },
32991     west: {
32992         split:true,
32993         initialSize: 200,
32994         minSize: 175,
32995         maxSize: 400,
32996         titlebar: true,
32997         collapsible: true,
32998         panels: [new CP("west", {title: "West"})]
32999     },
33000     east: {
33001         split:true,
33002         initialSize: 202,
33003         minSize: 175,
33004         maxSize: 400,
33005         titlebar: true,
33006         collapsible: true,
33007         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33008     },
33009     south: {
33010         split:true,
33011         initialSize: 100,
33012         minSize: 100,
33013         maxSize: 200,
33014         titlebar: true,
33015         collapsible: true,
33016         panels: [new CP("south", {title: "South", closable: true})]
33017     },
33018     center: {
33019         titlebar: true,
33020         autoScroll:true,
33021         resizeTabs: true,
33022         minTabWidth: 50,
33023         preferredTabWidth: 150,
33024         panels: [
33025             new CP("center1", {title: "Close Me", closable: true}),
33026             new CP("center2", {title: "Center Panel", closable: false})
33027         ]
33028     }
33029 }, document.body);
33030
33031 layout.getRegion("center").showPanel("center1");
33032 </code></pre>
33033  * @param config
33034  * @param targetEl
33035  */
33036 Roo.BorderLayout.create = function(config, targetEl){
33037     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33038     layout.beginUpdate();
33039     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33040     for(var j = 0, jlen = regions.length; j < jlen; j++){
33041         var lr = regions[j];
33042         if(layout.regions[lr] && config[lr].panels){
33043             var r = layout.regions[lr];
33044             var ps = config[lr].panels;
33045             layout.addTypedPanels(r, ps);
33046         }
33047     }
33048     layout.endUpdate();
33049     return layout;
33050 };
33051
33052 // private
33053 Roo.BorderLayout.RegionFactory = {
33054     // private
33055     validRegions : ["north","south","east","west","center"],
33056
33057     // private
33058     create : function(target, mgr, config){
33059         target = target.toLowerCase();
33060         if(config.lightweight || config.basic){
33061             return new Roo.BasicLayoutRegion(mgr, config, target);
33062         }
33063         switch(target){
33064             case "north":
33065                 return new Roo.NorthLayoutRegion(mgr, config);
33066             case "south":
33067                 return new Roo.SouthLayoutRegion(mgr, config);
33068             case "east":
33069                 return new Roo.EastLayoutRegion(mgr, config);
33070             case "west":
33071                 return new Roo.WestLayoutRegion(mgr, config);
33072             case "center":
33073                 return new Roo.CenterLayoutRegion(mgr, config);
33074         }
33075         throw 'Layout region "'+target+'" not supported.';
33076     }
33077 };/*
33078  * Based on:
33079  * Ext JS Library 1.1.1
33080  * Copyright(c) 2006-2007, Ext JS, LLC.
33081  *
33082  * Originally Released Under LGPL - original licence link has changed is not relivant.
33083  *
33084  * Fork - LGPL
33085  * <script type="text/javascript">
33086  */
33087  
33088 /**
33089  * @class Roo.BasicLayoutRegion
33090  * @extends Roo.util.Observable
33091  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33092  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33093  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33094  */
33095 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33096     this.mgr = mgr;
33097     this.position  = pos;
33098     this.events = {
33099         /**
33100          * @scope Roo.BasicLayoutRegion
33101          */
33102         
33103         /**
33104          * @event beforeremove
33105          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33106          * @param {Roo.LayoutRegion} this
33107          * @param {Roo.ContentPanel} panel The panel
33108          * @param {Object} e The cancel event object
33109          */
33110         "beforeremove" : true,
33111         /**
33112          * @event invalidated
33113          * Fires when the layout for this region is changed.
33114          * @param {Roo.LayoutRegion} this
33115          */
33116         "invalidated" : true,
33117         /**
33118          * @event visibilitychange
33119          * Fires when this region is shown or hidden 
33120          * @param {Roo.LayoutRegion} this
33121          * @param {Boolean} visibility true or false
33122          */
33123         "visibilitychange" : true,
33124         /**
33125          * @event paneladded
33126          * Fires when a panel is added. 
33127          * @param {Roo.LayoutRegion} this
33128          * @param {Roo.ContentPanel} panel The panel
33129          */
33130         "paneladded" : true,
33131         /**
33132          * @event panelremoved
33133          * Fires when a panel is removed. 
33134          * @param {Roo.LayoutRegion} this
33135          * @param {Roo.ContentPanel} panel The panel
33136          */
33137         "panelremoved" : true,
33138         /**
33139          * @event collapsed
33140          * Fires when this region is collapsed.
33141          * @param {Roo.LayoutRegion} this
33142          */
33143         "collapsed" : true,
33144         /**
33145          * @event expanded
33146          * Fires when this region is expanded.
33147          * @param {Roo.LayoutRegion} this
33148          */
33149         "expanded" : true,
33150         /**
33151          * @event slideshow
33152          * Fires when this region is slid into view.
33153          * @param {Roo.LayoutRegion} this
33154          */
33155         "slideshow" : true,
33156         /**
33157          * @event slidehide
33158          * Fires when this region slides out of view. 
33159          * @param {Roo.LayoutRegion} this
33160          */
33161         "slidehide" : true,
33162         /**
33163          * @event panelactivated
33164          * Fires when a panel is activated. 
33165          * @param {Roo.LayoutRegion} this
33166          * @param {Roo.ContentPanel} panel The activated panel
33167          */
33168         "panelactivated" : true,
33169         /**
33170          * @event resized
33171          * Fires when the user resizes this region. 
33172          * @param {Roo.LayoutRegion} this
33173          * @param {Number} newSize The new size (width for east/west, height for north/south)
33174          */
33175         "resized" : true
33176     };
33177     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33178     this.panels = new Roo.util.MixedCollection();
33179     this.panels.getKey = this.getPanelId.createDelegate(this);
33180     this.box = null;
33181     this.activePanel = null;
33182     // ensure listeners are added...
33183     
33184     if (config.listeners || config.events) {
33185         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33186             listeners : config.listeners || {},
33187             events : config.events || {}
33188         });
33189     }
33190     
33191     if(skipConfig !== true){
33192         this.applyConfig(config);
33193     }
33194 };
33195
33196 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33197     getPanelId : function(p){
33198         return p.getId();
33199     },
33200     
33201     applyConfig : function(config){
33202         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33203         this.config = config;
33204         
33205     },
33206     
33207     /**
33208      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33209      * the width, for horizontal (north, south) the height.
33210      * @param {Number} newSize The new width or height
33211      */
33212     resizeTo : function(newSize){
33213         var el = this.el ? this.el :
33214                  (this.activePanel ? this.activePanel.getEl() : null);
33215         if(el){
33216             switch(this.position){
33217                 case "east":
33218                 case "west":
33219                     el.setWidth(newSize);
33220                     this.fireEvent("resized", this, newSize);
33221                 break;
33222                 case "north":
33223                 case "south":
33224                     el.setHeight(newSize);
33225                     this.fireEvent("resized", this, newSize);
33226                 break;                
33227             }
33228         }
33229     },
33230     
33231     getBox : function(){
33232         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33233     },
33234     
33235     getMargins : function(){
33236         return this.margins;
33237     },
33238     
33239     updateBox : function(box){
33240         this.box = box;
33241         var el = this.activePanel.getEl();
33242         el.dom.style.left = box.x + "px";
33243         el.dom.style.top = box.y + "px";
33244         this.activePanel.setSize(box.width, box.height);
33245     },
33246     
33247     /**
33248      * Returns the container element for this region.
33249      * @return {Roo.Element}
33250      */
33251     getEl : function(){
33252         return this.activePanel;
33253     },
33254     
33255     /**
33256      * Returns true if this region is currently visible.
33257      * @return {Boolean}
33258      */
33259     isVisible : function(){
33260         return this.activePanel ? true : false;
33261     },
33262     
33263     setActivePanel : function(panel){
33264         panel = this.getPanel(panel);
33265         if(this.activePanel && this.activePanel != panel){
33266             this.activePanel.setActiveState(false);
33267             this.activePanel.getEl().setLeftTop(-10000,-10000);
33268         }
33269         this.activePanel = panel;
33270         panel.setActiveState(true);
33271         if(this.box){
33272             panel.setSize(this.box.width, this.box.height);
33273         }
33274         this.fireEvent("panelactivated", this, panel);
33275         this.fireEvent("invalidated");
33276     },
33277     
33278     /**
33279      * Show the specified panel.
33280      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33281      * @return {Roo.ContentPanel} The shown panel or null
33282      */
33283     showPanel : function(panel){
33284         if(panel = this.getPanel(panel)){
33285             this.setActivePanel(panel);
33286         }
33287         return panel;
33288     },
33289     
33290     /**
33291      * Get the active panel for this region.
33292      * @return {Roo.ContentPanel} The active panel or null
33293      */
33294     getActivePanel : function(){
33295         return this.activePanel;
33296     },
33297     
33298     /**
33299      * Add the passed ContentPanel(s)
33300      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33301      * @return {Roo.ContentPanel} The panel added (if only one was added)
33302      */
33303     add : function(panel){
33304         if(arguments.length > 1){
33305             for(var i = 0, len = arguments.length; i < len; i++) {
33306                 this.add(arguments[i]);
33307             }
33308             return null;
33309         }
33310         if(this.hasPanel(panel)){
33311             this.showPanel(panel);
33312             return panel;
33313         }
33314         var el = panel.getEl();
33315         if(el.dom.parentNode != this.mgr.el.dom){
33316             this.mgr.el.dom.appendChild(el.dom);
33317         }
33318         if(panel.setRegion){
33319             panel.setRegion(this);
33320         }
33321         this.panels.add(panel);
33322         el.setStyle("position", "absolute");
33323         if(!panel.background){
33324             this.setActivePanel(panel);
33325             if(this.config.initialSize && this.panels.getCount()==1){
33326                 this.resizeTo(this.config.initialSize);
33327             }
33328         }
33329         this.fireEvent("paneladded", this, panel);
33330         return panel;
33331     },
33332     
33333     /**
33334      * Returns true if the panel is in this region.
33335      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33336      * @return {Boolean}
33337      */
33338     hasPanel : function(panel){
33339         if(typeof panel == "object"){ // must be panel obj
33340             panel = panel.getId();
33341         }
33342         return this.getPanel(panel) ? true : false;
33343     },
33344     
33345     /**
33346      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33347      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33348      * @param {Boolean} preservePanel Overrides the config preservePanel option
33349      * @return {Roo.ContentPanel} The panel that was removed
33350      */
33351     remove : function(panel, preservePanel){
33352         panel = this.getPanel(panel);
33353         if(!panel){
33354             return null;
33355         }
33356         var e = {};
33357         this.fireEvent("beforeremove", this, panel, e);
33358         if(e.cancel === true){
33359             return null;
33360         }
33361         var panelId = panel.getId();
33362         this.panels.removeKey(panelId);
33363         return panel;
33364     },
33365     
33366     /**
33367      * Returns the panel specified or null if it's not in this region.
33368      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33369      * @return {Roo.ContentPanel}
33370      */
33371     getPanel : function(id){
33372         if(typeof id == "object"){ // must be panel obj
33373             return id;
33374         }
33375         return this.panels.get(id);
33376     },
33377     
33378     /**
33379      * Returns this regions position (north/south/east/west/center).
33380      * @return {String} 
33381      */
33382     getPosition: function(){
33383         return this.position;    
33384     }
33385 });/*
33386  * Based on:
33387  * Ext JS Library 1.1.1
33388  * Copyright(c) 2006-2007, Ext JS, LLC.
33389  *
33390  * Originally Released Under LGPL - original licence link has changed is not relivant.
33391  *
33392  * Fork - LGPL
33393  * <script type="text/javascript">
33394  */
33395  
33396 /**
33397  * @class Roo.LayoutRegion
33398  * @extends Roo.BasicLayoutRegion
33399  * This class represents a region in a layout manager.
33400  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33401  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33402  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33403  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33404  * @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})
33405  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
33406  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33407  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33408  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33409  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33410  * @cfg {String}    title           The title for the region (overrides panel titles)
33411  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33412  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33413  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33414  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33415  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33416  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33417  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33418  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33419  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33420  * @cfg {Boolean}   showPin         True to show a pin button
33421  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33422  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33423  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33424  * @cfg {Number}    width           For East/West panels
33425  * @cfg {Number}    height          For North/South panels
33426  * @cfg {Boolean}   split           To show the splitter
33427  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33428  */
33429 Roo.LayoutRegion = function(mgr, config, pos){
33430     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33431     var dh = Roo.DomHelper;
33432     /** This region's container element 
33433     * @type Roo.Element */
33434     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33435     /** This region's title element 
33436     * @type Roo.Element */
33437
33438     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33439         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33440         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33441     ]}, true);
33442     this.titleEl.enableDisplayMode();
33443     /** This region's title text element 
33444     * @type HTMLElement */
33445     this.titleTextEl = this.titleEl.dom.firstChild;
33446     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33447     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33448     this.closeBtn.enableDisplayMode();
33449     this.closeBtn.on("click", this.closeClicked, this);
33450     this.closeBtn.hide();
33451
33452     this.createBody(config);
33453     this.visible = true;
33454     this.collapsed = false;
33455
33456     if(config.hideWhenEmpty){
33457         this.hide();
33458         this.on("paneladded", this.validateVisibility, this);
33459         this.on("panelremoved", this.validateVisibility, this);
33460     }
33461     this.applyConfig(config);
33462 };
33463
33464 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33465
33466     createBody : function(){
33467         /** This region's body element 
33468         * @type Roo.Element */
33469         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33470     },
33471
33472     applyConfig : function(c){
33473         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33474             var dh = Roo.DomHelper;
33475             if(c.titlebar !== false){
33476                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33477                 this.collapseBtn.on("click", this.collapse, this);
33478                 this.collapseBtn.enableDisplayMode();
33479
33480                 if(c.showPin === true || this.showPin){
33481                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33482                     this.stickBtn.enableDisplayMode();
33483                     this.stickBtn.on("click", this.expand, this);
33484                     this.stickBtn.hide();
33485                 }
33486             }
33487             /** This region's collapsed element
33488             * @type Roo.Element */
33489             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33490                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33491             ]}, true);
33492             if(c.floatable !== false){
33493                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33494                this.collapsedEl.on("click", this.collapseClick, this);
33495             }
33496
33497             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33498                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33499                    id: "message", unselectable: "on", style:{"float":"left"}});
33500                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33501              }
33502             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33503             this.expandBtn.on("click", this.expand, this);
33504         }
33505         if(this.collapseBtn){
33506             this.collapseBtn.setVisible(c.collapsible == true);
33507         }
33508         this.cmargins = c.cmargins || this.cmargins ||
33509                          (this.position == "west" || this.position == "east" ?
33510                              {top: 0, left: 2, right:2, bottom: 0} :
33511                              {top: 2, left: 0, right:0, bottom: 2});
33512         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33513         this.bottomTabs = c.tabPosition != "top";
33514         this.autoScroll = c.autoScroll || false;
33515         if(this.autoScroll){
33516             this.bodyEl.setStyle("overflow", "auto");
33517         }else{
33518             this.bodyEl.setStyle("overflow", "hidden");
33519         }
33520         //if(c.titlebar !== false){
33521             if((!c.titlebar && !c.title) || c.titlebar === false){
33522                 this.titleEl.hide();
33523             }else{
33524                 this.titleEl.show();
33525                 if(c.title){
33526                     this.titleTextEl.innerHTML = c.title;
33527                 }
33528             }
33529         //}
33530         this.duration = c.duration || .30;
33531         this.slideDuration = c.slideDuration || .45;
33532         this.config = c;
33533         if(c.collapsed){
33534             this.collapse(true);
33535         }
33536         if(c.hidden){
33537             this.hide();
33538         }
33539     },
33540     /**
33541      * Returns true if this region is currently visible.
33542      * @return {Boolean}
33543      */
33544     isVisible : function(){
33545         return this.visible;
33546     },
33547
33548     /**
33549      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33550      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33551      */
33552     setCollapsedTitle : function(title){
33553         title = title || "&#160;";
33554         if(this.collapsedTitleTextEl){
33555             this.collapsedTitleTextEl.innerHTML = title;
33556         }
33557     },
33558
33559     getBox : function(){
33560         var b;
33561         if(!this.collapsed){
33562             b = this.el.getBox(false, true);
33563         }else{
33564             b = this.collapsedEl.getBox(false, true);
33565         }
33566         return b;
33567     },
33568
33569     getMargins : function(){
33570         return this.collapsed ? this.cmargins : this.margins;
33571     },
33572
33573     highlight : function(){
33574         this.el.addClass("x-layout-panel-dragover");
33575     },
33576
33577     unhighlight : function(){
33578         this.el.removeClass("x-layout-panel-dragover");
33579     },
33580
33581     updateBox : function(box){
33582         this.box = box;
33583         if(!this.collapsed){
33584             this.el.dom.style.left = box.x + "px";
33585             this.el.dom.style.top = box.y + "px";
33586             this.updateBody(box.width, box.height);
33587         }else{
33588             this.collapsedEl.dom.style.left = box.x + "px";
33589             this.collapsedEl.dom.style.top = box.y + "px";
33590             this.collapsedEl.setSize(box.width, box.height);
33591         }
33592         if(this.tabs){
33593             this.tabs.autoSizeTabs();
33594         }
33595     },
33596
33597     updateBody : function(w, h){
33598         if(w !== null){
33599             this.el.setWidth(w);
33600             w -= this.el.getBorderWidth("rl");
33601             if(this.config.adjustments){
33602                 w += this.config.adjustments[0];
33603             }
33604         }
33605         if(h !== null){
33606             this.el.setHeight(h);
33607             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33608             h -= this.el.getBorderWidth("tb");
33609             if(this.config.adjustments){
33610                 h += this.config.adjustments[1];
33611             }
33612             this.bodyEl.setHeight(h);
33613             if(this.tabs){
33614                 h = this.tabs.syncHeight(h);
33615             }
33616         }
33617         if(this.panelSize){
33618             w = w !== null ? w : this.panelSize.width;
33619             h = h !== null ? h : this.panelSize.height;
33620         }
33621         if(this.activePanel){
33622             var el = this.activePanel.getEl();
33623             w = w !== null ? w : el.getWidth();
33624             h = h !== null ? h : el.getHeight();
33625             this.panelSize = {width: w, height: h};
33626             this.activePanel.setSize(w, h);
33627         }
33628         if(Roo.isIE && this.tabs){
33629             this.tabs.el.repaint();
33630         }
33631     },
33632
33633     /**
33634      * Returns the container element for this region.
33635      * @return {Roo.Element}
33636      */
33637     getEl : function(){
33638         return this.el;
33639     },
33640
33641     /**
33642      * Hides this region.
33643      */
33644     hide : function(){
33645         if(!this.collapsed){
33646             this.el.dom.style.left = "-2000px";
33647             this.el.hide();
33648         }else{
33649             this.collapsedEl.dom.style.left = "-2000px";
33650             this.collapsedEl.hide();
33651         }
33652         this.visible = false;
33653         this.fireEvent("visibilitychange", this, false);
33654     },
33655
33656     /**
33657      * Shows this region if it was previously hidden.
33658      */
33659     show : function(){
33660         if(!this.collapsed){
33661             this.el.show();
33662         }else{
33663             this.collapsedEl.show();
33664         }
33665         this.visible = true;
33666         this.fireEvent("visibilitychange", this, true);
33667     },
33668
33669     closeClicked : function(){
33670         if(this.activePanel){
33671             this.remove(this.activePanel);
33672         }
33673     },
33674
33675     collapseClick : function(e){
33676         if(this.isSlid){
33677            e.stopPropagation();
33678            this.slideIn();
33679         }else{
33680            e.stopPropagation();
33681            this.slideOut();
33682         }
33683     },
33684
33685     /**
33686      * Collapses this region.
33687      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33688      */
33689     collapse : function(skipAnim){
33690         if(this.collapsed) return;
33691         this.collapsed = true;
33692         if(this.split){
33693             this.split.el.hide();
33694         }
33695         if(this.config.animate && skipAnim !== true){
33696             this.fireEvent("invalidated", this);
33697             this.animateCollapse();
33698         }else{
33699             this.el.setLocation(-20000,-20000);
33700             this.el.hide();
33701             this.collapsedEl.show();
33702             this.fireEvent("collapsed", this);
33703             this.fireEvent("invalidated", this);
33704         }
33705     },
33706
33707     animateCollapse : function(){
33708         // overridden
33709     },
33710
33711     /**
33712      * Expands this region if it was previously collapsed.
33713      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33714      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33715      */
33716     expand : function(e, skipAnim){
33717         if(e) e.stopPropagation();
33718         if(!this.collapsed || this.el.hasActiveFx()) return;
33719         if(this.isSlid){
33720             this.afterSlideIn();
33721             skipAnim = true;
33722         }
33723         this.collapsed = false;
33724         if(this.config.animate && skipAnim !== true){
33725             this.animateExpand();
33726         }else{
33727             this.el.show();
33728             if(this.split){
33729                 this.split.el.show();
33730             }
33731             this.collapsedEl.setLocation(-2000,-2000);
33732             this.collapsedEl.hide();
33733             this.fireEvent("invalidated", this);
33734             this.fireEvent("expanded", this);
33735         }
33736     },
33737
33738     animateExpand : function(){
33739         // overridden
33740     },
33741
33742     initTabs : function()
33743     {
33744         this.bodyEl.setStyle("overflow", "hidden");
33745         var ts = new Roo.TabPanel(
33746                 this.bodyEl.dom,
33747                 {
33748                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
33749                     disableTooltips: this.config.disableTabTips,
33750                     toolbar : this.config.toolbar
33751                 }
33752         );
33753         if(this.config.hideTabs){
33754             ts.stripWrap.setDisplayed(false);
33755         }
33756         this.tabs = ts;
33757         ts.resizeTabs = this.config.resizeTabs === true;
33758         ts.minTabWidth = this.config.minTabWidth || 40;
33759         ts.maxTabWidth = this.config.maxTabWidth || 250;
33760         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33761         ts.monitorResize = false;
33762         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33763         ts.bodyEl.addClass('x-layout-tabs-body');
33764         this.panels.each(this.initPanelAsTab, this);
33765     },
33766
33767     initPanelAsTab : function(panel){
33768         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
33769                     this.config.closeOnTab && panel.isClosable());
33770         if(panel.tabTip !== undefined){
33771             ti.setTooltip(panel.tabTip);
33772         }
33773         ti.on("activate", function(){
33774               this.setActivePanel(panel);
33775         }, this);
33776         if(this.config.closeOnTab){
33777             ti.on("beforeclose", function(t, e){
33778                 e.cancel = true;
33779                 this.remove(panel);
33780             }, this);
33781         }
33782         return ti;
33783     },
33784
33785     updatePanelTitle : function(panel, title){
33786         if(this.activePanel == panel){
33787             this.updateTitle(title);
33788         }
33789         if(this.tabs){
33790             var ti = this.tabs.getTab(panel.getEl().id);
33791             ti.setText(title);
33792             if(panel.tabTip !== undefined){
33793                 ti.setTooltip(panel.tabTip);
33794             }
33795         }
33796     },
33797
33798     updateTitle : function(title){
33799         if(this.titleTextEl && !this.config.title){
33800             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33801         }
33802     },
33803
33804     setActivePanel : function(panel){
33805         panel = this.getPanel(panel);
33806         if(this.activePanel && this.activePanel != panel){
33807             this.activePanel.setActiveState(false);
33808         }
33809         this.activePanel = panel;
33810         panel.setActiveState(true);
33811         if(this.panelSize){
33812             panel.setSize(this.panelSize.width, this.panelSize.height);
33813         }
33814         if(this.closeBtn){
33815             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33816         }
33817         this.updateTitle(panel.getTitle());
33818         if(this.tabs){
33819             this.fireEvent("invalidated", this);
33820         }
33821         this.fireEvent("panelactivated", this, panel);
33822     },
33823
33824     /**
33825      * Shows the specified panel.
33826      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33827      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33828      */
33829     showPanel : function(panel){
33830         if(panel = this.getPanel(panel)){
33831             if(this.tabs){
33832                 var tab = this.tabs.getTab(panel.getEl().id);
33833                 if(tab.isHidden()){
33834                     this.tabs.unhideTab(tab.id);
33835                 }
33836                 tab.activate();
33837             }else{
33838                 this.setActivePanel(panel);
33839             }
33840         }
33841         return panel;
33842     },
33843
33844     /**
33845      * Get the active panel for this region.
33846      * @return {Roo.ContentPanel} The active panel or null
33847      */
33848     getActivePanel : function(){
33849         return this.activePanel;
33850     },
33851
33852     validateVisibility : function(){
33853         if(this.panels.getCount() < 1){
33854             this.updateTitle("&#160;");
33855             this.closeBtn.hide();
33856             this.hide();
33857         }else{
33858             if(!this.isVisible()){
33859                 this.show();
33860             }
33861         }
33862     },
33863
33864     /**
33865      * Adds the passed ContentPanel(s) to this region.
33866      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33867      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33868      */
33869     add : function(panel){
33870         if(arguments.length > 1){
33871             for(var i = 0, len = arguments.length; i < len; i++) {
33872                 this.add(arguments[i]);
33873             }
33874             return null;
33875         }
33876         if(this.hasPanel(panel)){
33877             this.showPanel(panel);
33878             return panel;
33879         }
33880         panel.setRegion(this);
33881         this.panels.add(panel);
33882         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33883             this.bodyEl.dom.appendChild(panel.getEl().dom);
33884             if(panel.background !== true){
33885                 this.setActivePanel(panel);
33886             }
33887             this.fireEvent("paneladded", this, panel);
33888             return panel;
33889         }
33890         if(!this.tabs){
33891             this.initTabs();
33892         }else{
33893             this.initPanelAsTab(panel);
33894         }
33895         if(panel.background !== true){
33896             this.tabs.activate(panel.getEl().id);
33897         }
33898         this.fireEvent("paneladded", this, panel);
33899         return panel;
33900     },
33901
33902     /**
33903      * Hides the tab for the specified panel.
33904      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33905      */
33906     hidePanel : function(panel){
33907         if(this.tabs && (panel = this.getPanel(panel))){
33908             this.tabs.hideTab(panel.getEl().id);
33909         }
33910     },
33911
33912     /**
33913      * Unhides the tab for a previously hidden panel.
33914      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33915      */
33916     unhidePanel : function(panel){
33917         if(this.tabs && (panel = this.getPanel(panel))){
33918             this.tabs.unhideTab(panel.getEl().id);
33919         }
33920     },
33921
33922     clearPanels : function(){
33923         while(this.panels.getCount() > 0){
33924              this.remove(this.panels.first());
33925         }
33926     },
33927
33928     /**
33929      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33930      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33931      * @param {Boolean} preservePanel Overrides the config preservePanel option
33932      * @return {Roo.ContentPanel} The panel that was removed
33933      */
33934     remove : function(panel, preservePanel){
33935         panel = this.getPanel(panel);
33936         if(!panel){
33937             return null;
33938         }
33939         var e = {};
33940         this.fireEvent("beforeremove", this, panel, e);
33941         if(e.cancel === true){
33942             return null;
33943         }
33944         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33945         var panelId = panel.getId();
33946         this.panels.removeKey(panelId);
33947         if(preservePanel){
33948             document.body.appendChild(panel.getEl().dom);
33949         }
33950         if(this.tabs){
33951             this.tabs.removeTab(panel.getEl().id);
33952         }else if (!preservePanel){
33953             this.bodyEl.dom.removeChild(panel.getEl().dom);
33954         }
33955         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33956             var p = this.panels.first();
33957             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33958             tempEl.appendChild(p.getEl().dom);
33959             this.bodyEl.update("");
33960             this.bodyEl.dom.appendChild(p.getEl().dom);
33961             tempEl = null;
33962             this.updateTitle(p.getTitle());
33963             this.tabs = null;
33964             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33965             this.setActivePanel(p);
33966         }
33967         panel.setRegion(null);
33968         if(this.activePanel == panel){
33969             this.activePanel = null;
33970         }
33971         if(this.config.autoDestroy !== false && preservePanel !== true){
33972             try{panel.destroy();}catch(e){}
33973         }
33974         this.fireEvent("panelremoved", this, panel);
33975         return panel;
33976     },
33977
33978     /**
33979      * Returns the TabPanel component used by this region
33980      * @return {Roo.TabPanel}
33981      */
33982     getTabs : function(){
33983         return this.tabs;
33984     },
33985
33986     createTool : function(parentEl, className){
33987         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33988             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33989         btn.addClassOnOver("x-layout-tools-button-over");
33990         return btn;
33991     }
33992 });/*
33993  * Based on:
33994  * Ext JS Library 1.1.1
33995  * Copyright(c) 2006-2007, Ext JS, LLC.
33996  *
33997  * Originally Released Under LGPL - original licence link has changed is not relivant.
33998  *
33999  * Fork - LGPL
34000  * <script type="text/javascript">
34001  */
34002  
34003
34004
34005 /**
34006  * @class Roo.SplitLayoutRegion
34007  * @extends Roo.LayoutRegion
34008  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34009  */
34010 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34011     this.cursor = cursor;
34012     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34013 };
34014
34015 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34016     splitTip : "Drag to resize.",
34017     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34018     useSplitTips : false,
34019
34020     applyConfig : function(config){
34021         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34022         if(config.split){
34023             if(!this.split){
34024                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34025                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34026                 /** The SplitBar for this region 
34027                 * @type Roo.SplitBar */
34028                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34029                 this.split.on("moved", this.onSplitMove, this);
34030                 this.split.useShim = config.useShim === true;
34031                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34032                 if(this.useSplitTips){
34033                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34034                 }
34035                 if(config.collapsible){
34036                     this.split.el.on("dblclick", this.collapse,  this);
34037                 }
34038             }
34039             if(typeof config.minSize != "undefined"){
34040                 this.split.minSize = config.minSize;
34041             }
34042             if(typeof config.maxSize != "undefined"){
34043                 this.split.maxSize = config.maxSize;
34044             }
34045             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34046                 this.hideSplitter();
34047             }
34048         }
34049     },
34050
34051     getHMaxSize : function(){
34052          var cmax = this.config.maxSize || 10000;
34053          var center = this.mgr.getRegion("center");
34054          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34055     },
34056
34057     getVMaxSize : function(){
34058          var cmax = this.config.maxSize || 10000;
34059          var center = this.mgr.getRegion("center");
34060          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34061     },
34062
34063     onSplitMove : function(split, newSize){
34064         this.fireEvent("resized", this, newSize);
34065     },
34066     
34067     /** 
34068      * Returns the {@link Roo.SplitBar} for this region.
34069      * @return {Roo.SplitBar}
34070      */
34071     getSplitBar : function(){
34072         return this.split;
34073     },
34074     
34075     hide : function(){
34076         this.hideSplitter();
34077         Roo.SplitLayoutRegion.superclass.hide.call(this);
34078     },
34079
34080     hideSplitter : function(){
34081         if(this.split){
34082             this.split.el.setLocation(-2000,-2000);
34083             this.split.el.hide();
34084         }
34085     },
34086
34087     show : function(){
34088         if(this.split){
34089             this.split.el.show();
34090         }
34091         Roo.SplitLayoutRegion.superclass.show.call(this);
34092     },
34093     
34094     beforeSlide: function(){
34095         if(Roo.isGecko){// firefox overflow auto bug workaround
34096             this.bodyEl.clip();
34097             if(this.tabs) this.tabs.bodyEl.clip();
34098             if(this.activePanel){
34099                 this.activePanel.getEl().clip();
34100                 
34101                 if(this.activePanel.beforeSlide){
34102                     this.activePanel.beforeSlide();
34103                 }
34104             }
34105         }
34106     },
34107     
34108     afterSlide : function(){
34109         if(Roo.isGecko){// firefox overflow auto bug workaround
34110             this.bodyEl.unclip();
34111             if(this.tabs) this.tabs.bodyEl.unclip();
34112             if(this.activePanel){
34113                 this.activePanel.getEl().unclip();
34114                 if(this.activePanel.afterSlide){
34115                     this.activePanel.afterSlide();
34116                 }
34117             }
34118         }
34119     },
34120
34121     initAutoHide : function(){
34122         if(this.autoHide !== false){
34123             if(!this.autoHideHd){
34124                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34125                 this.autoHideHd = {
34126                     "mouseout": function(e){
34127                         if(!e.within(this.el, true)){
34128                             st.delay(500);
34129                         }
34130                     },
34131                     "mouseover" : function(e){
34132                         st.cancel();
34133                     },
34134                     scope : this
34135                 };
34136             }
34137             this.el.on(this.autoHideHd);
34138         }
34139     },
34140
34141     clearAutoHide : function(){
34142         if(this.autoHide !== false){
34143             this.el.un("mouseout", this.autoHideHd.mouseout);
34144             this.el.un("mouseover", this.autoHideHd.mouseover);
34145         }
34146     },
34147
34148     clearMonitor : function(){
34149         Roo.get(document).un("click", this.slideInIf, this);
34150     },
34151
34152     // these names are backwards but not changed for compat
34153     slideOut : function(){
34154         if(this.isSlid || this.el.hasActiveFx()){
34155             return;
34156         }
34157         this.isSlid = true;
34158         if(this.collapseBtn){
34159             this.collapseBtn.hide();
34160         }
34161         this.closeBtnState = this.closeBtn.getStyle('display');
34162         this.closeBtn.hide();
34163         if(this.stickBtn){
34164             this.stickBtn.show();
34165         }
34166         this.el.show();
34167         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34168         this.beforeSlide();
34169         this.el.setStyle("z-index", 10001);
34170         this.el.slideIn(this.getSlideAnchor(), {
34171             callback: function(){
34172                 this.afterSlide();
34173                 this.initAutoHide();
34174                 Roo.get(document).on("click", this.slideInIf, this);
34175                 this.fireEvent("slideshow", this);
34176             },
34177             scope: this,
34178             block: true
34179         });
34180     },
34181
34182     afterSlideIn : function(){
34183         this.clearAutoHide();
34184         this.isSlid = false;
34185         this.clearMonitor();
34186         this.el.setStyle("z-index", "");
34187         if(this.collapseBtn){
34188             this.collapseBtn.show();
34189         }
34190         this.closeBtn.setStyle('display', this.closeBtnState);
34191         if(this.stickBtn){
34192             this.stickBtn.hide();
34193         }
34194         this.fireEvent("slidehide", this);
34195     },
34196
34197     slideIn : function(cb){
34198         if(!this.isSlid || this.el.hasActiveFx()){
34199             Roo.callback(cb);
34200             return;
34201         }
34202         this.isSlid = false;
34203         this.beforeSlide();
34204         this.el.slideOut(this.getSlideAnchor(), {
34205             callback: function(){
34206                 this.el.setLeftTop(-10000, -10000);
34207                 this.afterSlide();
34208                 this.afterSlideIn();
34209                 Roo.callback(cb);
34210             },
34211             scope: this,
34212             block: true
34213         });
34214     },
34215     
34216     slideInIf : function(e){
34217         if(!e.within(this.el)){
34218             this.slideIn();
34219         }
34220     },
34221
34222     animateCollapse : function(){
34223         this.beforeSlide();
34224         this.el.setStyle("z-index", 20000);
34225         var anchor = this.getSlideAnchor();
34226         this.el.slideOut(anchor, {
34227             callback : function(){
34228                 this.el.setStyle("z-index", "");
34229                 this.collapsedEl.slideIn(anchor, {duration:.3});
34230                 this.afterSlide();
34231                 this.el.setLocation(-10000,-10000);
34232                 this.el.hide();
34233                 this.fireEvent("collapsed", this);
34234             },
34235             scope: this,
34236             block: true
34237         });
34238     },
34239
34240     animateExpand : function(){
34241         this.beforeSlide();
34242         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34243         this.el.setStyle("z-index", 20000);
34244         this.collapsedEl.hide({
34245             duration:.1
34246         });
34247         this.el.slideIn(this.getSlideAnchor(), {
34248             callback : function(){
34249                 this.el.setStyle("z-index", "");
34250                 this.afterSlide();
34251                 if(this.split){
34252                     this.split.el.show();
34253                 }
34254                 this.fireEvent("invalidated", this);
34255                 this.fireEvent("expanded", this);
34256             },
34257             scope: this,
34258             block: true
34259         });
34260     },
34261
34262     anchors : {
34263         "west" : "left",
34264         "east" : "right",
34265         "north" : "top",
34266         "south" : "bottom"
34267     },
34268
34269     sanchors : {
34270         "west" : "l",
34271         "east" : "r",
34272         "north" : "t",
34273         "south" : "b"
34274     },
34275
34276     canchors : {
34277         "west" : "tl-tr",
34278         "east" : "tr-tl",
34279         "north" : "tl-bl",
34280         "south" : "bl-tl"
34281     },
34282
34283     getAnchor : function(){
34284         return this.anchors[this.position];
34285     },
34286
34287     getCollapseAnchor : function(){
34288         return this.canchors[this.position];
34289     },
34290
34291     getSlideAnchor : function(){
34292         return this.sanchors[this.position];
34293     },
34294
34295     getAlignAdj : function(){
34296         var cm = this.cmargins;
34297         switch(this.position){
34298             case "west":
34299                 return [0, 0];
34300             break;
34301             case "east":
34302                 return [0, 0];
34303             break;
34304             case "north":
34305                 return [0, 0];
34306             break;
34307             case "south":
34308                 return [0, 0];
34309             break;
34310         }
34311     },
34312
34313     getExpandAdj : function(){
34314         var c = this.collapsedEl, cm = this.cmargins;
34315         switch(this.position){
34316             case "west":
34317                 return [-(cm.right+c.getWidth()+cm.left), 0];
34318             break;
34319             case "east":
34320                 return [cm.right+c.getWidth()+cm.left, 0];
34321             break;
34322             case "north":
34323                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34324             break;
34325             case "south":
34326                 return [0, cm.top+cm.bottom+c.getHeight()];
34327             break;
34328         }
34329     }
34330 });/*
34331  * Based on:
34332  * Ext JS Library 1.1.1
34333  * Copyright(c) 2006-2007, Ext JS, LLC.
34334  *
34335  * Originally Released Under LGPL - original licence link has changed is not relivant.
34336  *
34337  * Fork - LGPL
34338  * <script type="text/javascript">
34339  */
34340 /*
34341  * These classes are private internal classes
34342  */
34343 Roo.CenterLayoutRegion = function(mgr, config){
34344     Roo.LayoutRegion.call(this, mgr, config, "center");
34345     this.visible = true;
34346     this.minWidth = config.minWidth || 20;
34347     this.minHeight = config.minHeight || 20;
34348 };
34349
34350 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34351     hide : function(){
34352         // center panel can't be hidden
34353     },
34354     
34355     show : function(){
34356         // center panel can't be hidden
34357     },
34358     
34359     getMinWidth: function(){
34360         return this.minWidth;
34361     },
34362     
34363     getMinHeight: function(){
34364         return this.minHeight;
34365     }
34366 });
34367
34368
34369 Roo.NorthLayoutRegion = function(mgr, config){
34370     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34371     if(this.split){
34372         this.split.placement = Roo.SplitBar.TOP;
34373         this.split.orientation = Roo.SplitBar.VERTICAL;
34374         this.split.el.addClass("x-layout-split-v");
34375     }
34376     var size = config.initialSize || config.height;
34377     if(typeof size != "undefined"){
34378         this.el.setHeight(size);
34379     }
34380 };
34381 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34382     orientation: Roo.SplitBar.VERTICAL,
34383     getBox : function(){
34384         if(this.collapsed){
34385             return this.collapsedEl.getBox();
34386         }
34387         var box = this.el.getBox();
34388         if(this.split){
34389             box.height += this.split.el.getHeight();
34390         }
34391         return box;
34392     },
34393     
34394     updateBox : function(box){
34395         if(this.split && !this.collapsed){
34396             box.height -= this.split.el.getHeight();
34397             this.split.el.setLeft(box.x);
34398             this.split.el.setTop(box.y+box.height);
34399             this.split.el.setWidth(box.width);
34400         }
34401         if(this.collapsed){
34402             this.updateBody(box.width, null);
34403         }
34404         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34405     }
34406 });
34407
34408 Roo.SouthLayoutRegion = function(mgr, config){
34409     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34410     if(this.split){
34411         this.split.placement = Roo.SplitBar.BOTTOM;
34412         this.split.orientation = Roo.SplitBar.VERTICAL;
34413         this.split.el.addClass("x-layout-split-v");
34414     }
34415     var size = config.initialSize || config.height;
34416     if(typeof size != "undefined"){
34417         this.el.setHeight(size);
34418     }
34419 };
34420 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34421     orientation: Roo.SplitBar.VERTICAL,
34422     getBox : function(){
34423         if(this.collapsed){
34424             return this.collapsedEl.getBox();
34425         }
34426         var box = this.el.getBox();
34427         if(this.split){
34428             var sh = this.split.el.getHeight();
34429             box.height += sh;
34430             box.y -= sh;
34431         }
34432         return box;
34433     },
34434     
34435     updateBox : function(box){
34436         if(this.split && !this.collapsed){
34437             var sh = this.split.el.getHeight();
34438             box.height -= sh;
34439             box.y += sh;
34440             this.split.el.setLeft(box.x);
34441             this.split.el.setTop(box.y-sh);
34442             this.split.el.setWidth(box.width);
34443         }
34444         if(this.collapsed){
34445             this.updateBody(box.width, null);
34446         }
34447         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34448     }
34449 });
34450
34451 Roo.EastLayoutRegion = function(mgr, config){
34452     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34453     if(this.split){
34454         this.split.placement = Roo.SplitBar.RIGHT;
34455         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34456         this.split.el.addClass("x-layout-split-h");
34457     }
34458     var size = config.initialSize || config.width;
34459     if(typeof size != "undefined"){
34460         this.el.setWidth(size);
34461     }
34462 };
34463 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34464     orientation: Roo.SplitBar.HORIZONTAL,
34465     getBox : function(){
34466         if(this.collapsed){
34467             return this.collapsedEl.getBox();
34468         }
34469         var box = this.el.getBox();
34470         if(this.split){
34471             var sw = this.split.el.getWidth();
34472             box.width += sw;
34473             box.x -= sw;
34474         }
34475         return box;
34476     },
34477
34478     updateBox : function(box){
34479         if(this.split && !this.collapsed){
34480             var sw = this.split.el.getWidth();
34481             box.width -= sw;
34482             this.split.el.setLeft(box.x);
34483             this.split.el.setTop(box.y);
34484             this.split.el.setHeight(box.height);
34485             box.x += sw;
34486         }
34487         if(this.collapsed){
34488             this.updateBody(null, box.height);
34489         }
34490         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34491     }
34492 });
34493
34494 Roo.WestLayoutRegion = function(mgr, config){
34495     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34496     if(this.split){
34497         this.split.placement = Roo.SplitBar.LEFT;
34498         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34499         this.split.el.addClass("x-layout-split-h");
34500     }
34501     var size = config.initialSize || config.width;
34502     if(typeof size != "undefined"){
34503         this.el.setWidth(size);
34504     }
34505 };
34506 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34507     orientation: Roo.SplitBar.HORIZONTAL,
34508     getBox : function(){
34509         if(this.collapsed){
34510             return this.collapsedEl.getBox();
34511         }
34512         var box = this.el.getBox();
34513         if(this.split){
34514             box.width += this.split.el.getWidth();
34515         }
34516         return box;
34517     },
34518     
34519     updateBox : function(box){
34520         if(this.split && !this.collapsed){
34521             var sw = this.split.el.getWidth();
34522             box.width -= sw;
34523             this.split.el.setLeft(box.x+box.width);
34524             this.split.el.setTop(box.y);
34525             this.split.el.setHeight(box.height);
34526         }
34527         if(this.collapsed){
34528             this.updateBody(null, box.height);
34529         }
34530         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34531     }
34532 });
34533 /*
34534  * Based on:
34535  * Ext JS Library 1.1.1
34536  * Copyright(c) 2006-2007, Ext JS, LLC.
34537  *
34538  * Originally Released Under LGPL - original licence link has changed is not relivant.
34539  *
34540  * Fork - LGPL
34541  * <script type="text/javascript">
34542  */
34543  
34544  
34545 /*
34546  * Private internal class for reading and applying state
34547  */
34548 Roo.LayoutStateManager = function(layout){
34549      // default empty state
34550      this.state = {
34551         north: {},
34552         south: {},
34553         east: {},
34554         west: {}       
34555     };
34556 };
34557
34558 Roo.LayoutStateManager.prototype = {
34559     init : function(layout, provider){
34560         this.provider = provider;
34561         var state = provider.get(layout.id+"-layout-state");
34562         if(state){
34563             var wasUpdating = layout.isUpdating();
34564             if(!wasUpdating){
34565                 layout.beginUpdate();
34566             }
34567             for(var key in state){
34568                 if(typeof state[key] != "function"){
34569                     var rstate = state[key];
34570                     var r = layout.getRegion(key);
34571                     if(r && rstate){
34572                         if(rstate.size){
34573                             r.resizeTo(rstate.size);
34574                         }
34575                         if(rstate.collapsed == true){
34576                             r.collapse(true);
34577                         }else{
34578                             r.expand(null, true);
34579                         }
34580                     }
34581                 }
34582             }
34583             if(!wasUpdating){
34584                 layout.endUpdate();
34585             }
34586             this.state = state; 
34587         }
34588         this.layout = layout;
34589         layout.on("regionresized", this.onRegionResized, this);
34590         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34591         layout.on("regionexpanded", this.onRegionExpanded, this);
34592     },
34593     
34594     storeState : function(){
34595         this.provider.set(this.layout.id+"-layout-state", this.state);
34596     },
34597     
34598     onRegionResized : function(region, newSize){
34599         this.state[region.getPosition()].size = newSize;
34600         this.storeState();
34601     },
34602     
34603     onRegionCollapsed : function(region){
34604         this.state[region.getPosition()].collapsed = true;
34605         this.storeState();
34606     },
34607     
34608     onRegionExpanded : function(region){
34609         this.state[region.getPosition()].collapsed = false;
34610         this.storeState();
34611     }
34612 };/*
34613  * Based on:
34614  * Ext JS Library 1.1.1
34615  * Copyright(c) 2006-2007, Ext JS, LLC.
34616  *
34617  * Originally Released Under LGPL - original licence link has changed is not relivant.
34618  *
34619  * Fork - LGPL
34620  * <script type="text/javascript">
34621  */
34622 /**
34623  * @class Roo.ContentPanel
34624  * @extends Roo.util.Observable
34625  * A basic ContentPanel element.
34626  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34627  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34628  * @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
34629  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34630  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34631  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34632  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34633  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34634  * @cfg {String} title          The title for this panel
34635  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34636  * @cfg {String} url            Calls {@link #setUrl} with this value
34637  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34638  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34639  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34640  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34641
34642  * @constructor
34643  * Create a new ContentPanel.
34644  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34645  * @param {String/Object} config A string to set only the title or a config object
34646  * @param {String} content (optional) Set the HTML content for this panel
34647  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34648  */
34649 Roo.ContentPanel = function(el, config, content){
34650     
34651      
34652     /*
34653     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34654         config = el;
34655         el = Roo.id();
34656     }
34657     if (config && config.parentLayout) { 
34658         el = config.parentLayout.el.createChild(); 
34659     }
34660     */
34661     if(el.autoCreate){ // xtype is available if this is called from factory
34662         config = el;
34663         el = Roo.id();
34664     }
34665     this.el = Roo.get(el);
34666     if(!this.el && config && config.autoCreate){
34667         if(typeof config.autoCreate == "object"){
34668             if(!config.autoCreate.id){
34669                 config.autoCreate.id = config.id||el;
34670             }
34671             this.el = Roo.DomHelper.append(document.body,
34672                         config.autoCreate, true);
34673         }else{
34674             this.el = Roo.DomHelper.append(document.body,
34675                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
34676         }
34677     }
34678     this.closable = false;
34679     this.loaded = false;
34680     this.active = false;
34681     if(typeof config == "string"){
34682         this.title = config;
34683     }else{
34684         Roo.apply(this, config);
34685     }
34686     
34687     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
34688         this.wrapEl = this.el.wrap();
34689         this.toolbar.container = this.el.insertSibling(false, 'before');
34690         this.toolbar = new Roo.Toolbar(this.toolbar);
34691     }
34692     
34693     // xtype created footer. - not sure if will work as we normally have to render first..
34694     if (this.footer && !this.footer.el && this.footer.xtype) {
34695         if (!this.wrapEl) {
34696             this.wrapEl = this.el.wrap();
34697         }
34698     
34699         this.footer.container = this.wrapEl.createChild();
34700          
34701         this.footer = Roo.factory(this.footer, Roo);
34702         
34703     }
34704     
34705     if(this.resizeEl){
34706         this.resizeEl = Roo.get(this.resizeEl, true);
34707     }else{
34708         this.resizeEl = this.el;
34709     }
34710     // handle view.xtype
34711     
34712  
34713     
34714     
34715     this.addEvents({
34716         /**
34717          * @event activate
34718          * Fires when this panel is activated. 
34719          * @param {Roo.ContentPanel} this
34720          */
34721         "activate" : true,
34722         /**
34723          * @event deactivate
34724          * Fires when this panel is activated. 
34725          * @param {Roo.ContentPanel} this
34726          */
34727         "deactivate" : true,
34728
34729         /**
34730          * @event resize
34731          * Fires when this panel is resized if fitToFrame is true.
34732          * @param {Roo.ContentPanel} this
34733          * @param {Number} width The width after any component adjustments
34734          * @param {Number} height The height after any component adjustments
34735          */
34736         "resize" : true,
34737         
34738          /**
34739          * @event render
34740          * Fires when this tab is created
34741          * @param {Roo.ContentPanel} this
34742          */
34743         "render" : true
34744         
34745         
34746         
34747     });
34748     
34749
34750     
34751     
34752     if(this.autoScroll){
34753         this.resizeEl.setStyle("overflow", "auto");
34754     } else {
34755         // fix randome scrolling
34756         this.el.on('scroll', function() {
34757             Roo.log('fix random scolling');
34758             this.scrollTo('top',0); 
34759         });
34760     }
34761     content = content || this.content;
34762     if(content){
34763         this.setContent(content);
34764     }
34765     if(config && config.url){
34766         this.setUrl(this.url, this.params, this.loadOnce);
34767     }
34768     
34769     
34770     
34771     Roo.ContentPanel.superclass.constructor.call(this);
34772     
34773     if (this.view && typeof(this.view.xtype) != 'undefined') {
34774         this.view.el = this.el.appendChild(document.createElement("div"));
34775         this.view = Roo.factory(this.view); 
34776         this.view.render  &&  this.view.render(false, '');  
34777     }
34778     
34779     
34780     this.fireEvent('render', this);
34781 };
34782
34783 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
34784     tabTip:'',
34785     setRegion : function(region){
34786         this.region = region;
34787         if(region){
34788            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34789         }else{
34790            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34791         } 
34792     },
34793     
34794     /**
34795      * Returns the toolbar for this Panel if one was configured. 
34796      * @return {Roo.Toolbar} 
34797      */
34798     getToolbar : function(){
34799         return this.toolbar;
34800     },
34801     
34802     setActiveState : function(active){
34803         this.active = active;
34804         if(!active){
34805             this.fireEvent("deactivate", this);
34806         }else{
34807             this.fireEvent("activate", this);
34808         }
34809     },
34810     /**
34811      * Updates this panel's element
34812      * @param {String} content The new content
34813      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34814     */
34815     setContent : function(content, loadScripts){
34816         this.el.update(content, loadScripts);
34817     },
34818
34819     ignoreResize : function(w, h){
34820         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34821             return true;
34822         }else{
34823             this.lastSize = {width: w, height: h};
34824             return false;
34825         }
34826     },
34827     /**
34828      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34829      * @return {Roo.UpdateManager} The UpdateManager
34830      */
34831     getUpdateManager : function(){
34832         return this.el.getUpdateManager();
34833     },
34834      /**
34835      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34836      * @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:
34837 <pre><code>
34838 panel.load({
34839     url: "your-url.php",
34840     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34841     callback: yourFunction,
34842     scope: yourObject, //(optional scope)
34843     discardUrl: false,
34844     nocache: false,
34845     text: "Loading...",
34846     timeout: 30,
34847     scripts: false
34848 });
34849 </code></pre>
34850      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34851      * 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.
34852      * @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}
34853      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34854      * @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.
34855      * @return {Roo.ContentPanel} this
34856      */
34857     load : function(){
34858         var um = this.el.getUpdateManager();
34859         um.update.apply(um, arguments);
34860         return this;
34861     },
34862
34863
34864     /**
34865      * 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.
34866      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34867      * @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)
34868      * @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)
34869      * @return {Roo.UpdateManager} The UpdateManager
34870      */
34871     setUrl : function(url, params, loadOnce){
34872         if(this.refreshDelegate){
34873             this.removeListener("activate", this.refreshDelegate);
34874         }
34875         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34876         this.on("activate", this.refreshDelegate);
34877         return this.el.getUpdateManager();
34878     },
34879     
34880     _handleRefresh : function(url, params, loadOnce){
34881         if(!loadOnce || !this.loaded){
34882             var updater = this.el.getUpdateManager();
34883             updater.update(url, params, this._setLoaded.createDelegate(this));
34884         }
34885     },
34886     
34887     _setLoaded : function(){
34888         this.loaded = true;
34889     }, 
34890     
34891     /**
34892      * Returns this panel's id
34893      * @return {String} 
34894      */
34895     getId : function(){
34896         return this.el.id;
34897     },
34898     
34899     /** 
34900      * Returns this panel's element - used by regiosn to add.
34901      * @return {Roo.Element} 
34902      */
34903     getEl : function(){
34904         return this.wrapEl || this.el;
34905     },
34906     
34907     adjustForComponents : function(width, height)
34908     {
34909         //Roo.log('adjustForComponents ');
34910         if(this.resizeEl != this.el){
34911             width -= this.el.getFrameWidth('lr');
34912             height -= this.el.getFrameWidth('tb');
34913         }
34914         if(this.toolbar){
34915             var te = this.toolbar.getEl();
34916             height -= te.getHeight();
34917             te.setWidth(width);
34918         }
34919         if(this.footer){
34920             var te = this.footer.getEl();
34921             Roo.log("footer:" + te.getHeight());
34922             
34923             height -= te.getHeight();
34924             te.setWidth(width);
34925         }
34926         
34927         
34928         if(this.adjustments){
34929             width += this.adjustments[0];
34930             height += this.adjustments[1];
34931         }
34932         return {"width": width, "height": height};
34933     },
34934     
34935     setSize : function(width, height){
34936         if(this.fitToFrame && !this.ignoreResize(width, height)){
34937             if(this.fitContainer && this.resizeEl != this.el){
34938                 this.el.setSize(width, height);
34939             }
34940             var size = this.adjustForComponents(width, height);
34941             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34942             this.fireEvent('resize', this, size.width, size.height);
34943         }
34944     },
34945     
34946     /**
34947      * Returns this panel's title
34948      * @return {String} 
34949      */
34950     getTitle : function(){
34951         return this.title;
34952     },
34953     
34954     /**
34955      * Set this panel's title
34956      * @param {String} title
34957      */
34958     setTitle : function(title){
34959         this.title = title;
34960         if(this.region){
34961             this.region.updatePanelTitle(this, title);
34962         }
34963     },
34964     
34965     /**
34966      * Returns true is this panel was configured to be closable
34967      * @return {Boolean} 
34968      */
34969     isClosable : function(){
34970         return this.closable;
34971     },
34972     
34973     beforeSlide : function(){
34974         this.el.clip();
34975         this.resizeEl.clip();
34976     },
34977     
34978     afterSlide : function(){
34979         this.el.unclip();
34980         this.resizeEl.unclip();
34981     },
34982     
34983     /**
34984      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34985      *   Will fail silently if the {@link #setUrl} method has not been called.
34986      *   This does not activate the panel, just updates its content.
34987      */
34988     refresh : function(){
34989         if(this.refreshDelegate){
34990            this.loaded = false;
34991            this.refreshDelegate();
34992         }
34993     },
34994     
34995     /**
34996      * Destroys this panel
34997      */
34998     destroy : function(){
34999         this.el.removeAllListeners();
35000         var tempEl = document.createElement("span");
35001         tempEl.appendChild(this.el.dom);
35002         tempEl.innerHTML = "";
35003         this.el.remove();
35004         this.el = null;
35005     },
35006     
35007     /**
35008      * form - if the content panel contains a form - this is a reference to it.
35009      * @type {Roo.form.Form}
35010      */
35011     form : false,
35012     /**
35013      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35014      *    This contains a reference to it.
35015      * @type {Roo.View}
35016      */
35017     view : false,
35018     
35019       /**
35020      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35021      * <pre><code>
35022
35023 layout.addxtype({
35024        xtype : 'Form',
35025        items: [ .... ]
35026    }
35027 );
35028
35029 </code></pre>
35030      * @param {Object} cfg Xtype definition of item to add.
35031      */
35032     
35033     addxtype : function(cfg) {
35034         // add form..
35035         if (cfg.xtype.match(/^Form$/)) {
35036             
35037             var el;
35038             //if (this.footer) {
35039             //    el = this.footer.container.insertSibling(false, 'before');
35040             //} else {
35041                 el = this.el.createChild();
35042             //}
35043
35044             this.form = new  Roo.form.Form(cfg);
35045             
35046             
35047             if ( this.form.allItems.length) this.form.render(el.dom);
35048             return this.form;
35049         }
35050         // should only have one of theses..
35051         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35052             // views.. should not be just added - used named prop 'view''
35053             
35054             cfg.el = this.el.appendChild(document.createElement("div"));
35055             // factory?
35056             
35057             var ret = new Roo.factory(cfg);
35058              
35059              ret.render && ret.render(false, ''); // render blank..
35060             this.view = ret;
35061             return ret;
35062         }
35063         return false;
35064     }
35065 });
35066
35067 /**
35068  * @class Roo.GridPanel
35069  * @extends Roo.ContentPanel
35070  * @constructor
35071  * Create a new GridPanel.
35072  * @param {Roo.grid.Grid} grid The grid for this panel
35073  * @param {String/Object} config A string to set only the panel's title, or a config object
35074  */
35075 Roo.GridPanel = function(grid, config){
35076     
35077   
35078     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35079         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35080         
35081     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35082     
35083     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35084     
35085     if(this.toolbar){
35086         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35087     }
35088     // xtype created footer. - not sure if will work as we normally have to render first..
35089     if (this.footer && !this.footer.el && this.footer.xtype) {
35090         
35091         this.footer.container = this.grid.getView().getFooterPanel(true);
35092         this.footer.dataSource = this.grid.dataSource;
35093         this.footer = Roo.factory(this.footer, Roo);
35094         
35095     }
35096     
35097     grid.monitorWindowResize = false; // turn off autosizing
35098     grid.autoHeight = false;
35099     grid.autoWidth = false;
35100     this.grid = grid;
35101     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35102 };
35103
35104 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35105     getId : function(){
35106         return this.grid.id;
35107     },
35108     
35109     /**
35110      * Returns the grid for this panel
35111      * @return {Roo.grid.Grid} 
35112      */
35113     getGrid : function(){
35114         return this.grid;    
35115     },
35116     
35117     setSize : function(width, height){
35118         if(!this.ignoreResize(width, height)){
35119             var grid = this.grid;
35120             var size = this.adjustForComponents(width, height);
35121             grid.getGridEl().setSize(size.width, size.height);
35122             grid.autoSize();
35123         }
35124     },
35125     
35126     beforeSlide : function(){
35127         this.grid.getView().scroller.clip();
35128     },
35129     
35130     afterSlide : function(){
35131         this.grid.getView().scroller.unclip();
35132     },
35133     
35134     destroy : function(){
35135         this.grid.destroy();
35136         delete this.grid;
35137         Roo.GridPanel.superclass.destroy.call(this); 
35138     }
35139 });
35140
35141
35142 /**
35143  * @class Roo.NestedLayoutPanel
35144  * @extends Roo.ContentPanel
35145  * @constructor
35146  * Create a new NestedLayoutPanel.
35147  * 
35148  * 
35149  * @param {Roo.BorderLayout} layout The layout for this panel
35150  * @param {String/Object} config A string to set only the title or a config object
35151  */
35152 Roo.NestedLayoutPanel = function(layout, config)
35153 {
35154     // construct with only one argument..
35155     /* FIXME - implement nicer consturctors
35156     if (layout.layout) {
35157         config = layout;
35158         layout = config.layout;
35159         delete config.layout;
35160     }
35161     if (layout.xtype && !layout.getEl) {
35162         // then layout needs constructing..
35163         layout = Roo.factory(layout, Roo);
35164     }
35165     */
35166     
35167     
35168     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35169     
35170     layout.monitorWindowResize = false; // turn off autosizing
35171     this.layout = layout;
35172     this.layout.getEl().addClass("x-layout-nested-layout");
35173     
35174     
35175     
35176     
35177 };
35178
35179 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35180
35181     setSize : function(width, height){
35182         if(!this.ignoreResize(width, height)){
35183             var size = this.adjustForComponents(width, height);
35184             var el = this.layout.getEl();
35185             el.setSize(size.width, size.height);
35186             var touch = el.dom.offsetWidth;
35187             this.layout.layout();
35188             // ie requires a double layout on the first pass
35189             if(Roo.isIE && !this.initialized){
35190                 this.initialized = true;
35191                 this.layout.layout();
35192             }
35193         }
35194     },
35195     
35196     // activate all subpanels if not currently active..
35197     
35198     setActiveState : function(active){
35199         this.active = active;
35200         if(!active){
35201             this.fireEvent("deactivate", this);
35202             return;
35203         }
35204         
35205         this.fireEvent("activate", this);
35206         // not sure if this should happen before or after..
35207         if (!this.layout) {
35208             return; // should not happen..
35209         }
35210         var reg = false;
35211         for (var r in this.layout.regions) {
35212             reg = this.layout.getRegion(r);
35213             if (reg.getActivePanel()) {
35214                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35215                 reg.setActivePanel(reg.getActivePanel());
35216                 continue;
35217             }
35218             if (!reg.panels.length) {
35219                 continue;
35220             }
35221             reg.showPanel(reg.getPanel(0));
35222         }
35223         
35224         
35225         
35226         
35227     },
35228     
35229     /**
35230      * Returns the nested BorderLayout for this panel
35231      * @return {Roo.BorderLayout} 
35232      */
35233     getLayout : function(){
35234         return this.layout;
35235     },
35236     
35237      /**
35238      * Adds a xtype elements to the layout of the nested panel
35239      * <pre><code>
35240
35241 panel.addxtype({
35242        xtype : 'ContentPanel',
35243        region: 'west',
35244        items: [ .... ]
35245    }
35246 );
35247
35248 panel.addxtype({
35249         xtype : 'NestedLayoutPanel',
35250         region: 'west',
35251         layout: {
35252            center: { },
35253            west: { }   
35254         },
35255         items : [ ... list of content panels or nested layout panels.. ]
35256    }
35257 );
35258 </code></pre>
35259      * @param {Object} cfg Xtype definition of item to add.
35260      */
35261     addxtype : function(cfg) {
35262         return this.layout.addxtype(cfg);
35263     
35264     }
35265 });
35266
35267 Roo.ScrollPanel = function(el, config, content){
35268     config = config || {};
35269     config.fitToFrame = true;
35270     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35271     
35272     this.el.dom.style.overflow = "hidden";
35273     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35274     this.el.removeClass("x-layout-inactive-content");
35275     this.el.on("mousewheel", this.onWheel, this);
35276
35277     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35278     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35279     up.unselectable(); down.unselectable();
35280     up.on("click", this.scrollUp, this);
35281     down.on("click", this.scrollDown, this);
35282     up.addClassOnOver("x-scroller-btn-over");
35283     down.addClassOnOver("x-scroller-btn-over");
35284     up.addClassOnClick("x-scroller-btn-click");
35285     down.addClassOnClick("x-scroller-btn-click");
35286     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35287
35288     this.resizeEl = this.el;
35289     this.el = wrap; this.up = up; this.down = down;
35290 };
35291
35292 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35293     increment : 100,
35294     wheelIncrement : 5,
35295     scrollUp : function(){
35296         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35297     },
35298
35299     scrollDown : function(){
35300         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35301     },
35302
35303     afterScroll : function(){
35304         var el = this.resizeEl;
35305         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35306         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35307         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35308     },
35309
35310     setSize : function(){
35311         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35312         this.afterScroll();
35313     },
35314
35315     onWheel : function(e){
35316         var d = e.getWheelDelta();
35317         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35318         this.afterScroll();
35319         e.stopEvent();
35320     },
35321
35322     setContent : function(content, loadScripts){
35323         this.resizeEl.update(content, loadScripts);
35324     }
35325
35326 });
35327
35328
35329
35330
35331
35332
35333
35334
35335
35336 /**
35337  * @class Roo.TreePanel
35338  * @extends Roo.ContentPanel
35339  * @constructor
35340  * Create a new TreePanel. - defaults to fit/scoll contents.
35341  * @param {String/Object} config A string to set only the panel's title, or a config object
35342  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35343  */
35344 Roo.TreePanel = function(config){
35345     var el = config.el;
35346     var tree = config.tree;
35347     delete config.tree; 
35348     delete config.el; // hopefull!
35349     
35350     // wrapper for IE7 strict & safari scroll issue
35351     
35352     var treeEl = el.createChild();
35353     config.resizeEl = treeEl;
35354     
35355     
35356     
35357     Roo.TreePanel.superclass.constructor.call(this, el, config);
35358  
35359  
35360     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35361     //console.log(tree);
35362     this.on('activate', function()
35363     {
35364         if (this.tree.rendered) {
35365             return;
35366         }
35367         //console.log('render tree');
35368         this.tree.render();
35369     });
35370     // this should not be needed.. - it's actually the 'el' that resizes?
35371     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35372     
35373     //this.on('resize',  function (cp, w, h) {
35374     //        this.tree.innerCt.setWidth(w);
35375     //        this.tree.innerCt.setHeight(h);
35376     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35377     //});
35378
35379         
35380     
35381 };
35382
35383 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35384     fitToFrame : true,
35385     autoScroll : true
35386 });
35387
35388
35389
35390
35391
35392
35393
35394
35395
35396
35397
35398 /*
35399  * Based on:
35400  * Ext JS Library 1.1.1
35401  * Copyright(c) 2006-2007, Ext JS, LLC.
35402  *
35403  * Originally Released Under LGPL - original licence link has changed is not relivant.
35404  *
35405  * Fork - LGPL
35406  * <script type="text/javascript">
35407  */
35408  
35409
35410 /**
35411  * @class Roo.ReaderLayout
35412  * @extends Roo.BorderLayout
35413  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35414  * center region containing two nested regions (a top one for a list view and one for item preview below),
35415  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35416  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35417  * expedites the setup of the overall layout and regions for this common application style.
35418  * Example:
35419  <pre><code>
35420 var reader = new Roo.ReaderLayout();
35421 var CP = Roo.ContentPanel;  // shortcut for adding
35422
35423 reader.beginUpdate();
35424 reader.add("north", new CP("north", "North"));
35425 reader.add("west", new CP("west", {title: "West"}));
35426 reader.add("east", new CP("east", {title: "East"}));
35427
35428 reader.regions.listView.add(new CP("listView", "List"));
35429 reader.regions.preview.add(new CP("preview", "Preview"));
35430 reader.endUpdate();
35431 </code></pre>
35432 * @constructor
35433 * Create a new ReaderLayout
35434 * @param {Object} config Configuration options
35435 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35436 * document.body if omitted)
35437 */
35438 Roo.ReaderLayout = function(config, renderTo){
35439     var c = config || {size:{}};
35440     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35441         north: c.north !== false ? Roo.apply({
35442             split:false,
35443             initialSize: 32,
35444             titlebar: false
35445         }, c.north) : false,
35446         west: c.west !== false ? Roo.apply({
35447             split:true,
35448             initialSize: 200,
35449             minSize: 175,
35450             maxSize: 400,
35451             titlebar: true,
35452             collapsible: true,
35453             animate: true,
35454             margins:{left:5,right:0,bottom:5,top:5},
35455             cmargins:{left:5,right:5,bottom:5,top:5}
35456         }, c.west) : false,
35457         east: c.east !== false ? Roo.apply({
35458             split:true,
35459             initialSize: 200,
35460             minSize: 175,
35461             maxSize: 400,
35462             titlebar: true,
35463             collapsible: true,
35464             animate: true,
35465             margins:{left:0,right:5,bottom:5,top:5},
35466             cmargins:{left:5,right:5,bottom:5,top:5}
35467         }, c.east) : false,
35468         center: Roo.apply({
35469             tabPosition: 'top',
35470             autoScroll:false,
35471             closeOnTab: true,
35472             titlebar:false,
35473             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35474         }, c.center)
35475     });
35476
35477     this.el.addClass('x-reader');
35478
35479     this.beginUpdate();
35480
35481     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35482         south: c.preview !== false ? Roo.apply({
35483             split:true,
35484             initialSize: 200,
35485             minSize: 100,
35486             autoScroll:true,
35487             collapsible:true,
35488             titlebar: true,
35489             cmargins:{top:5,left:0, right:0, bottom:0}
35490         }, c.preview) : false,
35491         center: Roo.apply({
35492             autoScroll:false,
35493             titlebar:false,
35494             minHeight:200
35495         }, c.listView)
35496     });
35497     this.add('center', new Roo.NestedLayoutPanel(inner,
35498             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35499
35500     this.endUpdate();
35501
35502     this.regions.preview = inner.getRegion('south');
35503     this.regions.listView = inner.getRegion('center');
35504 };
35505
35506 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35507  * Based on:
35508  * Ext JS Library 1.1.1
35509  * Copyright(c) 2006-2007, Ext JS, LLC.
35510  *
35511  * Originally Released Under LGPL - original licence link has changed is not relivant.
35512  *
35513  * Fork - LGPL
35514  * <script type="text/javascript">
35515  */
35516  
35517 /**
35518  * @class Roo.grid.Grid
35519  * @extends Roo.util.Observable
35520  * This class represents the primary interface of a component based grid control.
35521  * <br><br>Usage:<pre><code>
35522  var grid = new Roo.grid.Grid("my-container-id", {
35523      ds: myDataStore,
35524      cm: myColModel,
35525      selModel: mySelectionModel,
35526      autoSizeColumns: true,
35527      monitorWindowResize: false,
35528      trackMouseOver: true
35529  });
35530  // set any options
35531  grid.render();
35532  * </code></pre>
35533  * <b>Common Problems:</b><br/>
35534  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35535  * element will correct this<br/>
35536  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35537  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35538  * are unpredictable.<br/>
35539  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35540  * grid to calculate dimensions/offsets.<br/>
35541   * @constructor
35542  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35543  * The container MUST have some type of size defined for the grid to fill. The container will be
35544  * automatically set to position relative if it isn't already.
35545  * @param {Object} config A config object that sets properties on this grid.
35546  */
35547 Roo.grid.Grid = function(container, config){
35548         // initialize the container
35549         this.container = Roo.get(container);
35550         this.container.update("");
35551         this.container.setStyle("overflow", "hidden");
35552     this.container.addClass('x-grid-container');
35553
35554     this.id = this.container.id;
35555
35556     Roo.apply(this, config);
35557     // check and correct shorthanded configs
35558     if(this.ds){
35559         this.dataSource = this.ds;
35560         delete this.ds;
35561     }
35562     if(this.cm){
35563         this.colModel = this.cm;
35564         delete this.cm;
35565     }
35566     if(this.sm){
35567         this.selModel = this.sm;
35568         delete this.sm;
35569     }
35570
35571     if (this.selModel) {
35572         this.selModel = Roo.factory(this.selModel, Roo.grid);
35573         this.sm = this.selModel;
35574         this.sm.xmodule = this.xmodule || false;
35575     }
35576     if (typeof(this.colModel.config) == 'undefined') {
35577         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35578         this.cm = this.colModel;
35579         this.cm.xmodule = this.xmodule || false;
35580     }
35581     if (this.dataSource) {
35582         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35583         this.ds = this.dataSource;
35584         this.ds.xmodule = this.xmodule || false;
35585          
35586     }
35587     
35588     
35589     
35590     if(this.width){
35591         this.container.setWidth(this.width);
35592     }
35593
35594     if(this.height){
35595         this.container.setHeight(this.height);
35596     }
35597     /** @private */
35598         this.addEvents({
35599         // raw events
35600         /**
35601          * @event click
35602          * The raw click event for the entire grid.
35603          * @param {Roo.EventObject} e
35604          */
35605         "click" : true,
35606         /**
35607          * @event dblclick
35608          * The raw dblclick event for the entire grid.
35609          * @param {Roo.EventObject} e
35610          */
35611         "dblclick" : true,
35612         /**
35613          * @event contextmenu
35614          * The raw contextmenu event for the entire grid.
35615          * @param {Roo.EventObject} e
35616          */
35617         "contextmenu" : true,
35618         /**
35619          * @event mousedown
35620          * The raw mousedown event for the entire grid.
35621          * @param {Roo.EventObject} e
35622          */
35623         "mousedown" : true,
35624         /**
35625          * @event mouseup
35626          * The raw mouseup event for the entire grid.
35627          * @param {Roo.EventObject} e
35628          */
35629         "mouseup" : true,
35630         /**
35631          * @event mouseover
35632          * The raw mouseover event for the entire grid.
35633          * @param {Roo.EventObject} e
35634          */
35635         "mouseover" : true,
35636         /**
35637          * @event mouseout
35638          * The raw mouseout event for the entire grid.
35639          * @param {Roo.EventObject} e
35640          */
35641         "mouseout" : true,
35642         /**
35643          * @event keypress
35644          * The raw keypress event for the entire grid.
35645          * @param {Roo.EventObject} e
35646          */
35647         "keypress" : true,
35648         /**
35649          * @event keydown
35650          * The raw keydown event for the entire grid.
35651          * @param {Roo.EventObject} e
35652          */
35653         "keydown" : true,
35654
35655         // custom events
35656
35657         /**
35658          * @event cellclick
35659          * Fires when a cell is clicked
35660          * @param {Grid} this
35661          * @param {Number} rowIndex
35662          * @param {Number} columnIndex
35663          * @param {Roo.EventObject} e
35664          */
35665         "cellclick" : true,
35666         /**
35667          * @event celldblclick
35668          * Fires when a cell is double clicked
35669          * @param {Grid} this
35670          * @param {Number} rowIndex
35671          * @param {Number} columnIndex
35672          * @param {Roo.EventObject} e
35673          */
35674         "celldblclick" : true,
35675         /**
35676          * @event rowclick
35677          * Fires when a row is clicked
35678          * @param {Grid} this
35679          * @param {Number} rowIndex
35680          * @param {Roo.EventObject} e
35681          */
35682         "rowclick" : true,
35683         /**
35684          * @event rowdblclick
35685          * Fires when a row is double clicked
35686          * @param {Grid} this
35687          * @param {Number} rowIndex
35688          * @param {Roo.EventObject} e
35689          */
35690         "rowdblclick" : true,
35691         /**
35692          * @event headerclick
35693          * Fires when a header is clicked
35694          * @param {Grid} this
35695          * @param {Number} columnIndex
35696          * @param {Roo.EventObject} e
35697          */
35698         "headerclick" : true,
35699         /**
35700          * @event headerdblclick
35701          * Fires when a header cell is double clicked
35702          * @param {Grid} this
35703          * @param {Number} columnIndex
35704          * @param {Roo.EventObject} e
35705          */
35706         "headerdblclick" : true,
35707         /**
35708          * @event rowcontextmenu
35709          * Fires when a row is right clicked
35710          * @param {Grid} this
35711          * @param {Number} rowIndex
35712          * @param {Roo.EventObject} e
35713          */
35714         "rowcontextmenu" : true,
35715         /**
35716          * @event cellcontextmenu
35717          * Fires when a cell is right clicked
35718          * @param {Grid} this
35719          * @param {Number} rowIndex
35720          * @param {Number} cellIndex
35721          * @param {Roo.EventObject} e
35722          */
35723          "cellcontextmenu" : true,
35724         /**
35725          * @event headercontextmenu
35726          * Fires when a header is right clicked
35727          * @param {Grid} this
35728          * @param {Number} columnIndex
35729          * @param {Roo.EventObject} e
35730          */
35731         "headercontextmenu" : true,
35732         /**
35733          * @event bodyscroll
35734          * Fires when the body element is scrolled
35735          * @param {Number} scrollLeft
35736          * @param {Number} scrollTop
35737          */
35738         "bodyscroll" : true,
35739         /**
35740          * @event columnresize
35741          * Fires when the user resizes a column
35742          * @param {Number} columnIndex
35743          * @param {Number} newSize
35744          */
35745         "columnresize" : true,
35746         /**
35747          * @event columnmove
35748          * Fires when the user moves a column
35749          * @param {Number} oldIndex
35750          * @param {Number} newIndex
35751          */
35752         "columnmove" : true,
35753         /**
35754          * @event startdrag
35755          * Fires when row(s) start being dragged
35756          * @param {Grid} this
35757          * @param {Roo.GridDD} dd The drag drop object
35758          * @param {event} e The raw browser event
35759          */
35760         "startdrag" : true,
35761         /**
35762          * @event enddrag
35763          * Fires when a drag operation is complete
35764          * @param {Grid} this
35765          * @param {Roo.GridDD} dd The drag drop object
35766          * @param {event} e The raw browser event
35767          */
35768         "enddrag" : true,
35769         /**
35770          * @event dragdrop
35771          * Fires when dragged row(s) are dropped on a valid DD target
35772          * @param {Grid} this
35773          * @param {Roo.GridDD} dd The drag drop object
35774          * @param {String} targetId The target drag drop object
35775          * @param {event} e The raw browser event
35776          */
35777         "dragdrop" : true,
35778         /**
35779          * @event dragover
35780          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
35781          * @param {Grid} this
35782          * @param {Roo.GridDD} dd The drag drop object
35783          * @param {String} targetId The target drag drop object
35784          * @param {event} e The raw browser event
35785          */
35786         "dragover" : true,
35787         /**
35788          * @event dragenter
35789          *  Fires when the dragged row(s) first cross another DD target while being dragged
35790          * @param {Grid} this
35791          * @param {Roo.GridDD} dd The drag drop object
35792          * @param {String} targetId The target drag drop object
35793          * @param {event} e The raw browser event
35794          */
35795         "dragenter" : true,
35796         /**
35797          * @event dragout
35798          * Fires when the dragged row(s) leave another DD target while being dragged
35799          * @param {Grid} this
35800          * @param {Roo.GridDD} dd The drag drop object
35801          * @param {String} targetId The target drag drop object
35802          * @param {event} e The raw browser event
35803          */
35804         "dragout" : true,
35805         /**
35806          * @event rowclass
35807          * Fires when a row is rendered, so you can change add a style to it.
35808          * @param {GridView} gridview   The grid view
35809          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35810          */
35811         'rowclass' : true,
35812
35813         /**
35814          * @event render
35815          * Fires when the grid is rendered
35816          * @param {Grid} grid
35817          */
35818         'render' : true
35819     });
35820
35821     Roo.grid.Grid.superclass.constructor.call(this);
35822 };
35823 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35824     
35825     /**
35826      * @cfg {String} ddGroup - drag drop group.
35827      */
35828
35829     /**
35830      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35831      */
35832     minColumnWidth : 25,
35833
35834     /**
35835      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35836      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35837      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35838      */
35839     autoSizeColumns : false,
35840
35841     /**
35842      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35843      */
35844     autoSizeHeaders : true,
35845
35846     /**
35847      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35848      */
35849     monitorWindowResize : true,
35850
35851     /**
35852      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35853      * rows measured to get a columns size. Default is 0 (all rows).
35854      */
35855     maxRowsToMeasure : 0,
35856
35857     /**
35858      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35859      */
35860     trackMouseOver : true,
35861
35862     /**
35863     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35864     */
35865     
35866     /**
35867     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35868     */
35869     enableDragDrop : false,
35870     
35871     /**
35872     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35873     */
35874     enableColumnMove : true,
35875     
35876     /**
35877     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35878     */
35879     enableColumnHide : true,
35880     
35881     /**
35882     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35883     */
35884     enableRowHeightSync : false,
35885     
35886     /**
35887     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35888     */
35889     stripeRows : true,
35890     
35891     /**
35892     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35893     */
35894     autoHeight : false,
35895
35896     /**
35897      * @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.
35898      */
35899     autoExpandColumn : false,
35900
35901     /**
35902     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35903     * Default is 50.
35904     */
35905     autoExpandMin : 50,
35906
35907     /**
35908     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35909     */
35910     autoExpandMax : 1000,
35911
35912     /**
35913     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35914     */
35915     view : null,
35916
35917     /**
35918     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35919     */
35920     loadMask : false,
35921     /**
35922     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35923     */
35924     dropTarget: false,
35925     
35926    
35927     
35928     // private
35929     rendered : false,
35930
35931     /**
35932     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35933     * of a fixed width. Default is false.
35934     */
35935     /**
35936     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35937     */
35938     /**
35939      * Called once after all setup has been completed and the grid is ready to be rendered.
35940      * @return {Roo.grid.Grid} this
35941      */
35942     render : function()
35943     {
35944         var c = this.container;
35945         // try to detect autoHeight/width mode
35946         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35947             this.autoHeight = true;
35948         }
35949         var view = this.getView();
35950         view.init(this);
35951
35952         c.on("click", this.onClick, this);
35953         c.on("dblclick", this.onDblClick, this);
35954         c.on("contextmenu", this.onContextMenu, this);
35955         c.on("keydown", this.onKeyDown, this);
35956         if (Roo.isTouch) {
35957             c.on("touchstart", this.onTouchStart, this);
35958         }
35959
35960         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35961
35962         this.getSelectionModel().init(this);
35963
35964         view.render();
35965
35966         if(this.loadMask){
35967             this.loadMask = new Roo.LoadMask(this.container,
35968                     Roo.apply({store:this.dataSource}, this.loadMask));
35969         }
35970         
35971         
35972         if (this.toolbar && this.toolbar.xtype) {
35973             this.toolbar.container = this.getView().getHeaderPanel(true);
35974             this.toolbar = new Roo.Toolbar(this.toolbar);
35975         }
35976         if (this.footer && this.footer.xtype) {
35977             this.footer.dataSource = this.getDataSource();
35978             this.footer.container = this.getView().getFooterPanel(true);
35979             this.footer = Roo.factory(this.footer, Roo);
35980         }
35981         if (this.dropTarget && this.dropTarget.xtype) {
35982             delete this.dropTarget.xtype;
35983             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35984         }
35985         
35986         
35987         this.rendered = true;
35988         this.fireEvent('render', this);
35989         return this;
35990     },
35991
35992         /**
35993          * Reconfigures the grid to use a different Store and Column Model.
35994          * The View will be bound to the new objects and refreshed.
35995          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35996          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35997          */
35998     reconfigure : function(dataSource, colModel){
35999         if(this.loadMask){
36000             this.loadMask.destroy();
36001             this.loadMask = new Roo.LoadMask(this.container,
36002                     Roo.apply({store:dataSource}, this.loadMask));
36003         }
36004         this.view.bind(dataSource, colModel);
36005         this.dataSource = dataSource;
36006         this.colModel = colModel;
36007         this.view.refresh(true);
36008     },
36009
36010     // private
36011     onKeyDown : function(e){
36012         this.fireEvent("keydown", e);
36013     },
36014
36015     /**
36016      * Destroy this grid.
36017      * @param {Boolean} removeEl True to remove the element
36018      */
36019     destroy : function(removeEl, keepListeners){
36020         if(this.loadMask){
36021             this.loadMask.destroy();
36022         }
36023         var c = this.container;
36024         c.removeAllListeners();
36025         this.view.destroy();
36026         this.colModel.purgeListeners();
36027         if(!keepListeners){
36028             this.purgeListeners();
36029         }
36030         c.update("");
36031         if(removeEl === true){
36032             c.remove();
36033         }
36034     },
36035
36036     // private
36037     processEvent : function(name, e){
36038         // does this fire select???
36039         Roo.log('grid:processEvent '  + name);
36040         
36041         if (name != 'touchstart' ) {
36042             this.fireEvent(name, e);    
36043         }
36044         
36045         var t = e.getTarget();
36046         var v = this.view;
36047         var header = v.findHeaderIndex(t);
36048         if(header !== false){
36049             var ename = name == 'touchstart' ? 'click' : name;
36050              
36051             this.fireEvent("header" + ename, this, header, e);
36052         }else{
36053             var row = v.findRowIndex(t);
36054             var cell = v.findCellIndex(t);
36055             if (name == 'touchstart') {
36056                 // first touch is always a click.
36057                 // hopefull this happens after selection is updated.?
36058                 name = false;
36059                 
36060                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36061                     var cs = this.selModel.getSelectedCell();
36062                     if (row == cs[0] && cell == cs[1]){
36063                         name = 'dblclick';
36064                     }
36065                 }
36066                 if (typeof(this.selModel.getSelections) != 'undefined') {
36067                     var cs = this.selModel.getSelections();
36068                     var ds = this.dataSource;
36069                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36070                         name = 'dblclick';
36071                     }
36072                 }
36073                 if (!name) {
36074                     return;
36075                 }
36076             }
36077             
36078             
36079             if(row !== false){
36080                 this.fireEvent("row" + name, this, row, e);
36081                 if(cell !== false){
36082                     this.fireEvent("cell" + name, this, row, cell, e);
36083                 }
36084             }
36085         }
36086     },
36087
36088     // private
36089     onClick : function(e){
36090         this.processEvent("click", e);
36091     },
36092    // private
36093     onTouchStart : function(e){
36094         this.processEvent("touchstart", e);
36095     },
36096
36097     // private
36098     onContextMenu : function(e, t){
36099         this.processEvent("contextmenu", e);
36100     },
36101
36102     // private
36103     onDblClick : function(e){
36104         this.processEvent("dblclick", e);
36105     },
36106
36107     // private
36108     walkCells : function(row, col, step, fn, scope){
36109         var cm = this.colModel, clen = cm.getColumnCount();
36110         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36111         if(step < 0){
36112             if(col < 0){
36113                 row--;
36114                 first = false;
36115             }
36116             while(row >= 0){
36117                 if(!first){
36118                     col = clen-1;
36119                 }
36120                 first = false;
36121                 while(col >= 0){
36122                     if(fn.call(scope || this, row, col, cm) === true){
36123                         return [row, col];
36124                     }
36125                     col--;
36126                 }
36127                 row--;
36128             }
36129         } else {
36130             if(col >= clen){
36131                 row++;
36132                 first = false;
36133             }
36134             while(row < rlen){
36135                 if(!first){
36136                     col = 0;
36137                 }
36138                 first = false;
36139                 while(col < clen){
36140                     if(fn.call(scope || this, row, col, cm) === true){
36141                         return [row, col];
36142                     }
36143                     col++;
36144                 }
36145                 row++;
36146             }
36147         }
36148         return null;
36149     },
36150
36151     // private
36152     getSelections : function(){
36153         return this.selModel.getSelections();
36154     },
36155
36156     /**
36157      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36158      * but if manual update is required this method will initiate it.
36159      */
36160     autoSize : function(){
36161         if(this.rendered){
36162             this.view.layout();
36163             if(this.view.adjustForScroll){
36164                 this.view.adjustForScroll();
36165             }
36166         }
36167     },
36168
36169     /**
36170      * Returns the grid's underlying element.
36171      * @return {Element} The element
36172      */
36173     getGridEl : function(){
36174         return this.container;
36175     },
36176
36177     // private for compatibility, overridden by editor grid
36178     stopEditing : function(){},
36179
36180     /**
36181      * Returns the grid's SelectionModel.
36182      * @return {SelectionModel}
36183      */
36184     getSelectionModel : function(){
36185         if(!this.selModel){
36186             this.selModel = new Roo.grid.RowSelectionModel();
36187         }
36188         return this.selModel;
36189     },
36190
36191     /**
36192      * Returns the grid's DataSource.
36193      * @return {DataSource}
36194      */
36195     getDataSource : function(){
36196         return this.dataSource;
36197     },
36198
36199     /**
36200      * Returns the grid's ColumnModel.
36201      * @return {ColumnModel}
36202      */
36203     getColumnModel : function(){
36204         return this.colModel;
36205     },
36206
36207     /**
36208      * Returns the grid's GridView object.
36209      * @return {GridView}
36210      */
36211     getView : function(){
36212         if(!this.view){
36213             this.view = new Roo.grid.GridView(this.viewConfig);
36214         }
36215         return this.view;
36216     },
36217     /**
36218      * Called to get grid's drag proxy text, by default returns this.ddText.
36219      * @return {String}
36220      */
36221     getDragDropText : function(){
36222         var count = this.selModel.getCount();
36223         return String.format(this.ddText, count, count == 1 ? '' : 's');
36224     }
36225 });
36226 /**
36227  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36228  * %0 is replaced with the number of selected rows.
36229  * @type String
36230  */
36231 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36232  * Based on:
36233  * Ext JS Library 1.1.1
36234  * Copyright(c) 2006-2007, Ext JS, LLC.
36235  *
36236  * Originally Released Under LGPL - original licence link has changed is not relivant.
36237  *
36238  * Fork - LGPL
36239  * <script type="text/javascript">
36240  */
36241  
36242 Roo.grid.AbstractGridView = function(){
36243         this.grid = null;
36244         
36245         this.events = {
36246             "beforerowremoved" : true,
36247             "beforerowsinserted" : true,
36248             "beforerefresh" : true,
36249             "rowremoved" : true,
36250             "rowsinserted" : true,
36251             "rowupdated" : true,
36252             "refresh" : true
36253         };
36254     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36255 };
36256
36257 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36258     rowClass : "x-grid-row",
36259     cellClass : "x-grid-cell",
36260     tdClass : "x-grid-td",
36261     hdClass : "x-grid-hd",
36262     splitClass : "x-grid-hd-split",
36263     
36264     init: function(grid){
36265         this.grid = grid;
36266                 var cid = this.grid.getGridEl().id;
36267         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36268         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36269         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36270         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36271         },
36272         
36273     getColumnRenderers : function(){
36274         var renderers = [];
36275         var cm = this.grid.colModel;
36276         var colCount = cm.getColumnCount();
36277         for(var i = 0; i < colCount; i++){
36278             renderers[i] = cm.getRenderer(i);
36279         }
36280         return renderers;
36281     },
36282     
36283     getColumnIds : function(){
36284         var ids = [];
36285         var cm = this.grid.colModel;
36286         var colCount = cm.getColumnCount();
36287         for(var i = 0; i < colCount; i++){
36288             ids[i] = cm.getColumnId(i);
36289         }
36290         return ids;
36291     },
36292     
36293     getDataIndexes : function(){
36294         if(!this.indexMap){
36295             this.indexMap = this.buildIndexMap();
36296         }
36297         return this.indexMap.colToData;
36298     },
36299     
36300     getColumnIndexByDataIndex : function(dataIndex){
36301         if(!this.indexMap){
36302             this.indexMap = this.buildIndexMap();
36303         }
36304         return this.indexMap.dataToCol[dataIndex];
36305     },
36306     
36307     /**
36308      * Set a css style for a column dynamically. 
36309      * @param {Number} colIndex The index of the column
36310      * @param {String} name The css property name
36311      * @param {String} value The css value
36312      */
36313     setCSSStyle : function(colIndex, name, value){
36314         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36315         Roo.util.CSS.updateRule(selector, name, value);
36316     },
36317     
36318     generateRules : function(cm){
36319         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36320         Roo.util.CSS.removeStyleSheet(rulesId);
36321         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36322             var cid = cm.getColumnId(i);
36323             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36324                          this.tdSelector, cid, " {\n}\n",
36325                          this.hdSelector, cid, " {\n}\n",
36326                          this.splitSelector, cid, " {\n}\n");
36327         }
36328         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36329     }
36330 });/*
36331  * Based on:
36332  * Ext JS Library 1.1.1
36333  * Copyright(c) 2006-2007, Ext JS, LLC.
36334  *
36335  * Originally Released Under LGPL - original licence link has changed is not relivant.
36336  *
36337  * Fork - LGPL
36338  * <script type="text/javascript">
36339  */
36340
36341 // private
36342 // This is a support class used internally by the Grid components
36343 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36344     this.grid = grid;
36345     this.view = grid.getView();
36346     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36347     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36348     if(hd2){
36349         this.setHandleElId(Roo.id(hd));
36350         this.setOuterHandleElId(Roo.id(hd2));
36351     }
36352     this.scroll = false;
36353 };
36354 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36355     maxDragWidth: 120,
36356     getDragData : function(e){
36357         var t = Roo.lib.Event.getTarget(e);
36358         var h = this.view.findHeaderCell(t);
36359         if(h){
36360             return {ddel: h.firstChild, header:h};
36361         }
36362         return false;
36363     },
36364
36365     onInitDrag : function(e){
36366         this.view.headersDisabled = true;
36367         var clone = this.dragData.ddel.cloneNode(true);
36368         clone.id = Roo.id();
36369         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36370         this.proxy.update(clone);
36371         return true;
36372     },
36373
36374     afterValidDrop : function(){
36375         var v = this.view;
36376         setTimeout(function(){
36377             v.headersDisabled = false;
36378         }, 50);
36379     },
36380
36381     afterInvalidDrop : function(){
36382         var v = this.view;
36383         setTimeout(function(){
36384             v.headersDisabled = false;
36385         }, 50);
36386     }
36387 });
36388 /*
36389  * Based on:
36390  * Ext JS Library 1.1.1
36391  * Copyright(c) 2006-2007, Ext JS, LLC.
36392  *
36393  * Originally Released Under LGPL - original licence link has changed is not relivant.
36394  *
36395  * Fork - LGPL
36396  * <script type="text/javascript">
36397  */
36398 // private
36399 // This is a support class used internally by the Grid components
36400 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36401     this.grid = grid;
36402     this.view = grid.getView();
36403     // split the proxies so they don't interfere with mouse events
36404     this.proxyTop = Roo.DomHelper.append(document.body, {
36405         cls:"col-move-top", html:"&#160;"
36406     }, true);
36407     this.proxyBottom = Roo.DomHelper.append(document.body, {
36408         cls:"col-move-bottom", html:"&#160;"
36409     }, true);
36410     this.proxyTop.hide = this.proxyBottom.hide = function(){
36411         this.setLeftTop(-100,-100);
36412         this.setStyle("visibility", "hidden");
36413     };
36414     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36415     // temporarily disabled
36416     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36417     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36418 };
36419 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36420     proxyOffsets : [-4, -9],
36421     fly: Roo.Element.fly,
36422
36423     getTargetFromEvent : function(e){
36424         var t = Roo.lib.Event.getTarget(e);
36425         var cindex = this.view.findCellIndex(t);
36426         if(cindex !== false){
36427             return this.view.getHeaderCell(cindex);
36428         }
36429         return null;
36430     },
36431
36432     nextVisible : function(h){
36433         var v = this.view, cm = this.grid.colModel;
36434         h = h.nextSibling;
36435         while(h){
36436             if(!cm.isHidden(v.getCellIndex(h))){
36437                 return h;
36438             }
36439             h = h.nextSibling;
36440         }
36441         return null;
36442     },
36443
36444     prevVisible : function(h){
36445         var v = this.view, cm = this.grid.colModel;
36446         h = h.prevSibling;
36447         while(h){
36448             if(!cm.isHidden(v.getCellIndex(h))){
36449                 return h;
36450             }
36451             h = h.prevSibling;
36452         }
36453         return null;
36454     },
36455
36456     positionIndicator : function(h, n, e){
36457         var x = Roo.lib.Event.getPageX(e);
36458         var r = Roo.lib.Dom.getRegion(n.firstChild);
36459         var px, pt, py = r.top + this.proxyOffsets[1];
36460         if((r.right - x) <= (r.right-r.left)/2){
36461             px = r.right+this.view.borderWidth;
36462             pt = "after";
36463         }else{
36464             px = r.left;
36465             pt = "before";
36466         }
36467         var oldIndex = this.view.getCellIndex(h);
36468         var newIndex = this.view.getCellIndex(n);
36469
36470         if(this.grid.colModel.isFixed(newIndex)){
36471             return false;
36472         }
36473
36474         var locked = this.grid.colModel.isLocked(newIndex);
36475
36476         if(pt == "after"){
36477             newIndex++;
36478         }
36479         if(oldIndex < newIndex){
36480             newIndex--;
36481         }
36482         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36483             return false;
36484         }
36485         px +=  this.proxyOffsets[0];
36486         this.proxyTop.setLeftTop(px, py);
36487         this.proxyTop.show();
36488         if(!this.bottomOffset){
36489             this.bottomOffset = this.view.mainHd.getHeight();
36490         }
36491         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36492         this.proxyBottom.show();
36493         return pt;
36494     },
36495
36496     onNodeEnter : function(n, dd, e, data){
36497         if(data.header != n){
36498             this.positionIndicator(data.header, n, e);
36499         }
36500     },
36501
36502     onNodeOver : function(n, dd, e, data){
36503         var result = false;
36504         if(data.header != n){
36505             result = this.positionIndicator(data.header, n, e);
36506         }
36507         if(!result){
36508             this.proxyTop.hide();
36509             this.proxyBottom.hide();
36510         }
36511         return result ? this.dropAllowed : this.dropNotAllowed;
36512     },
36513
36514     onNodeOut : function(n, dd, e, data){
36515         this.proxyTop.hide();
36516         this.proxyBottom.hide();
36517     },
36518
36519     onNodeDrop : function(n, dd, e, data){
36520         var h = data.header;
36521         if(h != n){
36522             var cm = this.grid.colModel;
36523             var x = Roo.lib.Event.getPageX(e);
36524             var r = Roo.lib.Dom.getRegion(n.firstChild);
36525             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36526             var oldIndex = this.view.getCellIndex(h);
36527             var newIndex = this.view.getCellIndex(n);
36528             var locked = cm.isLocked(newIndex);
36529             if(pt == "after"){
36530                 newIndex++;
36531             }
36532             if(oldIndex < newIndex){
36533                 newIndex--;
36534             }
36535             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36536                 return false;
36537             }
36538             cm.setLocked(oldIndex, locked, true);
36539             cm.moveColumn(oldIndex, newIndex);
36540             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36541             return true;
36542         }
36543         return false;
36544     }
36545 });
36546 /*
36547  * Based on:
36548  * Ext JS Library 1.1.1
36549  * Copyright(c) 2006-2007, Ext JS, LLC.
36550  *
36551  * Originally Released Under LGPL - original licence link has changed is not relivant.
36552  *
36553  * Fork - LGPL
36554  * <script type="text/javascript">
36555  */
36556   
36557 /**
36558  * @class Roo.grid.GridView
36559  * @extends Roo.util.Observable
36560  *
36561  * @constructor
36562  * @param {Object} config
36563  */
36564 Roo.grid.GridView = function(config){
36565     Roo.grid.GridView.superclass.constructor.call(this);
36566     this.el = null;
36567
36568     Roo.apply(this, config);
36569 };
36570
36571 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36572
36573     unselectable :  'unselectable="on"',
36574     unselectableCls :  'x-unselectable',
36575     
36576     
36577     rowClass : "x-grid-row",
36578
36579     cellClass : "x-grid-col",
36580
36581     tdClass : "x-grid-td",
36582
36583     hdClass : "x-grid-hd",
36584
36585     splitClass : "x-grid-split",
36586
36587     sortClasses : ["sort-asc", "sort-desc"],
36588
36589     enableMoveAnim : false,
36590
36591     hlColor: "C3DAF9",
36592
36593     dh : Roo.DomHelper,
36594
36595     fly : Roo.Element.fly,
36596
36597     css : Roo.util.CSS,
36598
36599     borderWidth: 1,
36600
36601     splitOffset: 3,
36602
36603     scrollIncrement : 22,
36604
36605     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36606
36607     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36608
36609     bind : function(ds, cm){
36610         if(this.ds){
36611             this.ds.un("load", this.onLoad, this);
36612             this.ds.un("datachanged", this.onDataChange, this);
36613             this.ds.un("add", this.onAdd, this);
36614             this.ds.un("remove", this.onRemove, this);
36615             this.ds.un("update", this.onUpdate, this);
36616             this.ds.un("clear", this.onClear, this);
36617         }
36618         if(ds){
36619             ds.on("load", this.onLoad, this);
36620             ds.on("datachanged", this.onDataChange, this);
36621             ds.on("add", this.onAdd, this);
36622             ds.on("remove", this.onRemove, this);
36623             ds.on("update", this.onUpdate, this);
36624             ds.on("clear", this.onClear, this);
36625         }
36626         this.ds = ds;
36627
36628         if(this.cm){
36629             this.cm.un("widthchange", this.onColWidthChange, this);
36630             this.cm.un("headerchange", this.onHeaderChange, this);
36631             this.cm.un("hiddenchange", this.onHiddenChange, this);
36632             this.cm.un("columnmoved", this.onColumnMove, this);
36633             this.cm.un("columnlockchange", this.onColumnLock, this);
36634         }
36635         if(cm){
36636             this.generateRules(cm);
36637             cm.on("widthchange", this.onColWidthChange, this);
36638             cm.on("headerchange", this.onHeaderChange, this);
36639             cm.on("hiddenchange", this.onHiddenChange, this);
36640             cm.on("columnmoved", this.onColumnMove, this);
36641             cm.on("columnlockchange", this.onColumnLock, this);
36642         }
36643         this.cm = cm;
36644     },
36645
36646     init: function(grid){
36647         Roo.grid.GridView.superclass.init.call(this, grid);
36648
36649         this.bind(grid.dataSource, grid.colModel);
36650
36651         grid.on("headerclick", this.handleHeaderClick, this);
36652
36653         if(grid.trackMouseOver){
36654             grid.on("mouseover", this.onRowOver, this);
36655             grid.on("mouseout", this.onRowOut, this);
36656         }
36657         grid.cancelTextSelection = function(){};
36658         this.gridId = grid.id;
36659
36660         var tpls = this.templates || {};
36661
36662         if(!tpls.master){
36663             tpls.master = new Roo.Template(
36664                '<div class="x-grid" hidefocus="true">',
36665                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
36666                   '<div class="x-grid-topbar"></div>',
36667                   '<div class="x-grid-scroller"><div></div></div>',
36668                   '<div class="x-grid-locked">',
36669                       '<div class="x-grid-header">{lockedHeader}</div>',
36670                       '<div class="x-grid-body">{lockedBody}</div>',
36671                   "</div>",
36672                   '<div class="x-grid-viewport">',
36673                       '<div class="x-grid-header">{header}</div>',
36674                       '<div class="x-grid-body">{body}</div>',
36675                   "</div>",
36676                   '<div class="x-grid-bottombar"></div>',
36677                  
36678                   '<div class="x-grid-resize-proxy">&#160;</div>',
36679                "</div>"
36680             );
36681             tpls.master.disableformats = true;
36682         }
36683
36684         if(!tpls.header){
36685             tpls.header = new Roo.Template(
36686                '<table border="0" cellspacing="0" cellpadding="0">',
36687                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
36688                "</table>{splits}"
36689             );
36690             tpls.header.disableformats = true;
36691         }
36692         tpls.header.compile();
36693
36694         if(!tpls.hcell){
36695             tpls.hcell = new Roo.Template(
36696                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
36697                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
36698                 "</div></td>"
36699              );
36700              tpls.hcell.disableFormats = true;
36701         }
36702         tpls.hcell.compile();
36703
36704         if(!tpls.hsplit){
36705             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
36706                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
36707             tpls.hsplit.disableFormats = true;
36708         }
36709         tpls.hsplit.compile();
36710
36711         if(!tpls.body){
36712             tpls.body = new Roo.Template(
36713                '<table border="0" cellspacing="0" cellpadding="0">',
36714                "<tbody>{rows}</tbody>",
36715                "</table>"
36716             );
36717             tpls.body.disableFormats = true;
36718         }
36719         tpls.body.compile();
36720
36721         if(!tpls.row){
36722             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
36723             tpls.row.disableFormats = true;
36724         }
36725         tpls.row.compile();
36726
36727         if(!tpls.cell){
36728             tpls.cell = new Roo.Template(
36729                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
36730                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
36731                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
36732                 "</td>"
36733             );
36734             tpls.cell.disableFormats = true;
36735         }
36736         tpls.cell.compile();
36737
36738         this.templates = tpls;
36739     },
36740
36741     // remap these for backwards compat
36742     onColWidthChange : function(){
36743         this.updateColumns.apply(this, arguments);
36744     },
36745     onHeaderChange : function(){
36746         this.updateHeaders.apply(this, arguments);
36747     }, 
36748     onHiddenChange : function(){
36749         this.handleHiddenChange.apply(this, arguments);
36750     },
36751     onColumnMove : function(){
36752         this.handleColumnMove.apply(this, arguments);
36753     },
36754     onColumnLock : function(){
36755         this.handleLockChange.apply(this, arguments);
36756     },
36757
36758     onDataChange : function(){
36759         this.refresh();
36760         this.updateHeaderSortState();
36761     },
36762
36763     onClear : function(){
36764         this.refresh();
36765     },
36766
36767     onUpdate : function(ds, record){
36768         this.refreshRow(record);
36769     },
36770
36771     refreshRow : function(record){
36772         var ds = this.ds, index;
36773         if(typeof record == 'number'){
36774             index = record;
36775             record = ds.getAt(index);
36776         }else{
36777             index = ds.indexOf(record);
36778         }
36779         this.insertRows(ds, index, index, true);
36780         this.onRemove(ds, record, index+1, true);
36781         this.syncRowHeights(index, index);
36782         this.layout();
36783         this.fireEvent("rowupdated", this, index, record);
36784     },
36785
36786     onAdd : function(ds, records, index){
36787         this.insertRows(ds, index, index + (records.length-1));
36788     },
36789
36790     onRemove : function(ds, record, index, isUpdate){
36791         if(isUpdate !== true){
36792             this.fireEvent("beforerowremoved", this, index, record);
36793         }
36794         var bt = this.getBodyTable(), lt = this.getLockedTable();
36795         if(bt.rows[index]){
36796             bt.firstChild.removeChild(bt.rows[index]);
36797         }
36798         if(lt.rows[index]){
36799             lt.firstChild.removeChild(lt.rows[index]);
36800         }
36801         if(isUpdate !== true){
36802             this.stripeRows(index);
36803             this.syncRowHeights(index, index);
36804             this.layout();
36805             this.fireEvent("rowremoved", this, index, record);
36806         }
36807     },
36808
36809     onLoad : function(){
36810         this.scrollToTop();
36811     },
36812
36813     /**
36814      * Scrolls the grid to the top
36815      */
36816     scrollToTop : function(){
36817         if(this.scroller){
36818             this.scroller.dom.scrollTop = 0;
36819             this.syncScroll();
36820         }
36821     },
36822
36823     /**
36824      * Gets a panel in the header of the grid that can be used for toolbars etc.
36825      * After modifying the contents of this panel a call to grid.autoSize() may be
36826      * required to register any changes in size.
36827      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36828      * @return Roo.Element
36829      */
36830     getHeaderPanel : function(doShow){
36831         if(doShow){
36832             this.headerPanel.show();
36833         }
36834         return this.headerPanel;
36835     },
36836
36837     /**
36838      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36839      * After modifying the contents of this panel a call to grid.autoSize() may be
36840      * required to register any changes in size.
36841      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36842      * @return Roo.Element
36843      */
36844     getFooterPanel : function(doShow){
36845         if(doShow){
36846             this.footerPanel.show();
36847         }
36848         return this.footerPanel;
36849     },
36850
36851     initElements : function(){
36852         var E = Roo.Element;
36853         var el = this.grid.getGridEl().dom.firstChild;
36854         var cs = el.childNodes;
36855
36856         this.el = new E(el);
36857         
36858          this.focusEl = new E(el.firstChild);
36859         this.focusEl.swallowEvent("click", true);
36860         
36861         this.headerPanel = new E(cs[1]);
36862         this.headerPanel.enableDisplayMode("block");
36863
36864         this.scroller = new E(cs[2]);
36865         this.scrollSizer = new E(this.scroller.dom.firstChild);
36866
36867         this.lockedWrap = new E(cs[3]);
36868         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36869         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36870
36871         this.mainWrap = new E(cs[4]);
36872         this.mainHd = new E(this.mainWrap.dom.firstChild);
36873         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36874
36875         this.footerPanel = new E(cs[5]);
36876         this.footerPanel.enableDisplayMode("block");
36877
36878         this.resizeProxy = new E(cs[6]);
36879
36880         this.headerSelector = String.format(
36881            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36882            this.lockedHd.id, this.mainHd.id
36883         );
36884
36885         this.splitterSelector = String.format(
36886            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36887            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36888         );
36889     },
36890     idToCssName : function(s)
36891     {
36892         return s.replace(/[^a-z0-9]+/ig, '-');
36893     },
36894
36895     getHeaderCell : function(index){
36896         return Roo.DomQuery.select(this.headerSelector)[index];
36897     },
36898
36899     getHeaderCellMeasure : function(index){
36900         return this.getHeaderCell(index).firstChild;
36901     },
36902
36903     getHeaderCellText : function(index){
36904         return this.getHeaderCell(index).firstChild.firstChild;
36905     },
36906
36907     getLockedTable : function(){
36908         return this.lockedBody.dom.firstChild;
36909     },
36910
36911     getBodyTable : function(){
36912         return this.mainBody.dom.firstChild;
36913     },
36914
36915     getLockedRow : function(index){
36916         return this.getLockedTable().rows[index];
36917     },
36918
36919     getRow : function(index){
36920         return this.getBodyTable().rows[index];
36921     },
36922
36923     getRowComposite : function(index){
36924         if(!this.rowEl){
36925             this.rowEl = new Roo.CompositeElementLite();
36926         }
36927         var els = [], lrow, mrow;
36928         if(lrow = this.getLockedRow(index)){
36929             els.push(lrow);
36930         }
36931         if(mrow = this.getRow(index)){
36932             els.push(mrow);
36933         }
36934         this.rowEl.elements = els;
36935         return this.rowEl;
36936     },
36937     /**
36938      * Gets the 'td' of the cell
36939      * 
36940      * @param {Integer} rowIndex row to select
36941      * @param {Integer} colIndex column to select
36942      * 
36943      * @return {Object} 
36944      */
36945     getCell : function(rowIndex, colIndex){
36946         var locked = this.cm.getLockedCount();
36947         var source;
36948         if(colIndex < locked){
36949             source = this.lockedBody.dom.firstChild;
36950         }else{
36951             source = this.mainBody.dom.firstChild;
36952             colIndex -= locked;
36953         }
36954         return source.rows[rowIndex].childNodes[colIndex];
36955     },
36956
36957     getCellText : function(rowIndex, colIndex){
36958         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36959     },
36960
36961     getCellBox : function(cell){
36962         var b = this.fly(cell).getBox();
36963         if(Roo.isOpera){ // opera fails to report the Y
36964             b.y = cell.offsetTop + this.mainBody.getY();
36965         }
36966         return b;
36967     },
36968
36969     getCellIndex : function(cell){
36970         var id = String(cell.className).match(this.cellRE);
36971         if(id){
36972             return parseInt(id[1], 10);
36973         }
36974         return 0;
36975     },
36976
36977     findHeaderIndex : function(n){
36978         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36979         return r ? this.getCellIndex(r) : false;
36980     },
36981
36982     findHeaderCell : function(n){
36983         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36984         return r ? r : false;
36985     },
36986
36987     findRowIndex : function(n){
36988         if(!n){
36989             return false;
36990         }
36991         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36992         return r ? r.rowIndex : false;
36993     },
36994
36995     findCellIndex : function(node){
36996         var stop = this.el.dom;
36997         while(node && node != stop){
36998             if(this.findRE.test(node.className)){
36999                 return this.getCellIndex(node);
37000             }
37001             node = node.parentNode;
37002         }
37003         return false;
37004     },
37005
37006     getColumnId : function(index){
37007         return this.cm.getColumnId(index);
37008     },
37009
37010     getSplitters : function()
37011     {
37012         if(this.splitterSelector){
37013            return Roo.DomQuery.select(this.splitterSelector);
37014         }else{
37015             return null;
37016       }
37017     },
37018
37019     getSplitter : function(index){
37020         return this.getSplitters()[index];
37021     },
37022
37023     onRowOver : function(e, t){
37024         var row;
37025         if((row = this.findRowIndex(t)) !== false){
37026             this.getRowComposite(row).addClass("x-grid-row-over");
37027         }
37028     },
37029
37030     onRowOut : function(e, t){
37031         var row;
37032         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37033             this.getRowComposite(row).removeClass("x-grid-row-over");
37034         }
37035     },
37036
37037     renderHeaders : function(){
37038         var cm = this.cm;
37039         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37040         var cb = [], lb = [], sb = [], lsb = [], p = {};
37041         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37042             p.cellId = "x-grid-hd-0-" + i;
37043             p.splitId = "x-grid-csplit-0-" + i;
37044             p.id = cm.getColumnId(i);
37045             p.title = cm.getColumnTooltip(i) || "";
37046             p.value = cm.getColumnHeader(i) || "";
37047             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37048             if(!cm.isLocked(i)){
37049                 cb[cb.length] = ct.apply(p);
37050                 sb[sb.length] = st.apply(p);
37051             }else{
37052                 lb[lb.length] = ct.apply(p);
37053                 lsb[lsb.length] = st.apply(p);
37054             }
37055         }
37056         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37057                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37058     },
37059
37060     updateHeaders : function(){
37061         var html = this.renderHeaders();
37062         this.lockedHd.update(html[0]);
37063         this.mainHd.update(html[1]);
37064     },
37065
37066     /**
37067      * Focuses the specified row.
37068      * @param {Number} row The row index
37069      */
37070     focusRow : function(row)
37071     {
37072         //Roo.log('GridView.focusRow');
37073         var x = this.scroller.dom.scrollLeft;
37074         this.focusCell(row, 0, false);
37075         this.scroller.dom.scrollLeft = x;
37076     },
37077
37078     /**
37079      * Focuses the specified cell.
37080      * @param {Number} row The row index
37081      * @param {Number} col The column index
37082      * @param {Boolean} hscroll false to disable horizontal scrolling
37083      */
37084     focusCell : function(row, col, hscroll)
37085     {
37086         //Roo.log('GridView.focusCell');
37087         var el = this.ensureVisible(row, col, hscroll);
37088         this.focusEl.alignTo(el, "tl-tl");
37089         if(Roo.isGecko){
37090             this.focusEl.focus();
37091         }else{
37092             this.focusEl.focus.defer(1, this.focusEl);
37093         }
37094     },
37095
37096     /**
37097      * Scrolls the specified cell into view
37098      * @param {Number} row The row index
37099      * @param {Number} col The column index
37100      * @param {Boolean} hscroll false to disable horizontal scrolling
37101      */
37102     ensureVisible : function(row, col, hscroll)
37103     {
37104         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37105         //return null; //disable for testing.
37106         if(typeof row != "number"){
37107             row = row.rowIndex;
37108         }
37109         if(row < 0 && row >= this.ds.getCount()){
37110             return  null;
37111         }
37112         col = (col !== undefined ? col : 0);
37113         var cm = this.grid.colModel;
37114         while(cm.isHidden(col)){
37115             col++;
37116         }
37117
37118         var el = this.getCell(row, col);
37119         if(!el){
37120             return null;
37121         }
37122         var c = this.scroller.dom;
37123
37124         var ctop = parseInt(el.offsetTop, 10);
37125         var cleft = parseInt(el.offsetLeft, 10);
37126         var cbot = ctop + el.offsetHeight;
37127         var cright = cleft + el.offsetWidth;
37128         
37129         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37130         var stop = parseInt(c.scrollTop, 10);
37131         var sleft = parseInt(c.scrollLeft, 10);
37132         var sbot = stop + ch;
37133         var sright = sleft + c.clientWidth;
37134         /*
37135         Roo.log('GridView.ensureVisible:' +
37136                 ' ctop:' + ctop +
37137                 ' c.clientHeight:' + c.clientHeight +
37138                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37139                 ' stop:' + stop +
37140                 ' cbot:' + cbot +
37141                 ' sbot:' + sbot +
37142                 ' ch:' + ch  
37143                 );
37144         */
37145         if(ctop < stop){
37146              c.scrollTop = ctop;
37147             //Roo.log("set scrolltop to ctop DISABLE?");
37148         }else if(cbot > sbot){
37149             //Roo.log("set scrolltop to cbot-ch");
37150             c.scrollTop = cbot-ch;
37151         }
37152         
37153         if(hscroll !== false){
37154             if(cleft < sleft){
37155                 c.scrollLeft = cleft;
37156             }else if(cright > sright){
37157                 c.scrollLeft = cright-c.clientWidth;
37158             }
37159         }
37160          
37161         return el;
37162     },
37163
37164     updateColumns : function(){
37165         this.grid.stopEditing();
37166         var cm = this.grid.colModel, colIds = this.getColumnIds();
37167         //var totalWidth = cm.getTotalWidth();
37168         var pos = 0;
37169         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37170             //if(cm.isHidden(i)) continue;
37171             var w = cm.getColumnWidth(i);
37172             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37173             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37174         }
37175         this.updateSplitters();
37176     },
37177
37178     generateRules : function(cm){
37179         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37180         Roo.util.CSS.removeStyleSheet(rulesId);
37181         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37182             var cid = cm.getColumnId(i);
37183             var align = '';
37184             if(cm.config[i].align){
37185                 align = 'text-align:'+cm.config[i].align+';';
37186             }
37187             var hidden = '';
37188             if(cm.isHidden(i)){
37189                 hidden = 'display:none;';
37190             }
37191             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37192             ruleBuf.push(
37193                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37194                     this.hdSelector, cid, " {\n", align, width, "}\n",
37195                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37196                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37197         }
37198         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37199     },
37200
37201     updateSplitters : function(){
37202         var cm = this.cm, s = this.getSplitters();
37203         if(s){ // splitters not created yet
37204             var pos = 0, locked = true;
37205             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37206                 if(cm.isHidden(i)) continue;
37207                 var w = cm.getColumnWidth(i); // make sure it's a number
37208                 if(!cm.isLocked(i) && locked){
37209                     pos = 0;
37210                     locked = false;
37211                 }
37212                 pos += w;
37213                 s[i].style.left = (pos-this.splitOffset) + "px";
37214             }
37215         }
37216     },
37217
37218     handleHiddenChange : function(colModel, colIndex, hidden){
37219         if(hidden){
37220             this.hideColumn(colIndex);
37221         }else{
37222             this.unhideColumn(colIndex);
37223         }
37224     },
37225
37226     hideColumn : function(colIndex){
37227         var cid = this.getColumnId(colIndex);
37228         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37229         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37230         if(Roo.isSafari){
37231             this.updateHeaders();
37232         }
37233         this.updateSplitters();
37234         this.layout();
37235     },
37236
37237     unhideColumn : function(colIndex){
37238         var cid = this.getColumnId(colIndex);
37239         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37240         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37241
37242         if(Roo.isSafari){
37243             this.updateHeaders();
37244         }
37245         this.updateSplitters();
37246         this.layout();
37247     },
37248
37249     insertRows : function(dm, firstRow, lastRow, isUpdate){
37250         if(firstRow == 0 && lastRow == dm.getCount()-1){
37251             this.refresh();
37252         }else{
37253             if(!isUpdate){
37254                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37255             }
37256             var s = this.getScrollState();
37257             var markup = this.renderRows(firstRow, lastRow);
37258             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37259             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37260             this.restoreScroll(s);
37261             if(!isUpdate){
37262                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37263                 this.syncRowHeights(firstRow, lastRow);
37264                 this.stripeRows(firstRow);
37265                 this.layout();
37266             }
37267         }
37268     },
37269
37270     bufferRows : function(markup, target, index){
37271         var before = null, trows = target.rows, tbody = target.tBodies[0];
37272         if(index < trows.length){
37273             before = trows[index];
37274         }
37275         var b = document.createElement("div");
37276         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37277         var rows = b.firstChild.rows;
37278         for(var i = 0, len = rows.length; i < len; i++){
37279             if(before){
37280                 tbody.insertBefore(rows[0], before);
37281             }else{
37282                 tbody.appendChild(rows[0]);
37283             }
37284         }
37285         b.innerHTML = "";
37286         b = null;
37287     },
37288
37289     deleteRows : function(dm, firstRow, lastRow){
37290         if(dm.getRowCount()<1){
37291             this.fireEvent("beforerefresh", this);
37292             this.mainBody.update("");
37293             this.lockedBody.update("");
37294             this.fireEvent("refresh", this);
37295         }else{
37296             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37297             var bt = this.getBodyTable();
37298             var tbody = bt.firstChild;
37299             var rows = bt.rows;
37300             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37301                 tbody.removeChild(rows[firstRow]);
37302             }
37303             this.stripeRows(firstRow);
37304             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37305         }
37306     },
37307
37308     updateRows : function(dataSource, firstRow, lastRow){
37309         var s = this.getScrollState();
37310         this.refresh();
37311         this.restoreScroll(s);
37312     },
37313
37314     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37315         if(!noRefresh){
37316            this.refresh();
37317         }
37318         this.updateHeaderSortState();
37319     },
37320
37321     getScrollState : function(){
37322         
37323         var sb = this.scroller.dom;
37324         return {left: sb.scrollLeft, top: sb.scrollTop};
37325     },
37326
37327     stripeRows : function(startRow){
37328         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37329             return;
37330         }
37331         startRow = startRow || 0;
37332         var rows = this.getBodyTable().rows;
37333         var lrows = this.getLockedTable().rows;
37334         var cls = ' x-grid-row-alt ';
37335         for(var i = startRow, len = rows.length; i < len; i++){
37336             var row = rows[i], lrow = lrows[i];
37337             var isAlt = ((i+1) % 2 == 0);
37338             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37339             if(isAlt == hasAlt){
37340                 continue;
37341             }
37342             if(isAlt){
37343                 row.className += " x-grid-row-alt";
37344             }else{
37345                 row.className = row.className.replace("x-grid-row-alt", "");
37346             }
37347             if(lrow){
37348                 lrow.className = row.className;
37349             }
37350         }
37351     },
37352
37353     restoreScroll : function(state){
37354         //Roo.log('GridView.restoreScroll');
37355         var sb = this.scroller.dom;
37356         sb.scrollLeft = state.left;
37357         sb.scrollTop = state.top;
37358         this.syncScroll();
37359     },
37360
37361     syncScroll : function(){
37362         //Roo.log('GridView.syncScroll');
37363         var sb = this.scroller.dom;
37364         var sh = this.mainHd.dom;
37365         var bs = this.mainBody.dom;
37366         var lv = this.lockedBody.dom;
37367         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37368         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37369     },
37370
37371     handleScroll : function(e){
37372         this.syncScroll();
37373         var sb = this.scroller.dom;
37374         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37375         e.stopEvent();
37376     },
37377
37378     handleWheel : function(e){
37379         var d = e.getWheelDelta();
37380         this.scroller.dom.scrollTop -= d*22;
37381         // set this here to prevent jumpy scrolling on large tables
37382         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37383         e.stopEvent();
37384     },
37385
37386     renderRows : function(startRow, endRow){
37387         // pull in all the crap needed to render rows
37388         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37389         var colCount = cm.getColumnCount();
37390
37391         if(ds.getCount() < 1){
37392             return ["", ""];
37393         }
37394
37395         // build a map for all the columns
37396         var cs = [];
37397         for(var i = 0; i < colCount; i++){
37398             var name = cm.getDataIndex(i);
37399             cs[i] = {
37400                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37401                 renderer : cm.getRenderer(i),
37402                 id : cm.getColumnId(i),
37403                 locked : cm.isLocked(i)
37404             };
37405         }
37406
37407         startRow = startRow || 0;
37408         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37409
37410         // records to render
37411         var rs = ds.getRange(startRow, endRow);
37412
37413         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37414     },
37415
37416     // As much as I hate to duplicate code, this was branched because FireFox really hates
37417     // [].join("") on strings. The performance difference was substantial enough to
37418     // branch this function
37419     doRender : Roo.isGecko ?
37420             function(cs, rs, ds, startRow, colCount, stripe){
37421                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37422                 // buffers
37423                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37424                 
37425                 var hasListener = this.grid.hasListener('rowclass');
37426                 var rowcfg = {};
37427                 for(var j = 0, len = rs.length; j < len; j++){
37428                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37429                     for(var i = 0; i < colCount; i++){
37430                         c = cs[i];
37431                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37432                         p.id = c.id;
37433                         p.css = p.attr = "";
37434                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37435                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37436                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37437                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37438                         }
37439                         var markup = ct.apply(p);
37440                         if(!c.locked){
37441                             cb+= markup;
37442                         }else{
37443                             lcb+= markup;
37444                         }
37445                     }
37446                     var alt = [];
37447                     if(stripe && ((rowIndex+1) % 2 == 0)){
37448                         alt.push("x-grid-row-alt")
37449                     }
37450                     if(r.dirty){
37451                         alt.push(  " x-grid-dirty-row");
37452                     }
37453                     rp.cells = lcb;
37454                     if(this.getRowClass){
37455                         alt.push(this.getRowClass(r, rowIndex));
37456                     }
37457                     if (hasListener) {
37458                         rowcfg = {
37459                              
37460                             record: r,
37461                             rowIndex : rowIndex,
37462                             rowClass : ''
37463                         }
37464                         this.grid.fireEvent('rowclass', this, rowcfg);
37465                         alt.push(rowcfg.rowClass);
37466                     }
37467                     rp.alt = alt.join(" ");
37468                     lbuf+= rt.apply(rp);
37469                     rp.cells = cb;
37470                     buf+=  rt.apply(rp);
37471                 }
37472                 return [lbuf, buf];
37473             } :
37474             function(cs, rs, ds, startRow, colCount, stripe){
37475                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37476                 // buffers
37477                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37478                 var hasListener = this.grid.hasListener('rowclass');
37479  
37480                 var rowcfg = {};
37481                 for(var j = 0, len = rs.length; j < len; j++){
37482                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37483                     for(var i = 0; i < colCount; i++){
37484                         c = cs[i];
37485                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37486                         p.id = c.id;
37487                         p.css = p.attr = "";
37488                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37489                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37490                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37491                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37492                         }
37493                         
37494                         var markup = ct.apply(p);
37495                         if(!c.locked){
37496                             cb[cb.length] = markup;
37497                         }else{
37498                             lcb[lcb.length] = markup;
37499                         }
37500                     }
37501                     var alt = [];
37502                     if(stripe && ((rowIndex+1) % 2 == 0)){
37503                         alt.push( "x-grid-row-alt");
37504                     }
37505                     if(r.dirty){
37506                         alt.push(" x-grid-dirty-row");
37507                     }
37508                     rp.cells = lcb;
37509                     if(this.getRowClass){
37510                         alt.push( this.getRowClass(r, rowIndex));
37511                     }
37512                     if (hasListener) {
37513                         rowcfg = {
37514                              
37515                             record: r,
37516                             rowIndex : rowIndex,
37517                             rowClass : ''
37518                         }
37519                         this.grid.fireEvent('rowclass', this, rowcfg);
37520                         alt.push(rowcfg.rowClass);
37521                     }
37522                     rp.alt = alt.join(" ");
37523                     rp.cells = lcb.join("");
37524                     lbuf[lbuf.length] = rt.apply(rp);
37525                     rp.cells = cb.join("");
37526                     buf[buf.length] =  rt.apply(rp);
37527                 }
37528                 return [lbuf.join(""), buf.join("")];
37529             },
37530
37531     renderBody : function(){
37532         var markup = this.renderRows();
37533         var bt = this.templates.body;
37534         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37535     },
37536
37537     /**
37538      * Refreshes the grid
37539      * @param {Boolean} headersToo
37540      */
37541     refresh : function(headersToo){
37542         this.fireEvent("beforerefresh", this);
37543         this.grid.stopEditing();
37544         var result = this.renderBody();
37545         this.lockedBody.update(result[0]);
37546         this.mainBody.update(result[1]);
37547         if(headersToo === true){
37548             this.updateHeaders();
37549             this.updateColumns();
37550             this.updateSplitters();
37551             this.updateHeaderSortState();
37552         }
37553         this.syncRowHeights();
37554         this.layout();
37555         this.fireEvent("refresh", this);
37556     },
37557
37558     handleColumnMove : function(cm, oldIndex, newIndex){
37559         this.indexMap = null;
37560         var s = this.getScrollState();
37561         this.refresh(true);
37562         this.restoreScroll(s);
37563         this.afterMove(newIndex);
37564     },
37565
37566     afterMove : function(colIndex){
37567         if(this.enableMoveAnim && Roo.enableFx){
37568             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37569         }
37570         // if multisort - fix sortOrder, and reload..
37571         if (this.grid.dataSource.multiSort) {
37572             // the we can call sort again..
37573             var dm = this.grid.dataSource;
37574             var cm = this.grid.colModel;
37575             var so = [];
37576             for(var i = 0; i < cm.config.length; i++ ) {
37577                 
37578                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37579                     continue; // dont' bother, it's not in sort list or being set.
37580                 }
37581                 
37582                 so.push(cm.config[i].dataIndex);
37583             };
37584             dm.sortOrder = so;
37585             dm.load(dm.lastOptions);
37586             
37587             
37588         }
37589         
37590     },
37591
37592     updateCell : function(dm, rowIndex, dataIndex){
37593         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37594         if(typeof colIndex == "undefined"){ // not present in grid
37595             return;
37596         }
37597         var cm = this.grid.colModel;
37598         var cell = this.getCell(rowIndex, colIndex);
37599         var cellText = this.getCellText(rowIndex, colIndex);
37600
37601         var p = {
37602             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37603             id : cm.getColumnId(colIndex),
37604             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37605         };
37606         var renderer = cm.getRenderer(colIndex);
37607         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37608         if(typeof val == "undefined" || val === "") val = "&#160;";
37609         cellText.innerHTML = val;
37610         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37611         this.syncRowHeights(rowIndex, rowIndex);
37612     },
37613
37614     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37615         var maxWidth = 0;
37616         if(this.grid.autoSizeHeaders){
37617             var h = this.getHeaderCellMeasure(colIndex);
37618             maxWidth = Math.max(maxWidth, h.scrollWidth);
37619         }
37620         var tb, index;
37621         if(this.cm.isLocked(colIndex)){
37622             tb = this.getLockedTable();
37623             index = colIndex;
37624         }else{
37625             tb = this.getBodyTable();
37626             index = colIndex - this.cm.getLockedCount();
37627         }
37628         if(tb && tb.rows){
37629             var rows = tb.rows;
37630             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37631             for(var i = 0; i < stopIndex; i++){
37632                 var cell = rows[i].childNodes[index].firstChild;
37633                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37634             }
37635         }
37636         return maxWidth + /*margin for error in IE*/ 5;
37637     },
37638     /**
37639      * Autofit a column to its content.
37640      * @param {Number} colIndex
37641      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37642      */
37643      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37644          if(this.cm.isHidden(colIndex)){
37645              return; // can't calc a hidden column
37646          }
37647         if(forceMinSize){
37648             var cid = this.cm.getColumnId(colIndex);
37649             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37650            if(this.grid.autoSizeHeaders){
37651                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37652            }
37653         }
37654         var newWidth = this.calcColumnWidth(colIndex);
37655         this.cm.setColumnWidth(colIndex,
37656             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37657         if(!suppressEvent){
37658             this.grid.fireEvent("columnresize", colIndex, newWidth);
37659         }
37660     },
37661
37662     /**
37663      * Autofits all columns to their content and then expands to fit any extra space in the grid
37664      */
37665      autoSizeColumns : function(){
37666         var cm = this.grid.colModel;
37667         var colCount = cm.getColumnCount();
37668         for(var i = 0; i < colCount; i++){
37669             this.autoSizeColumn(i, true, true);
37670         }
37671         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
37672             this.fitColumns();
37673         }else{
37674             this.updateColumns();
37675             this.layout();
37676         }
37677     },
37678
37679     /**
37680      * Autofits all columns to the grid's width proportionate with their current size
37681      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
37682      */
37683     fitColumns : function(reserveScrollSpace){
37684         var cm = this.grid.colModel;
37685         var colCount = cm.getColumnCount();
37686         var cols = [];
37687         var width = 0;
37688         var i, w;
37689         for (i = 0; i < colCount; i++){
37690             if(!cm.isHidden(i) && !cm.isFixed(i)){
37691                 w = cm.getColumnWidth(i);
37692                 cols.push(i);
37693                 cols.push(w);
37694                 width += w;
37695             }
37696         }
37697         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
37698         if(reserveScrollSpace){
37699             avail -= 17;
37700         }
37701         var frac = (avail - cm.getTotalWidth())/width;
37702         while (cols.length){
37703             w = cols.pop();
37704             i = cols.pop();
37705             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
37706         }
37707         this.updateColumns();
37708         this.layout();
37709     },
37710
37711     onRowSelect : function(rowIndex){
37712         var row = this.getRowComposite(rowIndex);
37713         row.addClass("x-grid-row-selected");
37714     },
37715
37716     onRowDeselect : function(rowIndex){
37717         var row = this.getRowComposite(rowIndex);
37718         row.removeClass("x-grid-row-selected");
37719     },
37720
37721     onCellSelect : function(row, col){
37722         var cell = this.getCell(row, col);
37723         if(cell){
37724             Roo.fly(cell).addClass("x-grid-cell-selected");
37725         }
37726     },
37727
37728     onCellDeselect : function(row, col){
37729         var cell = this.getCell(row, col);
37730         if(cell){
37731             Roo.fly(cell).removeClass("x-grid-cell-selected");
37732         }
37733     },
37734
37735     updateHeaderSortState : function(){
37736         
37737         // sort state can be single { field: xxx, direction : yyy}
37738         // or   { xxx=>ASC , yyy : DESC ..... }
37739         
37740         var mstate = {};
37741         if (!this.ds.multiSort) { 
37742             var state = this.ds.getSortState();
37743             if(!state){
37744                 return;
37745             }
37746             mstate[state.field] = state.direction;
37747             // FIXME... - this is not used here.. but might be elsewhere..
37748             this.sortState = state;
37749             
37750         } else {
37751             mstate = this.ds.sortToggle;
37752         }
37753         //remove existing sort classes..
37754         
37755         var sc = this.sortClasses;
37756         var hds = this.el.select(this.headerSelector).removeClass(sc);
37757         
37758         for(var f in mstate) {
37759         
37760             var sortColumn = this.cm.findColumnIndex(f);
37761             
37762             if(sortColumn != -1){
37763                 var sortDir = mstate[f];        
37764                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
37765             }
37766         }
37767         
37768          
37769         
37770     },
37771
37772
37773     handleHeaderClick : function(g, index,e){
37774         
37775         Roo.log("header click");
37776         
37777         if (Roo.isTouch) {
37778             // touch events on header are handled by context
37779             this.handleHdCtx(g,index,e);
37780             return;
37781         }
37782         
37783         
37784         if(this.headersDisabled){
37785             return;
37786         }
37787         var dm = g.dataSource, cm = g.colModel;
37788         if(!cm.isSortable(index)){
37789             return;
37790         }
37791         g.stopEditing();
37792         
37793         if (dm.multiSort) {
37794             // update the sortOrder
37795             var so = [];
37796             for(var i = 0; i < cm.config.length; i++ ) {
37797                 
37798                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
37799                     continue; // dont' bother, it's not in sort list or being set.
37800                 }
37801                 
37802                 so.push(cm.config[i].dataIndex);
37803             };
37804             dm.sortOrder = so;
37805         }
37806         
37807         
37808         dm.sort(cm.getDataIndex(index));
37809     },
37810
37811
37812     destroy : function(){
37813         if(this.colMenu){
37814             this.colMenu.removeAll();
37815             Roo.menu.MenuMgr.unregister(this.colMenu);
37816             this.colMenu.getEl().remove();
37817             delete this.colMenu;
37818         }
37819         if(this.hmenu){
37820             this.hmenu.removeAll();
37821             Roo.menu.MenuMgr.unregister(this.hmenu);
37822             this.hmenu.getEl().remove();
37823             delete this.hmenu;
37824         }
37825         if(this.grid.enableColumnMove){
37826             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37827             if(dds){
37828                 for(var dd in dds){
37829                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
37830                         var elid = dds[dd].dragElId;
37831                         dds[dd].unreg();
37832                         Roo.get(elid).remove();
37833                     } else if(dds[dd].config.isTarget){
37834                         dds[dd].proxyTop.remove();
37835                         dds[dd].proxyBottom.remove();
37836                         dds[dd].unreg();
37837                     }
37838                     if(Roo.dd.DDM.locationCache[dd]){
37839                         delete Roo.dd.DDM.locationCache[dd];
37840                     }
37841                 }
37842                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37843             }
37844         }
37845         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37846         this.bind(null, null);
37847         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37848     },
37849
37850     handleLockChange : function(){
37851         this.refresh(true);
37852     },
37853
37854     onDenyColumnLock : function(){
37855
37856     },
37857
37858     onDenyColumnHide : function(){
37859
37860     },
37861
37862     handleHdMenuClick : function(item){
37863         var index = this.hdCtxIndex;
37864         var cm = this.cm, ds = this.ds;
37865         switch(item.id){
37866             case "asc":
37867                 ds.sort(cm.getDataIndex(index), "ASC");
37868                 break;
37869             case "desc":
37870                 ds.sort(cm.getDataIndex(index), "DESC");
37871                 break;
37872             case "lock":
37873                 var lc = cm.getLockedCount();
37874                 if(cm.getColumnCount(true) <= lc+1){
37875                     this.onDenyColumnLock();
37876                     return;
37877                 }
37878                 if(lc != index){
37879                     cm.setLocked(index, true, true);
37880                     cm.moveColumn(index, lc);
37881                     this.grid.fireEvent("columnmove", index, lc);
37882                 }else{
37883                     cm.setLocked(index, true);
37884                 }
37885             break;
37886             case "unlock":
37887                 var lc = cm.getLockedCount();
37888                 if((lc-1) != index){
37889                     cm.setLocked(index, false, true);
37890                     cm.moveColumn(index, lc-1);
37891                     this.grid.fireEvent("columnmove", index, lc-1);
37892                 }else{
37893                     cm.setLocked(index, false);
37894                 }
37895             break;
37896             case 'wider': // used to expand cols on touch..
37897             case 'narrow':
37898                 var cw = cm.getColumnWidth(index);
37899                 cw += (item.id == 'wider' ? 1 : -1) * 50;
37900                 cw = Math.max(0, cw);
37901                 cw = Math.min(cw,4000);
37902                 cm.setColumnWidth(index, cw);
37903                 break;
37904                 
37905             default:
37906                 index = cm.getIndexById(item.id.substr(4));
37907                 if(index != -1){
37908                     if(item.checked && cm.getColumnCount(true) <= 1){
37909                         this.onDenyColumnHide();
37910                         return false;
37911                     }
37912                     cm.setHidden(index, item.checked);
37913                 }
37914         }
37915         return true;
37916     },
37917
37918     beforeColMenuShow : function(){
37919         var cm = this.cm,  colCount = cm.getColumnCount();
37920         this.colMenu.removeAll();
37921         for(var i = 0; i < colCount; i++){
37922             this.colMenu.add(new Roo.menu.CheckItem({
37923                 id: "col-"+cm.getColumnId(i),
37924                 text: cm.getColumnHeader(i),
37925                 checked: !cm.isHidden(i),
37926                 hideOnClick:false
37927             }));
37928         }
37929     },
37930
37931     handleHdCtx : function(g, index, e){
37932         e.stopEvent();
37933         var hd = this.getHeaderCell(index);
37934         this.hdCtxIndex = index;
37935         var ms = this.hmenu.items, cm = this.cm;
37936         ms.get("asc").setDisabled(!cm.isSortable(index));
37937         ms.get("desc").setDisabled(!cm.isSortable(index));
37938         if(this.grid.enableColLock !== false){
37939             ms.get("lock").setDisabled(cm.isLocked(index));
37940             ms.get("unlock").setDisabled(!cm.isLocked(index));
37941         }
37942         this.hmenu.show(hd, "tl-bl");
37943     },
37944
37945     handleHdOver : function(e){
37946         var hd = this.findHeaderCell(e.getTarget());
37947         if(hd && !this.headersDisabled){
37948             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37949                this.fly(hd).addClass("x-grid-hd-over");
37950             }
37951         }
37952     },
37953
37954     handleHdOut : function(e){
37955         var hd = this.findHeaderCell(e.getTarget());
37956         if(hd){
37957             this.fly(hd).removeClass("x-grid-hd-over");
37958         }
37959     },
37960
37961     handleSplitDblClick : function(e, t){
37962         var i = this.getCellIndex(t);
37963         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37964             this.autoSizeColumn(i, true);
37965             this.layout();
37966         }
37967     },
37968
37969     render : function(){
37970
37971         var cm = this.cm;
37972         var colCount = cm.getColumnCount();
37973
37974         if(this.grid.monitorWindowResize === true){
37975             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37976         }
37977         var header = this.renderHeaders();
37978         var body = this.templates.body.apply({rows:""});
37979         var html = this.templates.master.apply({
37980             lockedBody: body,
37981             body: body,
37982             lockedHeader: header[0],
37983             header: header[1]
37984         });
37985
37986         //this.updateColumns();
37987
37988         this.grid.getGridEl().dom.innerHTML = html;
37989
37990         this.initElements();
37991         
37992         // a kludge to fix the random scolling effect in webkit
37993         this.el.on("scroll", function() {
37994             this.el.dom.scrollTop=0; // hopefully not recursive..
37995         },this);
37996
37997         this.scroller.on("scroll", this.handleScroll, this);
37998         this.lockedBody.on("mousewheel", this.handleWheel, this);
37999         this.mainBody.on("mousewheel", this.handleWheel, this);
38000
38001         this.mainHd.on("mouseover", this.handleHdOver, this);
38002         this.mainHd.on("mouseout", this.handleHdOut, this);
38003         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38004                 {delegate: "."+this.splitClass});
38005
38006         this.lockedHd.on("mouseover", this.handleHdOver, this);
38007         this.lockedHd.on("mouseout", this.handleHdOut, this);
38008         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38009                 {delegate: "."+this.splitClass});
38010
38011         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38012             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38013         }
38014
38015         this.updateSplitters();
38016
38017         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38018             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38019             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38020         }
38021
38022         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38023             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38024             this.hmenu.add(
38025                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38026                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38027             );
38028             if(this.grid.enableColLock !== false){
38029                 this.hmenu.add('-',
38030                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38031                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38032                 );
38033             }
38034             if (Roo.isTouch) {
38035                  this.hmenu.add('-',
38036                     {id:"wider", text: this.columnsWiderText},
38037                     {id:"narrow", text: this.columnsNarrowText }
38038                 );
38039                 
38040                  
38041             }
38042             
38043             if(this.grid.enableColumnHide !== false){
38044
38045                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38046                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38047                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38048
38049                 this.hmenu.add('-',
38050                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38051                 );
38052             }
38053             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38054
38055             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38056         }
38057
38058         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38059             this.dd = new Roo.grid.GridDragZone(this.grid, {
38060                 ddGroup : this.grid.ddGroup || 'GridDD'
38061             });
38062             
38063         }
38064
38065         /*
38066         for(var i = 0; i < colCount; i++){
38067             if(cm.isHidden(i)){
38068                 this.hideColumn(i);
38069             }
38070             if(cm.config[i].align){
38071                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38072                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38073             }
38074         }*/
38075         
38076         this.updateHeaderSortState();
38077
38078         this.beforeInitialResize();
38079         this.layout(true);
38080
38081         // two part rendering gives faster view to the user
38082         this.renderPhase2.defer(1, this);
38083     },
38084
38085     renderPhase2 : function(){
38086         // render the rows now
38087         this.refresh();
38088         if(this.grid.autoSizeColumns){
38089             this.autoSizeColumns();
38090         }
38091     },
38092
38093     beforeInitialResize : function(){
38094
38095     },
38096
38097     onColumnSplitterMoved : function(i, w){
38098         this.userResized = true;
38099         var cm = this.grid.colModel;
38100         cm.setColumnWidth(i, w, true);
38101         var cid = cm.getColumnId(i);
38102         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38103         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38104         this.updateSplitters();
38105         this.layout();
38106         this.grid.fireEvent("columnresize", i, w);
38107     },
38108
38109     syncRowHeights : function(startIndex, endIndex){
38110         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38111             startIndex = startIndex || 0;
38112             var mrows = this.getBodyTable().rows;
38113             var lrows = this.getLockedTable().rows;
38114             var len = mrows.length-1;
38115             endIndex = Math.min(endIndex || len, len);
38116             for(var i = startIndex; i <= endIndex; i++){
38117                 var m = mrows[i], l = lrows[i];
38118                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38119                 m.style.height = l.style.height = h + "px";
38120             }
38121         }
38122     },
38123
38124     layout : function(initialRender, is2ndPass){
38125         var g = this.grid;
38126         var auto = g.autoHeight;
38127         var scrollOffset = 16;
38128         var c = g.getGridEl(), cm = this.cm,
38129                 expandCol = g.autoExpandColumn,
38130                 gv = this;
38131         //c.beginMeasure();
38132
38133         if(!c.dom.offsetWidth){ // display:none?
38134             if(initialRender){
38135                 this.lockedWrap.show();
38136                 this.mainWrap.show();
38137             }
38138             return;
38139         }
38140
38141         var hasLock = this.cm.isLocked(0);
38142
38143         var tbh = this.headerPanel.getHeight();
38144         var bbh = this.footerPanel.getHeight();
38145
38146         if(auto){
38147             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38148             var newHeight = ch + c.getBorderWidth("tb");
38149             if(g.maxHeight){
38150                 newHeight = Math.min(g.maxHeight, newHeight);
38151             }
38152             c.setHeight(newHeight);
38153         }
38154
38155         if(g.autoWidth){
38156             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38157         }
38158
38159         var s = this.scroller;
38160
38161         var csize = c.getSize(true);
38162
38163         this.el.setSize(csize.width, csize.height);
38164
38165         this.headerPanel.setWidth(csize.width);
38166         this.footerPanel.setWidth(csize.width);
38167
38168         var hdHeight = this.mainHd.getHeight();
38169         var vw = csize.width;
38170         var vh = csize.height - (tbh + bbh);
38171
38172         s.setSize(vw, vh);
38173
38174         var bt = this.getBodyTable();
38175         var ltWidth = hasLock ?
38176                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38177
38178         var scrollHeight = bt.offsetHeight;
38179         var scrollWidth = ltWidth + bt.offsetWidth;
38180         var vscroll = false, hscroll = false;
38181
38182         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38183
38184         var lw = this.lockedWrap, mw = this.mainWrap;
38185         var lb = this.lockedBody, mb = this.mainBody;
38186
38187         setTimeout(function(){
38188             var t = s.dom.offsetTop;
38189             var w = s.dom.clientWidth,
38190                 h = s.dom.clientHeight;
38191
38192             lw.setTop(t);
38193             lw.setSize(ltWidth, h);
38194
38195             mw.setLeftTop(ltWidth, t);
38196             mw.setSize(w-ltWidth, h);
38197
38198             lb.setHeight(h-hdHeight);
38199             mb.setHeight(h-hdHeight);
38200
38201             if(is2ndPass !== true && !gv.userResized && expandCol){
38202                 // high speed resize without full column calculation
38203                 
38204                 var ci = cm.getIndexById(expandCol);
38205                 if (ci < 0) {
38206                     ci = cm.findColumnIndex(expandCol);
38207                 }
38208                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38209                 var expandId = cm.getColumnId(ci);
38210                 var  tw = cm.getTotalWidth(false);
38211                 var currentWidth = cm.getColumnWidth(ci);
38212                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38213                 if(currentWidth != cw){
38214                     cm.setColumnWidth(ci, cw, true);
38215                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38216                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38217                     gv.updateSplitters();
38218                     gv.layout(false, true);
38219                 }
38220             }
38221
38222             if(initialRender){
38223                 lw.show();
38224                 mw.show();
38225             }
38226             //c.endMeasure();
38227         }, 10);
38228     },
38229
38230     onWindowResize : function(){
38231         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38232             return;
38233         }
38234         this.layout();
38235     },
38236
38237     appendFooter : function(parentEl){
38238         return null;
38239     },
38240
38241     sortAscText : "Sort Ascending",
38242     sortDescText : "Sort Descending",
38243     lockText : "Lock Column",
38244     unlockText : "Unlock Column",
38245     columnsText : "Columns",
38246  
38247     columnsWiderText : "Wider",
38248     columnsNarrowText : "Thinner"
38249 });
38250
38251
38252 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38253     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38254     this.proxy.el.addClass('x-grid3-col-dd');
38255 };
38256
38257 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38258     handleMouseDown : function(e){
38259
38260     },
38261
38262     callHandleMouseDown : function(e){
38263         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38264     }
38265 });
38266 /*
38267  * Based on:
38268  * Ext JS Library 1.1.1
38269  * Copyright(c) 2006-2007, Ext JS, LLC.
38270  *
38271  * Originally Released Under LGPL - original licence link has changed is not relivant.
38272  *
38273  * Fork - LGPL
38274  * <script type="text/javascript">
38275  */
38276  
38277 // private
38278 // This is a support class used internally by the Grid components
38279 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38280     this.grid = grid;
38281     this.view = grid.getView();
38282     this.proxy = this.view.resizeProxy;
38283     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38284         "gridSplitters" + this.grid.getGridEl().id, {
38285         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38286     });
38287     this.setHandleElId(Roo.id(hd));
38288     this.setOuterHandleElId(Roo.id(hd2));
38289     this.scroll = false;
38290 };
38291 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38292     fly: Roo.Element.fly,
38293
38294     b4StartDrag : function(x, y){
38295         this.view.headersDisabled = true;
38296         this.proxy.setHeight(this.view.mainWrap.getHeight());
38297         var w = this.cm.getColumnWidth(this.cellIndex);
38298         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38299         this.resetConstraints();
38300         this.setXConstraint(minw, 1000);
38301         this.setYConstraint(0, 0);
38302         this.minX = x - minw;
38303         this.maxX = x + 1000;
38304         this.startPos = x;
38305         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38306     },
38307
38308
38309     handleMouseDown : function(e){
38310         ev = Roo.EventObject.setEvent(e);
38311         var t = this.fly(ev.getTarget());
38312         if(t.hasClass("x-grid-split")){
38313             this.cellIndex = this.view.getCellIndex(t.dom);
38314             this.split = t.dom;
38315             this.cm = this.grid.colModel;
38316             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38317                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38318             }
38319         }
38320     },
38321
38322     endDrag : function(e){
38323         this.view.headersDisabled = false;
38324         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38325         var diff = endX - this.startPos;
38326         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38327     },
38328
38329     autoOffset : function(){
38330         this.setDelta(0,0);
38331     }
38332 });/*
38333  * Based on:
38334  * Ext JS Library 1.1.1
38335  * Copyright(c) 2006-2007, Ext JS, LLC.
38336  *
38337  * Originally Released Under LGPL - original licence link has changed is not relivant.
38338  *
38339  * Fork - LGPL
38340  * <script type="text/javascript">
38341  */
38342  
38343 // private
38344 // This is a support class used internally by the Grid components
38345 Roo.grid.GridDragZone = function(grid, config){
38346     this.view = grid.getView();
38347     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38348     if(this.view.lockedBody){
38349         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38350         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38351     }
38352     this.scroll = false;
38353     this.grid = grid;
38354     this.ddel = document.createElement('div');
38355     this.ddel.className = 'x-grid-dd-wrap';
38356 };
38357
38358 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38359     ddGroup : "GridDD",
38360
38361     getDragData : function(e){
38362         var t = Roo.lib.Event.getTarget(e);
38363         var rowIndex = this.view.findRowIndex(t);
38364         var sm = this.grid.selModel;
38365             
38366         //Roo.log(rowIndex);
38367         
38368         if (sm.getSelectedCell) {
38369             // cell selection..
38370             if (!sm.getSelectedCell()) {
38371                 return false;
38372             }
38373             if (rowIndex != sm.getSelectedCell()[0]) {
38374                 return false;
38375             }
38376         
38377         }
38378         
38379         if(rowIndex !== false){
38380             
38381             // if editorgrid.. 
38382             
38383             
38384             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38385                
38386             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38387               //  
38388             //}
38389             if (e.hasModifier()){
38390                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38391             }
38392             
38393             Roo.log("getDragData");
38394             
38395             return {
38396                 grid: this.grid,
38397                 ddel: this.ddel,
38398                 rowIndex: rowIndex,
38399                 selections:sm.getSelections ? sm.getSelections() : (
38400                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38401                 )
38402             };
38403         }
38404         return false;
38405     },
38406
38407     onInitDrag : function(e){
38408         var data = this.dragData;
38409         this.ddel.innerHTML = this.grid.getDragDropText();
38410         this.proxy.update(this.ddel);
38411         // fire start drag?
38412     },
38413
38414     afterRepair : function(){
38415         this.dragging = false;
38416     },
38417
38418     getRepairXY : function(e, data){
38419         return false;
38420     },
38421
38422     onEndDrag : function(data, e){
38423         // fire end drag?
38424     },
38425
38426     onValidDrop : function(dd, e, id){
38427         // fire drag drop?
38428         this.hideProxy();
38429     },
38430
38431     beforeInvalidDrop : function(e, id){
38432
38433     }
38434 });/*
38435  * Based on:
38436  * Ext JS Library 1.1.1
38437  * Copyright(c) 2006-2007, Ext JS, LLC.
38438  *
38439  * Originally Released Under LGPL - original licence link has changed is not relivant.
38440  *
38441  * Fork - LGPL
38442  * <script type="text/javascript">
38443  */
38444  
38445
38446 /**
38447  * @class Roo.grid.ColumnModel
38448  * @extends Roo.util.Observable
38449  * This is the default implementation of a ColumnModel used by the Grid. It defines
38450  * the columns in the grid.
38451  * <br>Usage:<br>
38452  <pre><code>
38453  var colModel = new Roo.grid.ColumnModel([
38454         {header: "Ticker", width: 60, sortable: true, locked: true},
38455         {header: "Company Name", width: 150, sortable: true},
38456         {header: "Market Cap.", width: 100, sortable: true},
38457         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38458         {header: "Employees", width: 100, sortable: true, resizable: false}
38459  ]);
38460  </code></pre>
38461  * <p>
38462  
38463  * The config options listed for this class are options which may appear in each
38464  * individual column definition.
38465  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38466  * @constructor
38467  * @param {Object} config An Array of column config objects. See this class's
38468  * config objects for details.
38469 */
38470 Roo.grid.ColumnModel = function(config){
38471         /**
38472      * The config passed into the constructor
38473      */
38474     this.config = config;
38475     this.lookup = {};
38476
38477     // if no id, create one
38478     // if the column does not have a dataIndex mapping,
38479     // map it to the order it is in the config
38480     for(var i = 0, len = config.length; i < len; i++){
38481         var c = config[i];
38482         if(typeof c.dataIndex == "undefined"){
38483             c.dataIndex = i;
38484         }
38485         if(typeof c.renderer == "string"){
38486             c.renderer = Roo.util.Format[c.renderer];
38487         }
38488         if(typeof c.id == "undefined"){
38489             c.id = Roo.id();
38490         }
38491         if(c.editor && c.editor.xtype){
38492             c.editor  = Roo.factory(c.editor, Roo.grid);
38493         }
38494         if(c.editor && c.editor.isFormField){
38495             c.editor = new Roo.grid.GridEditor(c.editor);
38496         }
38497         this.lookup[c.id] = c;
38498     }
38499
38500     /**
38501      * The width of columns which have no width specified (defaults to 100)
38502      * @type Number
38503      */
38504     this.defaultWidth = 100;
38505
38506     /**
38507      * Default sortable of columns which have no sortable specified (defaults to false)
38508      * @type Boolean
38509      */
38510     this.defaultSortable = false;
38511
38512     this.addEvents({
38513         /**
38514              * @event widthchange
38515              * Fires when the width of a column changes.
38516              * @param {ColumnModel} this
38517              * @param {Number} columnIndex The column index
38518              * @param {Number} newWidth The new width
38519              */
38520             "widthchange": true,
38521         /**
38522              * @event headerchange
38523              * Fires when the text of a header changes.
38524              * @param {ColumnModel} this
38525              * @param {Number} columnIndex The column index
38526              * @param {Number} newText The new header text
38527              */
38528             "headerchange": true,
38529         /**
38530              * @event hiddenchange
38531              * Fires when a column is hidden or "unhidden".
38532              * @param {ColumnModel} this
38533              * @param {Number} columnIndex The column index
38534              * @param {Boolean} hidden true if hidden, false otherwise
38535              */
38536             "hiddenchange": true,
38537             /**
38538          * @event columnmoved
38539          * Fires when a column is moved.
38540          * @param {ColumnModel} this
38541          * @param {Number} oldIndex
38542          * @param {Number} newIndex
38543          */
38544         "columnmoved" : true,
38545         /**
38546          * @event columlockchange
38547          * Fires when a column's locked state is changed
38548          * @param {ColumnModel} this
38549          * @param {Number} colIndex
38550          * @param {Boolean} locked true if locked
38551          */
38552         "columnlockchange" : true
38553     });
38554     Roo.grid.ColumnModel.superclass.constructor.call(this);
38555 };
38556 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38557     /**
38558      * @cfg {String} header The header text to display in the Grid view.
38559      */
38560     /**
38561      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38562      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38563      * specified, the column's index is used as an index into the Record's data Array.
38564      */
38565     /**
38566      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38567      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38568      */
38569     /**
38570      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38571      * Defaults to the value of the {@link #defaultSortable} property.
38572      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38573      */
38574     /**
38575      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38576      */
38577     /**
38578      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38579      */
38580     /**
38581      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38582      */
38583     /**
38584      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38585      */
38586     /**
38587      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38588      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38589      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38590      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38591      */
38592        /**
38593      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38594      */
38595     /**
38596      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38597      */
38598     /**
38599      * @cfg {String} cursor (Optional)
38600      */
38601     /**
38602      * Returns the id of the column at the specified index.
38603      * @param {Number} index The column index
38604      * @return {String} the id
38605      */
38606     getColumnId : function(index){
38607         return this.config[index].id;
38608     },
38609
38610     /**
38611      * Returns the column for a specified id.
38612      * @param {String} id The column id
38613      * @return {Object} the column
38614      */
38615     getColumnById : function(id){
38616         return this.lookup[id];
38617     },
38618
38619     
38620     /**
38621      * Returns the column for a specified dataIndex.
38622      * @param {String} dataIndex The column dataIndex
38623      * @return {Object|Boolean} the column or false if not found
38624      */
38625     getColumnByDataIndex: function(dataIndex){
38626         var index = this.findColumnIndex(dataIndex);
38627         return index > -1 ? this.config[index] : false;
38628     },
38629     
38630     /**
38631      * Returns the index for a specified column id.
38632      * @param {String} id The column id
38633      * @return {Number} the index, or -1 if not found
38634      */
38635     getIndexById : function(id){
38636         for(var i = 0, len = this.config.length; i < len; i++){
38637             if(this.config[i].id == id){
38638                 return i;
38639             }
38640         }
38641         return -1;
38642     },
38643     
38644     /**
38645      * Returns the index for a specified column dataIndex.
38646      * @param {String} dataIndex The column dataIndex
38647      * @return {Number} the index, or -1 if not found
38648      */
38649     
38650     findColumnIndex : function(dataIndex){
38651         for(var i = 0, len = this.config.length; i < len; i++){
38652             if(this.config[i].dataIndex == dataIndex){
38653                 return i;
38654             }
38655         }
38656         return -1;
38657     },
38658     
38659     
38660     moveColumn : function(oldIndex, newIndex){
38661         var c = this.config[oldIndex];
38662         this.config.splice(oldIndex, 1);
38663         this.config.splice(newIndex, 0, c);
38664         this.dataMap = null;
38665         this.fireEvent("columnmoved", this, oldIndex, newIndex);
38666     },
38667
38668     isLocked : function(colIndex){
38669         return this.config[colIndex].locked === true;
38670     },
38671
38672     setLocked : function(colIndex, value, suppressEvent){
38673         if(this.isLocked(colIndex) == value){
38674             return;
38675         }
38676         this.config[colIndex].locked = value;
38677         if(!suppressEvent){
38678             this.fireEvent("columnlockchange", this, colIndex, value);
38679         }
38680     },
38681
38682     getTotalLockedWidth : function(){
38683         var totalWidth = 0;
38684         for(var i = 0; i < this.config.length; i++){
38685             if(this.isLocked(i) && !this.isHidden(i)){
38686                 this.totalWidth += this.getColumnWidth(i);
38687             }
38688         }
38689         return totalWidth;
38690     },
38691
38692     getLockedCount : function(){
38693         for(var i = 0, len = this.config.length; i < len; i++){
38694             if(!this.isLocked(i)){
38695                 return i;
38696             }
38697         }
38698     },
38699
38700     /**
38701      * Returns the number of columns.
38702      * @return {Number}
38703      */
38704     getColumnCount : function(visibleOnly){
38705         if(visibleOnly === true){
38706             var c = 0;
38707             for(var i = 0, len = this.config.length; i < len; i++){
38708                 if(!this.isHidden(i)){
38709                     c++;
38710                 }
38711             }
38712             return c;
38713         }
38714         return this.config.length;
38715     },
38716
38717     /**
38718      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
38719      * @param {Function} fn
38720      * @param {Object} scope (optional)
38721      * @return {Array} result
38722      */
38723     getColumnsBy : function(fn, scope){
38724         var r = [];
38725         for(var i = 0, len = this.config.length; i < len; i++){
38726             var c = this.config[i];
38727             if(fn.call(scope||this, c, i) === true){
38728                 r[r.length] = c;
38729             }
38730         }
38731         return r;
38732     },
38733
38734     /**
38735      * Returns true if the specified column is sortable.
38736      * @param {Number} col The column index
38737      * @return {Boolean}
38738      */
38739     isSortable : function(col){
38740         if(typeof this.config[col].sortable == "undefined"){
38741             return this.defaultSortable;
38742         }
38743         return this.config[col].sortable;
38744     },
38745
38746     /**
38747      * Returns the rendering (formatting) function defined for the column.
38748      * @param {Number} col The column index.
38749      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
38750      */
38751     getRenderer : function(col){
38752         if(!this.config[col].renderer){
38753             return Roo.grid.ColumnModel.defaultRenderer;
38754         }
38755         return this.config[col].renderer;
38756     },
38757
38758     /**
38759      * Sets the rendering (formatting) function for a column.
38760      * @param {Number} col The column index
38761      * @param {Function} fn The function to use to process the cell's raw data
38762      * to return HTML markup for the grid view. The render function is called with
38763      * the following parameters:<ul>
38764      * <li>Data value.</li>
38765      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
38766      * <li>css A CSS style string to apply to the table cell.</li>
38767      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
38768      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
38769      * <li>Row index</li>
38770      * <li>Column index</li>
38771      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
38772      */
38773     setRenderer : function(col, fn){
38774         this.config[col].renderer = fn;
38775     },
38776
38777     /**
38778      * Returns the width for the specified column.
38779      * @param {Number} col The column index
38780      * @return {Number}
38781      */
38782     getColumnWidth : function(col){
38783         return this.config[col].width * 1 || this.defaultWidth;
38784     },
38785
38786     /**
38787      * Sets the width for a column.
38788      * @param {Number} col The column index
38789      * @param {Number} width The new width
38790      */
38791     setColumnWidth : function(col, width, suppressEvent){
38792         this.config[col].width = width;
38793         this.totalWidth = null;
38794         if(!suppressEvent){
38795              this.fireEvent("widthchange", this, col, width);
38796         }
38797     },
38798
38799     /**
38800      * Returns the total width of all columns.
38801      * @param {Boolean} includeHidden True to include hidden column widths
38802      * @return {Number}
38803      */
38804     getTotalWidth : function(includeHidden){
38805         if(!this.totalWidth){
38806             this.totalWidth = 0;
38807             for(var i = 0, len = this.config.length; i < len; i++){
38808                 if(includeHidden || !this.isHidden(i)){
38809                     this.totalWidth += this.getColumnWidth(i);
38810                 }
38811             }
38812         }
38813         return this.totalWidth;
38814     },
38815
38816     /**
38817      * Returns the header for the specified column.
38818      * @param {Number} col The column index
38819      * @return {String}
38820      */
38821     getColumnHeader : function(col){
38822         return this.config[col].header;
38823     },
38824
38825     /**
38826      * Sets the header for a column.
38827      * @param {Number} col The column index
38828      * @param {String} header The new header
38829      */
38830     setColumnHeader : function(col, header){
38831         this.config[col].header = header;
38832         this.fireEvent("headerchange", this, col, header);
38833     },
38834
38835     /**
38836      * Returns the tooltip for the specified column.
38837      * @param {Number} col The column index
38838      * @return {String}
38839      */
38840     getColumnTooltip : function(col){
38841             return this.config[col].tooltip;
38842     },
38843     /**
38844      * Sets the tooltip for a column.
38845      * @param {Number} col The column index
38846      * @param {String} tooltip The new tooltip
38847      */
38848     setColumnTooltip : function(col, tooltip){
38849             this.config[col].tooltip = tooltip;
38850     },
38851
38852     /**
38853      * Returns the dataIndex for the specified column.
38854      * @param {Number} col The column index
38855      * @return {Number}
38856      */
38857     getDataIndex : function(col){
38858         return this.config[col].dataIndex;
38859     },
38860
38861     /**
38862      * Sets the dataIndex for a column.
38863      * @param {Number} col The column index
38864      * @param {Number} dataIndex The new dataIndex
38865      */
38866     setDataIndex : function(col, dataIndex){
38867         this.config[col].dataIndex = dataIndex;
38868     },
38869
38870     
38871     
38872     /**
38873      * Returns true if the cell is editable.
38874      * @param {Number} colIndex The column index
38875      * @param {Number} rowIndex The row index
38876      * @return {Boolean}
38877      */
38878     isCellEditable : function(colIndex, rowIndex){
38879         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38880     },
38881
38882     /**
38883      * Returns the editor defined for the cell/column.
38884      * return false or null to disable editing.
38885      * @param {Number} colIndex The column index
38886      * @param {Number} rowIndex The row index
38887      * @return {Object}
38888      */
38889     getCellEditor : function(colIndex, rowIndex){
38890         return this.config[colIndex].editor;
38891     },
38892
38893     /**
38894      * Sets if a column is editable.
38895      * @param {Number} col The column index
38896      * @param {Boolean} editable True if the column is editable
38897      */
38898     setEditable : function(col, editable){
38899         this.config[col].editable = editable;
38900     },
38901
38902
38903     /**
38904      * Returns true if the column is hidden.
38905      * @param {Number} colIndex The column index
38906      * @return {Boolean}
38907      */
38908     isHidden : function(colIndex){
38909         return this.config[colIndex].hidden;
38910     },
38911
38912
38913     /**
38914      * Returns true if the column width cannot be changed
38915      */
38916     isFixed : function(colIndex){
38917         return this.config[colIndex].fixed;
38918     },
38919
38920     /**
38921      * Returns true if the column can be resized
38922      * @return {Boolean}
38923      */
38924     isResizable : function(colIndex){
38925         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38926     },
38927     /**
38928      * Sets if a column is hidden.
38929      * @param {Number} colIndex The column index
38930      * @param {Boolean} hidden True if the column is hidden
38931      */
38932     setHidden : function(colIndex, hidden){
38933         this.config[colIndex].hidden = hidden;
38934         this.totalWidth = null;
38935         this.fireEvent("hiddenchange", this, colIndex, hidden);
38936     },
38937
38938     /**
38939      * Sets the editor for a column.
38940      * @param {Number} col The column index
38941      * @param {Object} editor The editor object
38942      */
38943     setEditor : function(col, editor){
38944         this.config[col].editor = editor;
38945     }
38946 });
38947
38948 Roo.grid.ColumnModel.defaultRenderer = function(value){
38949         if(typeof value == "string" && value.length < 1){
38950             return "&#160;";
38951         }
38952         return value;
38953 };
38954
38955 // Alias for backwards compatibility
38956 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38957 /*
38958  * Based on:
38959  * Ext JS Library 1.1.1
38960  * Copyright(c) 2006-2007, Ext JS, LLC.
38961  *
38962  * Originally Released Under LGPL - original licence link has changed is not relivant.
38963  *
38964  * Fork - LGPL
38965  * <script type="text/javascript">
38966  */
38967
38968 /**
38969  * @class Roo.grid.AbstractSelectionModel
38970  * @extends Roo.util.Observable
38971  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38972  * implemented by descendant classes.  This class should not be directly instantiated.
38973  * @constructor
38974  */
38975 Roo.grid.AbstractSelectionModel = function(){
38976     this.locked = false;
38977     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38978 };
38979
38980 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38981     /** @ignore Called by the grid automatically. Do not call directly. */
38982     init : function(grid){
38983         this.grid = grid;
38984         this.initEvents();
38985     },
38986
38987     /**
38988      * Locks the selections.
38989      */
38990     lock : function(){
38991         this.locked = true;
38992     },
38993
38994     /**
38995      * Unlocks the selections.
38996      */
38997     unlock : function(){
38998         this.locked = false;
38999     },
39000
39001     /**
39002      * Returns true if the selections are locked.
39003      * @return {Boolean}
39004      */
39005     isLocked : function(){
39006         return this.locked;
39007     }
39008 });/*
39009  * Based on:
39010  * Ext JS Library 1.1.1
39011  * Copyright(c) 2006-2007, Ext JS, LLC.
39012  *
39013  * Originally Released Under LGPL - original licence link has changed is not relivant.
39014  *
39015  * Fork - LGPL
39016  * <script type="text/javascript">
39017  */
39018 /**
39019  * @extends Roo.grid.AbstractSelectionModel
39020  * @class Roo.grid.RowSelectionModel
39021  * The default SelectionModel used by {@link Roo.grid.Grid}.
39022  * It supports multiple selections and keyboard selection/navigation. 
39023  * @constructor
39024  * @param {Object} config
39025  */
39026 Roo.grid.RowSelectionModel = function(config){
39027     Roo.apply(this, config);
39028     this.selections = new Roo.util.MixedCollection(false, function(o){
39029         return o.id;
39030     });
39031
39032     this.last = false;
39033     this.lastActive = false;
39034
39035     this.addEvents({
39036         /**
39037              * @event selectionchange
39038              * Fires when the selection changes
39039              * @param {SelectionModel} this
39040              */
39041             "selectionchange" : true,
39042         /**
39043              * @event afterselectionchange
39044              * Fires after the selection changes (eg. by key press or clicking)
39045              * @param {SelectionModel} this
39046              */
39047             "afterselectionchange" : true,
39048         /**
39049              * @event beforerowselect
39050              * Fires when a row is selected being selected, return false to cancel.
39051              * @param {SelectionModel} this
39052              * @param {Number} rowIndex The selected index
39053              * @param {Boolean} keepExisting False if other selections will be cleared
39054              */
39055             "beforerowselect" : true,
39056         /**
39057              * @event rowselect
39058              * Fires when a row is selected.
39059              * @param {SelectionModel} this
39060              * @param {Number} rowIndex The selected index
39061              * @param {Roo.data.Record} r The record
39062              */
39063             "rowselect" : true,
39064         /**
39065              * @event rowdeselect
39066              * Fires when a row is deselected.
39067              * @param {SelectionModel} this
39068              * @param {Number} rowIndex The selected index
39069              */
39070         "rowdeselect" : true
39071     });
39072     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39073     this.locked = false;
39074 };
39075
39076 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39077     /**
39078      * @cfg {Boolean} singleSelect
39079      * True to allow selection of only one row at a time (defaults to false)
39080      */
39081     singleSelect : false,
39082
39083     // private
39084     initEvents : function(){
39085
39086         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39087             this.grid.on("mousedown", this.handleMouseDown, this);
39088         }else{ // allow click to work like normal
39089             this.grid.on("rowclick", this.handleDragableRowClick, this);
39090         }
39091
39092         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39093             "up" : function(e){
39094                 if(!e.shiftKey){
39095                     this.selectPrevious(e.shiftKey);
39096                 }else if(this.last !== false && this.lastActive !== false){
39097                     var last = this.last;
39098                     this.selectRange(this.last,  this.lastActive-1);
39099                     this.grid.getView().focusRow(this.lastActive);
39100                     if(last !== false){
39101                         this.last = last;
39102                     }
39103                 }else{
39104                     this.selectFirstRow();
39105                 }
39106                 this.fireEvent("afterselectionchange", this);
39107             },
39108             "down" : function(e){
39109                 if(!e.shiftKey){
39110                     this.selectNext(e.shiftKey);
39111                 }else if(this.last !== false && this.lastActive !== false){
39112                     var last = this.last;
39113                     this.selectRange(this.last,  this.lastActive+1);
39114                     this.grid.getView().focusRow(this.lastActive);
39115                     if(last !== false){
39116                         this.last = last;
39117                     }
39118                 }else{
39119                     this.selectFirstRow();
39120                 }
39121                 this.fireEvent("afterselectionchange", this);
39122             },
39123             scope: this
39124         });
39125
39126         var view = this.grid.view;
39127         view.on("refresh", this.onRefresh, this);
39128         view.on("rowupdated", this.onRowUpdated, this);
39129         view.on("rowremoved", this.onRemove, this);
39130     },
39131
39132     // private
39133     onRefresh : function(){
39134         var ds = this.grid.dataSource, i, v = this.grid.view;
39135         var s = this.selections;
39136         s.each(function(r){
39137             if((i = ds.indexOfId(r.id)) != -1){
39138                 v.onRowSelect(i);
39139             }else{
39140                 s.remove(r);
39141             }
39142         });
39143     },
39144
39145     // private
39146     onRemove : function(v, index, r){
39147         this.selections.remove(r);
39148     },
39149
39150     // private
39151     onRowUpdated : function(v, index, r){
39152         if(this.isSelected(r)){
39153             v.onRowSelect(index);
39154         }
39155     },
39156
39157     /**
39158      * Select records.
39159      * @param {Array} records The records to select
39160      * @param {Boolean} keepExisting (optional) True to keep existing selections
39161      */
39162     selectRecords : function(records, keepExisting){
39163         if(!keepExisting){
39164             this.clearSelections();
39165         }
39166         var ds = this.grid.dataSource;
39167         for(var i = 0, len = records.length; i < len; i++){
39168             this.selectRow(ds.indexOf(records[i]), true);
39169         }
39170     },
39171
39172     /**
39173      * Gets the number of selected rows.
39174      * @return {Number}
39175      */
39176     getCount : function(){
39177         return this.selections.length;
39178     },
39179
39180     /**
39181      * Selects the first row in the grid.
39182      */
39183     selectFirstRow : function(){
39184         this.selectRow(0);
39185     },
39186
39187     /**
39188      * Select the last row.
39189      * @param {Boolean} keepExisting (optional) True to keep existing selections
39190      */
39191     selectLastRow : function(keepExisting){
39192         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39193     },
39194
39195     /**
39196      * Selects the row immediately following the last selected row.
39197      * @param {Boolean} keepExisting (optional) True to keep existing selections
39198      */
39199     selectNext : function(keepExisting){
39200         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39201             this.selectRow(this.last+1, keepExisting);
39202             this.grid.getView().focusRow(this.last);
39203         }
39204     },
39205
39206     /**
39207      * Selects the row that precedes the last selected row.
39208      * @param {Boolean} keepExisting (optional) True to keep existing selections
39209      */
39210     selectPrevious : function(keepExisting){
39211         if(this.last){
39212             this.selectRow(this.last-1, keepExisting);
39213             this.grid.getView().focusRow(this.last);
39214         }
39215     },
39216
39217     /**
39218      * Returns the selected records
39219      * @return {Array} Array of selected records
39220      */
39221     getSelections : function(){
39222         return [].concat(this.selections.items);
39223     },
39224
39225     /**
39226      * Returns the first selected record.
39227      * @return {Record}
39228      */
39229     getSelected : function(){
39230         return this.selections.itemAt(0);
39231     },
39232
39233
39234     /**
39235      * Clears all selections.
39236      */
39237     clearSelections : function(fast){
39238         if(this.locked) return;
39239         if(fast !== true){
39240             var ds = this.grid.dataSource;
39241             var s = this.selections;
39242             s.each(function(r){
39243                 this.deselectRow(ds.indexOfId(r.id));
39244             }, this);
39245             s.clear();
39246         }else{
39247             this.selections.clear();
39248         }
39249         this.last = false;
39250     },
39251
39252
39253     /**
39254      * Selects all rows.
39255      */
39256     selectAll : function(){
39257         if(this.locked) return;
39258         this.selections.clear();
39259         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39260             this.selectRow(i, true);
39261         }
39262     },
39263
39264     /**
39265      * Returns True if there is a selection.
39266      * @return {Boolean}
39267      */
39268     hasSelection : function(){
39269         return this.selections.length > 0;
39270     },
39271
39272     /**
39273      * Returns True if the specified row is selected.
39274      * @param {Number/Record} record The record or index of the record to check
39275      * @return {Boolean}
39276      */
39277     isSelected : function(index){
39278         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39279         return (r && this.selections.key(r.id) ? true : false);
39280     },
39281
39282     /**
39283      * Returns True if the specified record id is selected.
39284      * @param {String} id The id of record to check
39285      * @return {Boolean}
39286      */
39287     isIdSelected : function(id){
39288         return (this.selections.key(id) ? true : false);
39289     },
39290
39291     // private
39292     handleMouseDown : function(e, t){
39293         var view = this.grid.getView(), rowIndex;
39294         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39295             return;
39296         };
39297         if(e.shiftKey && this.last !== false){
39298             var last = this.last;
39299             this.selectRange(last, rowIndex, e.ctrlKey);
39300             this.last = last; // reset the last
39301             view.focusRow(rowIndex);
39302         }else{
39303             var isSelected = this.isSelected(rowIndex);
39304             if(e.button !== 0 && isSelected){
39305                 view.focusRow(rowIndex);
39306             }else if(e.ctrlKey && isSelected){
39307                 this.deselectRow(rowIndex);
39308             }else if(!isSelected){
39309                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39310                 view.focusRow(rowIndex);
39311             }
39312         }
39313         this.fireEvent("afterselectionchange", this);
39314     },
39315     // private
39316     handleDragableRowClick :  function(grid, rowIndex, e) 
39317     {
39318         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39319             this.selectRow(rowIndex, false);
39320             grid.view.focusRow(rowIndex);
39321              this.fireEvent("afterselectionchange", this);
39322         }
39323     },
39324     
39325     /**
39326      * Selects multiple rows.
39327      * @param {Array} rows Array of the indexes of the row to select
39328      * @param {Boolean} keepExisting (optional) True to keep existing selections
39329      */
39330     selectRows : function(rows, keepExisting){
39331         if(!keepExisting){
39332             this.clearSelections();
39333         }
39334         for(var i = 0, len = rows.length; i < len; i++){
39335             this.selectRow(rows[i], true);
39336         }
39337     },
39338
39339     /**
39340      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39341      * @param {Number} startRow The index of the first row in the range
39342      * @param {Number} endRow The index of the last row in the range
39343      * @param {Boolean} keepExisting (optional) True to retain existing selections
39344      */
39345     selectRange : function(startRow, endRow, keepExisting){
39346         if(this.locked) return;
39347         if(!keepExisting){
39348             this.clearSelections();
39349         }
39350         if(startRow <= endRow){
39351             for(var i = startRow; i <= endRow; i++){
39352                 this.selectRow(i, true);
39353             }
39354         }else{
39355             for(var i = startRow; i >= endRow; i--){
39356                 this.selectRow(i, true);
39357             }
39358         }
39359     },
39360
39361     /**
39362      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39363      * @param {Number} startRow The index of the first row in the range
39364      * @param {Number} endRow The index of the last row in the range
39365      */
39366     deselectRange : function(startRow, endRow, preventViewNotify){
39367         if(this.locked) return;
39368         for(var i = startRow; i <= endRow; i++){
39369             this.deselectRow(i, preventViewNotify);
39370         }
39371     },
39372
39373     /**
39374      * Selects a row.
39375      * @param {Number} row The index of the row to select
39376      * @param {Boolean} keepExisting (optional) True to keep existing selections
39377      */
39378     selectRow : function(index, keepExisting, preventViewNotify){
39379         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39380         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39381             if(!keepExisting || this.singleSelect){
39382                 this.clearSelections();
39383             }
39384             var r = this.grid.dataSource.getAt(index);
39385             this.selections.add(r);
39386             this.last = this.lastActive = index;
39387             if(!preventViewNotify){
39388                 this.grid.getView().onRowSelect(index);
39389             }
39390             this.fireEvent("rowselect", this, index, r);
39391             this.fireEvent("selectionchange", this);
39392         }
39393     },
39394
39395     /**
39396      * Deselects a row.
39397      * @param {Number} row The index of the row to deselect
39398      */
39399     deselectRow : function(index, preventViewNotify){
39400         if(this.locked) return;
39401         if(this.last == index){
39402             this.last = false;
39403         }
39404         if(this.lastActive == index){
39405             this.lastActive = false;
39406         }
39407         var r = this.grid.dataSource.getAt(index);
39408         this.selections.remove(r);
39409         if(!preventViewNotify){
39410             this.grid.getView().onRowDeselect(index);
39411         }
39412         this.fireEvent("rowdeselect", this, index);
39413         this.fireEvent("selectionchange", this);
39414     },
39415
39416     // private
39417     restoreLast : function(){
39418         if(this._last){
39419             this.last = this._last;
39420         }
39421     },
39422
39423     // private
39424     acceptsNav : function(row, col, cm){
39425         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39426     },
39427
39428     // private
39429     onEditorKey : function(field, e){
39430         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39431         if(k == e.TAB){
39432             e.stopEvent();
39433             ed.completeEdit();
39434             if(e.shiftKey){
39435                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39436             }else{
39437                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39438             }
39439         }else if(k == e.ENTER && !e.ctrlKey){
39440             e.stopEvent();
39441             ed.completeEdit();
39442             if(e.shiftKey){
39443                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39444             }else{
39445                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39446             }
39447         }else if(k == e.ESC){
39448             ed.cancelEdit();
39449         }
39450         if(newCell){
39451             g.startEditing(newCell[0], newCell[1]);
39452         }
39453     }
39454 });/*
39455  * Based on:
39456  * Ext JS Library 1.1.1
39457  * Copyright(c) 2006-2007, Ext JS, LLC.
39458  *
39459  * Originally Released Under LGPL - original licence link has changed is not relivant.
39460  *
39461  * Fork - LGPL
39462  * <script type="text/javascript">
39463  */
39464 /**
39465  * @class Roo.grid.CellSelectionModel
39466  * @extends Roo.grid.AbstractSelectionModel
39467  * This class provides the basic implementation for cell selection in a grid.
39468  * @constructor
39469  * @param {Object} config The object containing the configuration of this model.
39470  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39471  */
39472 Roo.grid.CellSelectionModel = function(config){
39473     Roo.apply(this, config);
39474
39475     this.selection = null;
39476
39477     this.addEvents({
39478         /**
39479              * @event beforerowselect
39480              * Fires before a cell is selected.
39481              * @param {SelectionModel} this
39482              * @param {Number} rowIndex The selected row index
39483              * @param {Number} colIndex The selected cell index
39484              */
39485             "beforecellselect" : true,
39486         /**
39487              * @event cellselect
39488              * Fires when a cell is selected.
39489              * @param {SelectionModel} this
39490              * @param {Number} rowIndex The selected row index
39491              * @param {Number} colIndex The selected cell index
39492              */
39493             "cellselect" : true,
39494         /**
39495              * @event selectionchange
39496              * Fires when the active selection changes.
39497              * @param {SelectionModel} this
39498              * @param {Object} selection null for no selection or an object (o) with two properties
39499                 <ul>
39500                 <li>o.record: the record object for the row the selection is in</li>
39501                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39502                 </ul>
39503              */
39504             "selectionchange" : true,
39505         /**
39506              * @event tabend
39507              * Fires when the tab (or enter) was pressed on the last editable cell
39508              * You can use this to trigger add new row.
39509              * @param {SelectionModel} this
39510              */
39511             "tabend" : true,
39512          /**
39513              * @event beforeeditnext
39514              * Fires before the next editable sell is made active
39515              * You can use this to skip to another cell or fire the tabend
39516              *    if you set cell to false
39517              * @param {Object} eventdata object : { cell : [ row, col ] } 
39518              */
39519             "beforeeditnext" : true
39520     });
39521     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39522 };
39523
39524 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39525     
39526     enter_is_tab: false,
39527
39528     /** @ignore */
39529     initEvents : function(){
39530         this.grid.on("mousedown", this.handleMouseDown, this);
39531         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39532         var view = this.grid.view;
39533         view.on("refresh", this.onViewChange, this);
39534         view.on("rowupdated", this.onRowUpdated, this);
39535         view.on("beforerowremoved", this.clearSelections, this);
39536         view.on("beforerowsinserted", this.clearSelections, this);
39537         if(this.grid.isEditor){
39538             this.grid.on("beforeedit", this.beforeEdit,  this);
39539         }
39540     },
39541
39542         //private
39543     beforeEdit : function(e){
39544         this.select(e.row, e.column, false, true, e.record);
39545     },
39546
39547         //private
39548     onRowUpdated : function(v, index, r){
39549         if(this.selection && this.selection.record == r){
39550             v.onCellSelect(index, this.selection.cell[1]);
39551         }
39552     },
39553
39554         //private
39555     onViewChange : function(){
39556         this.clearSelections(true);
39557     },
39558
39559         /**
39560          * Returns the currently selected cell,.
39561          * @return {Array} The selected cell (row, column) or null if none selected.
39562          */
39563     getSelectedCell : function(){
39564         return this.selection ? this.selection.cell : null;
39565     },
39566
39567     /**
39568      * Clears all selections.
39569      * @param {Boolean} true to prevent the gridview from being notified about the change.
39570      */
39571     clearSelections : function(preventNotify){
39572         var s = this.selection;
39573         if(s){
39574             if(preventNotify !== true){
39575                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39576             }
39577             this.selection = null;
39578             this.fireEvent("selectionchange", this, null);
39579         }
39580     },
39581
39582     /**
39583      * Returns true if there is a selection.
39584      * @return {Boolean}
39585      */
39586     hasSelection : function(){
39587         return this.selection ? true : false;
39588     },
39589
39590     /** @ignore */
39591     handleMouseDown : function(e, t){
39592         var v = this.grid.getView();
39593         if(this.isLocked()){
39594             return;
39595         };
39596         var row = v.findRowIndex(t);
39597         var cell = v.findCellIndex(t);
39598         if(row !== false && cell !== false){
39599             this.select(row, cell);
39600         }
39601     },
39602
39603     /**
39604      * Selects a cell.
39605      * @param {Number} rowIndex
39606      * @param {Number} collIndex
39607      */
39608     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39609         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39610             this.clearSelections();
39611             r = r || this.grid.dataSource.getAt(rowIndex);
39612             this.selection = {
39613                 record : r,
39614                 cell : [rowIndex, colIndex]
39615             };
39616             if(!preventViewNotify){
39617                 var v = this.grid.getView();
39618                 v.onCellSelect(rowIndex, colIndex);
39619                 if(preventFocus !== true){
39620                     v.focusCell(rowIndex, colIndex);
39621                 }
39622             }
39623             this.fireEvent("cellselect", this, rowIndex, colIndex);
39624             this.fireEvent("selectionchange", this, this.selection);
39625         }
39626     },
39627
39628         //private
39629     isSelectable : function(rowIndex, colIndex, cm){
39630         return !cm.isHidden(colIndex);
39631     },
39632
39633     /** @ignore */
39634     handleKeyDown : function(e){
39635         //Roo.log('Cell Sel Model handleKeyDown');
39636         if(!e.isNavKeyPress()){
39637             return;
39638         }
39639         var g = this.grid, s = this.selection;
39640         if(!s){
39641             e.stopEvent();
39642             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39643             if(cell){
39644                 this.select(cell[0], cell[1]);
39645             }
39646             return;
39647         }
39648         var sm = this;
39649         var walk = function(row, col, step){
39650             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39651         };
39652         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39653         var newCell;
39654
39655       
39656
39657         switch(k){
39658             case e.TAB:
39659                 // handled by onEditorKey
39660                 if (g.isEditor && g.editing) {
39661                     return;
39662                 }
39663                 if(e.shiftKey) {
39664                     newCell = walk(r, c-1, -1);
39665                 } else {
39666                     newCell = walk(r, c+1, 1);
39667                 }
39668                 break;
39669             
39670             case e.DOWN:
39671                newCell = walk(r+1, c, 1);
39672                 break;
39673             
39674             case e.UP:
39675                 newCell = walk(r-1, c, -1);
39676                 break;
39677             
39678             case e.RIGHT:
39679                 newCell = walk(r, c+1, 1);
39680                 break;
39681             
39682             case e.LEFT:
39683                 newCell = walk(r, c-1, -1);
39684                 break;
39685             
39686             case e.ENTER:
39687                 
39688                 if(g.isEditor && !g.editing){
39689                    g.startEditing(r, c);
39690                    e.stopEvent();
39691                    return;
39692                 }
39693                 
39694                 
39695              break;
39696         };
39697         if(newCell){
39698             this.select(newCell[0], newCell[1]);
39699             e.stopEvent();
39700             
39701         }
39702     },
39703
39704     acceptsNav : function(row, col, cm){
39705         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39706     },
39707     /**
39708      * Selects a cell.
39709      * @param {Number} field (not used) - as it's normally used as a listener
39710      * @param {Number} e - event - fake it by using
39711      *
39712      * var e = Roo.EventObjectImpl.prototype;
39713      * e.keyCode = e.TAB
39714      *
39715      * 
39716      */
39717     onEditorKey : function(field, e){
39718         
39719         var k = e.getKey(),
39720             newCell,
39721             g = this.grid,
39722             ed = g.activeEditor,
39723             forward = false;
39724         ///Roo.log('onEditorKey' + k);
39725         
39726         
39727         if (this.enter_is_tab && k == e.ENTER) {
39728             k = e.TAB;
39729         }
39730         
39731         if(k == e.TAB){
39732             if(e.shiftKey){
39733                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39734             }else{
39735                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39736                 forward = true;
39737             }
39738             
39739             e.stopEvent();
39740             
39741         } else if(k == e.ENTER &&  !e.ctrlKey){
39742             ed.completeEdit();
39743             e.stopEvent();
39744             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39745         
39746                 } else if(k == e.ESC){
39747             ed.cancelEdit();
39748         }
39749                 
39750         if (newCell) {
39751             var ecall = { cell : newCell, forward : forward };
39752             this.fireEvent('beforeeditnext', ecall );
39753             newCell = ecall.cell;
39754                         forward = ecall.forward;
39755         }
39756                 
39757         if(newCell){
39758             //Roo.log('next cell after edit');
39759             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
39760         } else if (forward) {
39761             // tabbed past last
39762             this.fireEvent.defer(100, this, ['tabend',this]);
39763         }
39764     }
39765 });/*
39766  * Based on:
39767  * Ext JS Library 1.1.1
39768  * Copyright(c) 2006-2007, Ext JS, LLC.
39769  *
39770  * Originally Released Under LGPL - original licence link has changed is not relivant.
39771  *
39772  * Fork - LGPL
39773  * <script type="text/javascript">
39774  */
39775  
39776 /**
39777  * @class Roo.grid.EditorGrid
39778  * @extends Roo.grid.Grid
39779  * Class for creating and editable grid.
39780  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
39781  * The container MUST have some type of size defined for the grid to fill. The container will be 
39782  * automatically set to position relative if it isn't already.
39783  * @param {Object} dataSource The data model to bind to
39784  * @param {Object} colModel The column model with info about this grid's columns
39785  */
39786 Roo.grid.EditorGrid = function(container, config){
39787     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
39788     this.getGridEl().addClass("xedit-grid");
39789
39790     if(!this.selModel){
39791         this.selModel = new Roo.grid.CellSelectionModel();
39792     }
39793
39794     this.activeEditor = null;
39795
39796         this.addEvents({
39797             /**
39798              * @event beforeedit
39799              * Fires before cell editing is triggered. The edit event object has the following properties <br />
39800              * <ul style="padding:5px;padding-left:16px;">
39801              * <li>grid - This grid</li>
39802              * <li>record - The record being edited</li>
39803              * <li>field - The field name being edited</li>
39804              * <li>value - The value for the field being edited.</li>
39805              * <li>row - The grid row index</li>
39806              * <li>column - The grid column index</li>
39807              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39808              * </ul>
39809              * @param {Object} e An edit event (see above for description)
39810              */
39811             "beforeedit" : true,
39812             /**
39813              * @event afteredit
39814              * Fires after a cell is edited. <br />
39815              * <ul style="padding:5px;padding-left:16px;">
39816              * <li>grid - This grid</li>
39817              * <li>record - The record being edited</li>
39818              * <li>field - The field name being edited</li>
39819              * <li>value - The value being set</li>
39820              * <li>originalValue - The original value for the field, before the edit.</li>
39821              * <li>row - The grid row index</li>
39822              * <li>column - The grid column index</li>
39823              * </ul>
39824              * @param {Object} e An edit event (see above for description)
39825              */
39826             "afteredit" : true,
39827             /**
39828              * @event validateedit
39829              * Fires after a cell is edited, but before the value is set in the record. 
39830          * You can use this to modify the value being set in the field, Return false
39831              * to cancel the change. The edit event object has the following properties <br />
39832              * <ul style="padding:5px;padding-left:16px;">
39833          * <li>editor - This editor</li>
39834              * <li>grid - This grid</li>
39835              * <li>record - The record being edited</li>
39836              * <li>field - The field name being edited</li>
39837              * <li>value - The value being set</li>
39838              * <li>originalValue - The original value for the field, before the edit.</li>
39839              * <li>row - The grid row index</li>
39840              * <li>column - The grid column index</li>
39841              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
39842              * </ul>
39843              * @param {Object} e An edit event (see above for description)
39844              */
39845             "validateedit" : true
39846         });
39847     this.on("bodyscroll", this.stopEditing,  this);
39848     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
39849 };
39850
39851 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
39852     /**
39853      * @cfg {Number} clicksToEdit
39854      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
39855      */
39856     clicksToEdit: 2,
39857
39858     // private
39859     isEditor : true,
39860     // private
39861     trackMouseOver: false, // causes very odd FF errors
39862
39863     onCellDblClick : function(g, row, col){
39864         this.startEditing(row, col);
39865     },
39866
39867     onEditComplete : function(ed, value, startValue){
39868         this.editing = false;
39869         this.activeEditor = null;
39870         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39871         var r = ed.record;
39872         var field = this.colModel.getDataIndex(ed.col);
39873         var e = {
39874             grid: this,
39875             record: r,
39876             field: field,
39877             originalValue: startValue,
39878             value: value,
39879             row: ed.row,
39880             column: ed.col,
39881             cancel:false,
39882             editor: ed
39883         };
39884         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39885         cell.show();
39886           
39887         if(String(value) !== String(startValue)){
39888             
39889             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39890                 r.set(field, e.value);
39891                 // if we are dealing with a combo box..
39892                 // then we also set the 'name' colum to be the displayField
39893                 if (ed.field.displayField && ed.field.name) {
39894                     r.set(ed.field.name, ed.field.el.dom.value);
39895                 }
39896                 
39897                 delete e.cancel; //?? why!!!
39898                 this.fireEvent("afteredit", e);
39899             }
39900         } else {
39901             this.fireEvent("afteredit", e); // always fire it!
39902         }
39903         this.view.focusCell(ed.row, ed.col);
39904     },
39905
39906     /**
39907      * Starts editing the specified for the specified row/column
39908      * @param {Number} rowIndex
39909      * @param {Number} colIndex
39910      */
39911     startEditing : function(row, col){
39912         this.stopEditing();
39913         if(this.colModel.isCellEditable(col, row)){
39914             this.view.ensureVisible(row, col, true);
39915           
39916             var r = this.dataSource.getAt(row);
39917             var field = this.colModel.getDataIndex(col);
39918             var cell = Roo.get(this.view.getCell(row,col));
39919             var e = {
39920                 grid: this,
39921                 record: r,
39922                 field: field,
39923                 value: r.data[field],
39924                 row: row,
39925                 column: col,
39926                 cancel:false 
39927             };
39928             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39929                 this.editing = true;
39930                 var ed = this.colModel.getCellEditor(col, row);
39931                 
39932                 if (!ed) {
39933                     return;
39934                 }
39935                 if(!ed.rendered){
39936                     ed.render(ed.parentEl || document.body);
39937                 }
39938                 ed.field.reset();
39939                
39940                 cell.hide();
39941                 
39942                 (function(){ // complex but required for focus issues in safari, ie and opera
39943                     ed.row = row;
39944                     ed.col = col;
39945                     ed.record = r;
39946                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39947                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39948                     this.activeEditor = ed;
39949                     var v = r.data[field];
39950                     ed.startEdit(this.view.getCell(row, col), v);
39951                     // combo's with 'displayField and name set
39952                     if (ed.field.displayField && ed.field.name) {
39953                         ed.field.el.dom.value = r.data[ed.field.name];
39954                     }
39955                     
39956                     
39957                 }).defer(50, this);
39958             }
39959         }
39960     },
39961         
39962     /**
39963      * Stops any active editing
39964      */
39965     stopEditing : function(){
39966         if(this.activeEditor){
39967             this.activeEditor.completeEdit();
39968         }
39969         this.activeEditor = null;
39970     },
39971         
39972          /**
39973      * Called to get grid's drag proxy text, by default returns this.ddText.
39974      * @return {String}
39975      */
39976     getDragDropText : function(){
39977         var count = this.selModel.getSelectedCell() ? 1 : 0;
39978         return String.format(this.ddText, count, count == 1 ? '' : 's');
39979     }
39980         
39981 });/*
39982  * Based on:
39983  * Ext JS Library 1.1.1
39984  * Copyright(c) 2006-2007, Ext JS, LLC.
39985  *
39986  * Originally Released Under LGPL - original licence link has changed is not relivant.
39987  *
39988  * Fork - LGPL
39989  * <script type="text/javascript">
39990  */
39991
39992 // private - not really -- you end up using it !
39993 // This is a support class used internally by the Grid components
39994
39995 /**
39996  * @class Roo.grid.GridEditor
39997  * @extends Roo.Editor
39998  * Class for creating and editable grid elements.
39999  * @param {Object} config any settings (must include field)
40000  */
40001 Roo.grid.GridEditor = function(field, config){
40002     if (!config && field.field) {
40003         config = field;
40004         field = Roo.factory(config.field, Roo.form);
40005     }
40006     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40007     field.monitorTab = false;
40008 };
40009
40010 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40011     
40012     /**
40013      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40014      */
40015     
40016     alignment: "tl-tl",
40017     autoSize: "width",
40018     hideEl : false,
40019     cls: "x-small-editor x-grid-editor",
40020     shim:false,
40021     shadow:"frame"
40022 });/*
40023  * Based on:
40024  * Ext JS Library 1.1.1
40025  * Copyright(c) 2006-2007, Ext JS, LLC.
40026  *
40027  * Originally Released Under LGPL - original licence link has changed is not relivant.
40028  *
40029  * Fork - LGPL
40030  * <script type="text/javascript">
40031  */
40032   
40033
40034   
40035 Roo.grid.PropertyRecord = Roo.data.Record.create([
40036     {name:'name',type:'string'},  'value'
40037 ]);
40038
40039
40040 Roo.grid.PropertyStore = function(grid, source){
40041     this.grid = grid;
40042     this.store = new Roo.data.Store({
40043         recordType : Roo.grid.PropertyRecord
40044     });
40045     this.store.on('update', this.onUpdate,  this);
40046     if(source){
40047         this.setSource(source);
40048     }
40049     Roo.grid.PropertyStore.superclass.constructor.call(this);
40050 };
40051
40052
40053
40054 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40055     setSource : function(o){
40056         this.source = o;
40057         this.store.removeAll();
40058         var data = [];
40059         for(var k in o){
40060             if(this.isEditableValue(o[k])){
40061                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40062             }
40063         }
40064         this.store.loadRecords({records: data}, {}, true);
40065     },
40066
40067     onUpdate : function(ds, record, type){
40068         if(type == Roo.data.Record.EDIT){
40069             var v = record.data['value'];
40070             var oldValue = record.modified['value'];
40071             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40072                 this.source[record.id] = v;
40073                 record.commit();
40074                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40075             }else{
40076                 record.reject();
40077             }
40078         }
40079     },
40080
40081     getProperty : function(row){
40082        return this.store.getAt(row);
40083     },
40084
40085     isEditableValue: function(val){
40086         if(val && val instanceof Date){
40087             return true;
40088         }else if(typeof val == 'object' || typeof val == 'function'){
40089             return false;
40090         }
40091         return true;
40092     },
40093
40094     setValue : function(prop, value){
40095         this.source[prop] = value;
40096         this.store.getById(prop).set('value', value);
40097     },
40098
40099     getSource : function(){
40100         return this.source;
40101     }
40102 });
40103
40104 Roo.grid.PropertyColumnModel = function(grid, store){
40105     this.grid = grid;
40106     var g = Roo.grid;
40107     g.PropertyColumnModel.superclass.constructor.call(this, [
40108         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40109         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40110     ]);
40111     this.store = store;
40112     this.bselect = Roo.DomHelper.append(document.body, {
40113         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40114             {tag: 'option', value: 'true', html: 'true'},
40115             {tag: 'option', value: 'false', html: 'false'}
40116         ]
40117     });
40118     Roo.id(this.bselect);
40119     var f = Roo.form;
40120     this.editors = {
40121         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40122         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40123         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40124         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40125         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40126     };
40127     this.renderCellDelegate = this.renderCell.createDelegate(this);
40128     this.renderPropDelegate = this.renderProp.createDelegate(this);
40129 };
40130
40131 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40132     
40133     
40134     nameText : 'Name',
40135     valueText : 'Value',
40136     
40137     dateFormat : 'm/j/Y',
40138     
40139     
40140     renderDate : function(dateVal){
40141         return dateVal.dateFormat(this.dateFormat);
40142     },
40143
40144     renderBool : function(bVal){
40145         return bVal ? 'true' : 'false';
40146     },
40147
40148     isCellEditable : function(colIndex, rowIndex){
40149         return colIndex == 1;
40150     },
40151
40152     getRenderer : function(col){
40153         return col == 1 ?
40154             this.renderCellDelegate : this.renderPropDelegate;
40155     },
40156
40157     renderProp : function(v){
40158         return this.getPropertyName(v);
40159     },
40160
40161     renderCell : function(val){
40162         var rv = val;
40163         if(val instanceof Date){
40164             rv = this.renderDate(val);
40165         }else if(typeof val == 'boolean'){
40166             rv = this.renderBool(val);
40167         }
40168         return Roo.util.Format.htmlEncode(rv);
40169     },
40170
40171     getPropertyName : function(name){
40172         var pn = this.grid.propertyNames;
40173         return pn && pn[name] ? pn[name] : name;
40174     },
40175
40176     getCellEditor : function(colIndex, rowIndex){
40177         var p = this.store.getProperty(rowIndex);
40178         var n = p.data['name'], val = p.data['value'];
40179         
40180         if(typeof(this.grid.customEditors[n]) == 'string'){
40181             return this.editors[this.grid.customEditors[n]];
40182         }
40183         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40184             return this.grid.customEditors[n];
40185         }
40186         if(val instanceof Date){
40187             return this.editors['date'];
40188         }else if(typeof val == 'number'){
40189             return this.editors['number'];
40190         }else if(typeof val == 'boolean'){
40191             return this.editors['boolean'];
40192         }else{
40193             return this.editors['string'];
40194         }
40195     }
40196 });
40197
40198 /**
40199  * @class Roo.grid.PropertyGrid
40200  * @extends Roo.grid.EditorGrid
40201  * This class represents the  interface of a component based property grid control.
40202  * <br><br>Usage:<pre><code>
40203  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40204       
40205  });
40206  // set any options
40207  grid.render();
40208  * </code></pre>
40209   
40210  * @constructor
40211  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40212  * The container MUST have some type of size defined for the grid to fill. The container will be
40213  * automatically set to position relative if it isn't already.
40214  * @param {Object} config A config object that sets properties on this grid.
40215  */
40216 Roo.grid.PropertyGrid = function(container, config){
40217     config = config || {};
40218     var store = new Roo.grid.PropertyStore(this);
40219     this.store = store;
40220     var cm = new Roo.grid.PropertyColumnModel(this, store);
40221     store.store.sort('name', 'ASC');
40222     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40223         ds: store.store,
40224         cm: cm,
40225         enableColLock:false,
40226         enableColumnMove:false,
40227         stripeRows:false,
40228         trackMouseOver: false,
40229         clicksToEdit:1
40230     }, config));
40231     this.getGridEl().addClass('x-props-grid');
40232     this.lastEditRow = null;
40233     this.on('columnresize', this.onColumnResize, this);
40234     this.addEvents({
40235          /**
40236              * @event beforepropertychange
40237              * Fires before a property changes (return false to stop?)
40238              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40239              * @param {String} id Record Id
40240              * @param {String} newval New Value
40241          * @param {String} oldval Old Value
40242              */
40243         "beforepropertychange": true,
40244         /**
40245              * @event propertychange
40246              * Fires after a property changes
40247              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40248              * @param {String} id Record Id
40249              * @param {String} newval New Value
40250          * @param {String} oldval Old Value
40251              */
40252         "propertychange": true
40253     });
40254     this.customEditors = this.customEditors || {};
40255 };
40256 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40257     
40258      /**
40259      * @cfg {Object} customEditors map of colnames=> custom editors.
40260      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40261      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40262      * false disables editing of the field.
40263          */
40264     
40265       /**
40266      * @cfg {Object} propertyNames map of property Names to their displayed value
40267          */
40268     
40269     render : function(){
40270         Roo.grid.PropertyGrid.superclass.render.call(this);
40271         this.autoSize.defer(100, this);
40272     },
40273
40274     autoSize : function(){
40275         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40276         if(this.view){
40277             this.view.fitColumns();
40278         }
40279     },
40280
40281     onColumnResize : function(){
40282         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40283         this.autoSize();
40284     },
40285     /**
40286      * Sets the data for the Grid
40287      * accepts a Key => Value object of all the elements avaiable.
40288      * @param {Object} data  to appear in grid.
40289      */
40290     setSource : function(source){
40291         this.store.setSource(source);
40292         //this.autoSize();
40293     },
40294     /**
40295      * Gets all the data from the grid.
40296      * @return {Object} data  data stored in grid
40297      */
40298     getSource : function(){
40299         return this.store.getSource();
40300     }
40301 });/*
40302   
40303  * Licence LGPL
40304  
40305  */
40306  
40307 /**
40308  * @class Roo.grid.Calendar
40309  * @extends Roo.util.Grid
40310  * This class extends the Grid to provide a calendar widget
40311  * <br><br>Usage:<pre><code>
40312  var grid = new Roo.grid.Calendar("my-container-id", {
40313      ds: myDataStore,
40314      cm: myColModel,
40315      selModel: mySelectionModel,
40316      autoSizeColumns: true,
40317      monitorWindowResize: false,
40318      trackMouseOver: true
40319      eventstore : real data store..
40320  });
40321  // set any options
40322  grid.render();
40323   
40324   * @constructor
40325  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40326  * The container MUST have some type of size defined for the grid to fill. The container will be
40327  * automatically set to position relative if it isn't already.
40328  * @param {Object} config A config object that sets properties on this grid.
40329  */
40330 Roo.grid.Calendar = function(container, config){
40331         // initialize the container
40332         this.container = Roo.get(container);
40333         this.container.update("");
40334         this.container.setStyle("overflow", "hidden");
40335     this.container.addClass('x-grid-container');
40336
40337     this.id = this.container.id;
40338
40339     Roo.apply(this, config);
40340     // check and correct shorthanded configs
40341     
40342     var rows = [];
40343     var d =1;
40344     for (var r = 0;r < 6;r++) {
40345         
40346         rows[r]=[];
40347         for (var c =0;c < 7;c++) {
40348             rows[r][c]= '';
40349         }
40350     }
40351     if (this.eventStore) {
40352         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40353         this.eventStore.on('load',this.onLoad, this);
40354         this.eventStore.on('beforeload',this.clearEvents, this);
40355          
40356     }
40357     
40358     this.dataSource = new Roo.data.Store({
40359             proxy: new Roo.data.MemoryProxy(rows),
40360             reader: new Roo.data.ArrayReader({}, [
40361                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40362     });
40363
40364     this.dataSource.load();
40365     this.ds = this.dataSource;
40366     this.ds.xmodule = this.xmodule || false;
40367     
40368     
40369     var cellRender = function(v,x,r)
40370     {
40371         return String.format(
40372             '<div class="fc-day  fc-widget-content"><div>' +
40373                 '<div class="fc-event-container"></div>' +
40374                 '<div class="fc-day-number">{0}</div>'+
40375                 
40376                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40377             '</div></div>', v);
40378     
40379     }
40380     
40381     
40382     this.colModel = new Roo.grid.ColumnModel( [
40383         {
40384             xtype: 'ColumnModel',
40385             xns: Roo.grid,
40386             dataIndex : 'weekday0',
40387             header : 'Sunday',
40388             renderer : cellRender
40389         },
40390         {
40391             xtype: 'ColumnModel',
40392             xns: Roo.grid,
40393             dataIndex : 'weekday1',
40394             header : 'Monday',
40395             renderer : cellRender
40396         },
40397         {
40398             xtype: 'ColumnModel',
40399             xns: Roo.grid,
40400             dataIndex : 'weekday2',
40401             header : 'Tuesday',
40402             renderer : cellRender
40403         },
40404         {
40405             xtype: 'ColumnModel',
40406             xns: Roo.grid,
40407             dataIndex : 'weekday3',
40408             header : 'Wednesday',
40409             renderer : cellRender
40410         },
40411         {
40412             xtype: 'ColumnModel',
40413             xns: Roo.grid,
40414             dataIndex : 'weekday4',
40415             header : 'Thursday',
40416             renderer : cellRender
40417         },
40418         {
40419             xtype: 'ColumnModel',
40420             xns: Roo.grid,
40421             dataIndex : 'weekday5',
40422             header : 'Friday',
40423             renderer : cellRender
40424         },
40425         {
40426             xtype: 'ColumnModel',
40427             xns: Roo.grid,
40428             dataIndex : 'weekday6',
40429             header : 'Saturday',
40430             renderer : cellRender
40431         }
40432     ]);
40433     this.cm = this.colModel;
40434     this.cm.xmodule = this.xmodule || false;
40435  
40436         
40437           
40438     //this.selModel = new Roo.grid.CellSelectionModel();
40439     //this.sm = this.selModel;
40440     //this.selModel.init(this);
40441     
40442     
40443     if(this.width){
40444         this.container.setWidth(this.width);
40445     }
40446
40447     if(this.height){
40448         this.container.setHeight(this.height);
40449     }
40450     /** @private */
40451         this.addEvents({
40452         // raw events
40453         /**
40454          * @event click
40455          * The raw click event for the entire grid.
40456          * @param {Roo.EventObject} e
40457          */
40458         "click" : true,
40459         /**
40460          * @event dblclick
40461          * The raw dblclick event for the entire grid.
40462          * @param {Roo.EventObject} e
40463          */
40464         "dblclick" : true,
40465         /**
40466          * @event contextmenu
40467          * The raw contextmenu event for the entire grid.
40468          * @param {Roo.EventObject} e
40469          */
40470         "contextmenu" : true,
40471         /**
40472          * @event mousedown
40473          * The raw mousedown event for the entire grid.
40474          * @param {Roo.EventObject} e
40475          */
40476         "mousedown" : true,
40477         /**
40478          * @event mouseup
40479          * The raw mouseup event for the entire grid.
40480          * @param {Roo.EventObject} e
40481          */
40482         "mouseup" : true,
40483         /**
40484          * @event mouseover
40485          * The raw mouseover event for the entire grid.
40486          * @param {Roo.EventObject} e
40487          */
40488         "mouseover" : true,
40489         /**
40490          * @event mouseout
40491          * The raw mouseout event for the entire grid.
40492          * @param {Roo.EventObject} e
40493          */
40494         "mouseout" : true,
40495         /**
40496          * @event keypress
40497          * The raw keypress event for the entire grid.
40498          * @param {Roo.EventObject} e
40499          */
40500         "keypress" : true,
40501         /**
40502          * @event keydown
40503          * The raw keydown event for the entire grid.
40504          * @param {Roo.EventObject} e
40505          */
40506         "keydown" : true,
40507
40508         // custom events
40509
40510         /**
40511          * @event cellclick
40512          * Fires when a cell is clicked
40513          * @param {Grid} this
40514          * @param {Number} rowIndex
40515          * @param {Number} columnIndex
40516          * @param {Roo.EventObject} e
40517          */
40518         "cellclick" : true,
40519         /**
40520          * @event celldblclick
40521          * Fires when a cell is double clicked
40522          * @param {Grid} this
40523          * @param {Number} rowIndex
40524          * @param {Number} columnIndex
40525          * @param {Roo.EventObject} e
40526          */
40527         "celldblclick" : true,
40528         /**
40529          * @event rowclick
40530          * Fires when a row is clicked
40531          * @param {Grid} this
40532          * @param {Number} rowIndex
40533          * @param {Roo.EventObject} e
40534          */
40535         "rowclick" : true,
40536         /**
40537          * @event rowdblclick
40538          * Fires when a row is double clicked
40539          * @param {Grid} this
40540          * @param {Number} rowIndex
40541          * @param {Roo.EventObject} e
40542          */
40543         "rowdblclick" : true,
40544         /**
40545          * @event headerclick
40546          * Fires when a header is clicked
40547          * @param {Grid} this
40548          * @param {Number} columnIndex
40549          * @param {Roo.EventObject} e
40550          */
40551         "headerclick" : true,
40552         /**
40553          * @event headerdblclick
40554          * Fires when a header cell is double clicked
40555          * @param {Grid} this
40556          * @param {Number} columnIndex
40557          * @param {Roo.EventObject} e
40558          */
40559         "headerdblclick" : true,
40560         /**
40561          * @event rowcontextmenu
40562          * Fires when a row is right clicked
40563          * @param {Grid} this
40564          * @param {Number} rowIndex
40565          * @param {Roo.EventObject} e
40566          */
40567         "rowcontextmenu" : true,
40568         /**
40569          * @event cellcontextmenu
40570          * Fires when a cell is right clicked
40571          * @param {Grid} this
40572          * @param {Number} rowIndex
40573          * @param {Number} cellIndex
40574          * @param {Roo.EventObject} e
40575          */
40576          "cellcontextmenu" : true,
40577         /**
40578          * @event headercontextmenu
40579          * Fires when a header is right clicked
40580          * @param {Grid} this
40581          * @param {Number} columnIndex
40582          * @param {Roo.EventObject} e
40583          */
40584         "headercontextmenu" : true,
40585         /**
40586          * @event bodyscroll
40587          * Fires when the body element is scrolled
40588          * @param {Number} scrollLeft
40589          * @param {Number} scrollTop
40590          */
40591         "bodyscroll" : true,
40592         /**
40593          * @event columnresize
40594          * Fires when the user resizes a column
40595          * @param {Number} columnIndex
40596          * @param {Number} newSize
40597          */
40598         "columnresize" : true,
40599         /**
40600          * @event columnmove
40601          * Fires when the user moves a column
40602          * @param {Number} oldIndex
40603          * @param {Number} newIndex
40604          */
40605         "columnmove" : true,
40606         /**
40607          * @event startdrag
40608          * Fires when row(s) start being dragged
40609          * @param {Grid} this
40610          * @param {Roo.GridDD} dd The drag drop object
40611          * @param {event} e The raw browser event
40612          */
40613         "startdrag" : true,
40614         /**
40615          * @event enddrag
40616          * Fires when a drag operation is complete
40617          * @param {Grid} this
40618          * @param {Roo.GridDD} dd The drag drop object
40619          * @param {event} e The raw browser event
40620          */
40621         "enddrag" : true,
40622         /**
40623          * @event dragdrop
40624          * Fires when dragged row(s) are dropped on a valid DD target
40625          * @param {Grid} this
40626          * @param {Roo.GridDD} dd The drag drop object
40627          * @param {String} targetId The target drag drop object
40628          * @param {event} e The raw browser event
40629          */
40630         "dragdrop" : true,
40631         /**
40632          * @event dragover
40633          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40634          * @param {Grid} this
40635          * @param {Roo.GridDD} dd The drag drop object
40636          * @param {String} targetId The target drag drop object
40637          * @param {event} e The raw browser event
40638          */
40639         "dragover" : true,
40640         /**
40641          * @event dragenter
40642          *  Fires when the dragged row(s) first cross another DD target while being dragged
40643          * @param {Grid} this
40644          * @param {Roo.GridDD} dd The drag drop object
40645          * @param {String} targetId The target drag drop object
40646          * @param {event} e The raw browser event
40647          */
40648         "dragenter" : true,
40649         /**
40650          * @event dragout
40651          * Fires when the dragged row(s) leave another DD target while being dragged
40652          * @param {Grid} this
40653          * @param {Roo.GridDD} dd The drag drop object
40654          * @param {String} targetId The target drag drop object
40655          * @param {event} e The raw browser event
40656          */
40657         "dragout" : true,
40658         /**
40659          * @event rowclass
40660          * Fires when a row is rendered, so you can change add a style to it.
40661          * @param {GridView} gridview   The grid view
40662          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
40663          */
40664         'rowclass' : true,
40665
40666         /**
40667          * @event render
40668          * Fires when the grid is rendered
40669          * @param {Grid} grid
40670          */
40671         'render' : true,
40672             /**
40673              * @event select
40674              * Fires when a date is selected
40675              * @param {DatePicker} this
40676              * @param {Date} date The selected date
40677              */
40678         'select': true,
40679         /**
40680              * @event monthchange
40681              * Fires when the displayed month changes 
40682              * @param {DatePicker} this
40683              * @param {Date} date The selected month
40684              */
40685         'monthchange': true,
40686         /**
40687              * @event evententer
40688              * Fires when mouse over an event
40689              * @param {Calendar} this
40690              * @param {event} Event
40691              */
40692         'evententer': true,
40693         /**
40694              * @event eventleave
40695              * Fires when the mouse leaves an
40696              * @param {Calendar} this
40697              * @param {event}
40698              */
40699         'eventleave': true,
40700         /**
40701              * @event eventclick
40702              * Fires when the mouse click an
40703              * @param {Calendar} this
40704              * @param {event}
40705              */
40706         'eventclick': true,
40707         /**
40708              * @event eventrender
40709              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
40710              * @param {Calendar} this
40711              * @param {data} data to be modified
40712              */
40713         'eventrender': true
40714         
40715     });
40716
40717     Roo.grid.Grid.superclass.constructor.call(this);
40718     this.on('render', function() {
40719         this.view.el.addClass('x-grid-cal'); 
40720         
40721         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
40722
40723     },this);
40724     
40725     if (!Roo.grid.Calendar.style) {
40726         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
40727             
40728             
40729             '.x-grid-cal .x-grid-col' :  {
40730                 height: 'auto !important',
40731                 'vertical-align': 'top'
40732             },
40733             '.x-grid-cal  .fc-event-hori' : {
40734                 height: '14px'
40735             }
40736              
40737             
40738         }, Roo.id());
40739     }
40740
40741     
40742     
40743 };
40744 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
40745     /**
40746      * @cfg {Store} eventStore The store that loads events.
40747      */
40748     eventStore : 25,
40749
40750      
40751     activeDate : false,
40752     startDay : 0,
40753     autoWidth : true,
40754     monitorWindowResize : false,
40755
40756     
40757     resizeColumns : function() {
40758         var col = (this.view.el.getWidth() / 7) - 3;
40759         // loop through cols, and setWidth
40760         for(var i =0 ; i < 7 ; i++){
40761             this.cm.setColumnWidth(i, col);
40762         }
40763     },
40764      setDate :function(date) {
40765         
40766         Roo.log('setDate?');
40767         
40768         this.resizeColumns();
40769         var vd = this.activeDate;
40770         this.activeDate = date;
40771 //        if(vd && this.el){
40772 //            var t = date.getTime();
40773 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
40774 //                Roo.log('using add remove');
40775 //                
40776 //                this.fireEvent('monthchange', this, date);
40777 //                
40778 //                this.cells.removeClass("fc-state-highlight");
40779 //                this.cells.each(function(c){
40780 //                   if(c.dateValue == t){
40781 //                       c.addClass("fc-state-highlight");
40782 //                       setTimeout(function(){
40783 //                            try{c.dom.firstChild.focus();}catch(e){}
40784 //                       }, 50);
40785 //                       return false;
40786 //                   }
40787 //                   return true;
40788 //                });
40789 //                return;
40790 //            }
40791 //        }
40792         
40793         var days = date.getDaysInMonth();
40794         
40795         var firstOfMonth = date.getFirstDateOfMonth();
40796         var startingPos = firstOfMonth.getDay()-this.startDay;
40797         
40798         if(startingPos < this.startDay){
40799             startingPos += 7;
40800         }
40801         
40802         var pm = date.add(Date.MONTH, -1);
40803         var prevStart = pm.getDaysInMonth()-startingPos;
40804 //        
40805         
40806         
40807         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40808         
40809         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
40810         //this.cells.addClassOnOver('fc-state-hover');
40811         
40812         var cells = this.cells.elements;
40813         var textEls = this.textNodes;
40814         
40815         //Roo.each(cells, function(cell){
40816         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
40817         //});
40818         
40819         days += startingPos;
40820
40821         // convert everything to numbers so it's fast
40822         var day = 86400000;
40823         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
40824         //Roo.log(d);
40825         //Roo.log(pm);
40826         //Roo.log(prevStart);
40827         
40828         var today = new Date().clearTime().getTime();
40829         var sel = date.clearTime().getTime();
40830         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
40831         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
40832         var ddMatch = this.disabledDatesRE;
40833         var ddText = this.disabledDatesText;
40834         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
40835         var ddaysText = this.disabledDaysText;
40836         var format = this.format;
40837         
40838         var setCellClass = function(cal, cell){
40839             
40840             //Roo.log('set Cell Class');
40841             cell.title = "";
40842             var t = d.getTime();
40843             
40844             //Roo.log(d);
40845             
40846             
40847             cell.dateValue = t;
40848             if(t == today){
40849                 cell.className += " fc-today";
40850                 cell.className += " fc-state-highlight";
40851                 cell.title = cal.todayText;
40852             }
40853             if(t == sel){
40854                 // disable highlight in other month..
40855                 cell.className += " fc-state-highlight";
40856                 
40857             }
40858             // disabling
40859             if(t < min) {
40860                 //cell.className = " fc-state-disabled";
40861                 cell.title = cal.minText;
40862                 return;
40863             }
40864             if(t > max) {
40865                 //cell.className = " fc-state-disabled";
40866                 cell.title = cal.maxText;
40867                 return;
40868             }
40869             if(ddays){
40870                 if(ddays.indexOf(d.getDay()) != -1){
40871                     // cell.title = ddaysText;
40872                    // cell.className = " fc-state-disabled";
40873                 }
40874             }
40875             if(ddMatch && format){
40876                 var fvalue = d.dateFormat(format);
40877                 if(ddMatch.test(fvalue)){
40878                     cell.title = ddText.replace("%0", fvalue);
40879                    cell.className = " fc-state-disabled";
40880                 }
40881             }
40882             
40883             if (!cell.initialClassName) {
40884                 cell.initialClassName = cell.dom.className;
40885             }
40886             
40887             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
40888         };
40889
40890         var i = 0;
40891         
40892         for(; i < startingPos; i++) {
40893             cells[i].dayName =  (++prevStart);
40894             Roo.log(textEls[i]);
40895             d.setDate(d.getDate()+1);
40896             
40897             //cells[i].className = "fc-past fc-other-month";
40898             setCellClass(this, cells[i]);
40899         }
40900         
40901         var intDay = 0;
40902         
40903         for(; i < days; i++){
40904             intDay = i - startingPos + 1;
40905             cells[i].dayName =  (intDay);
40906             d.setDate(d.getDate()+1);
40907             
40908             cells[i].className = ''; // "x-date-active";
40909             setCellClass(this, cells[i]);
40910         }
40911         var extraDays = 0;
40912         
40913         for(; i < 42; i++) {
40914             //textEls[i].innerHTML = (++extraDays);
40915             
40916             d.setDate(d.getDate()+1);
40917             cells[i].dayName = (++extraDays);
40918             cells[i].className = "fc-future fc-other-month";
40919             setCellClass(this, cells[i]);
40920         }
40921         
40922         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
40923         
40924         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
40925         
40926         // this will cause all the cells to mis
40927         var rows= [];
40928         var i =0;
40929         for (var r = 0;r < 6;r++) {
40930             for (var c =0;c < 7;c++) {
40931                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
40932             }    
40933         }
40934         
40935         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
40936         for(i=0;i<cells.length;i++) {
40937             
40938             this.cells.elements[i].dayName = cells[i].dayName ;
40939             this.cells.elements[i].className = cells[i].className;
40940             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
40941             this.cells.elements[i].title = cells[i].title ;
40942             this.cells.elements[i].dateValue = cells[i].dateValue ;
40943         }
40944         
40945         
40946         
40947         
40948         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
40949         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
40950         
40951         ////if(totalRows != 6){
40952             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
40953            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
40954        // }
40955         
40956         this.fireEvent('monthchange', this, date);
40957         
40958         
40959     },
40960  /**
40961      * Returns the grid's SelectionModel.
40962      * @return {SelectionModel}
40963      */
40964     getSelectionModel : function(){
40965         if(!this.selModel){
40966             this.selModel = new Roo.grid.CellSelectionModel();
40967         }
40968         return this.selModel;
40969     },
40970
40971     load: function() {
40972         this.eventStore.load()
40973         
40974         
40975         
40976     },
40977     
40978     findCell : function(dt) {
40979         dt = dt.clearTime().getTime();
40980         var ret = false;
40981         this.cells.each(function(c){
40982             //Roo.log("check " +c.dateValue + '?=' + dt);
40983             if(c.dateValue == dt){
40984                 ret = c;
40985                 return false;
40986             }
40987             return true;
40988         });
40989         
40990         return ret;
40991     },
40992     
40993     findCells : function(rec) {
40994         var s = rec.data.start_dt.clone().clearTime().getTime();
40995        // Roo.log(s);
40996         var e= rec.data.end_dt.clone().clearTime().getTime();
40997        // Roo.log(e);
40998         var ret = [];
40999         this.cells.each(function(c){
41000              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41001             
41002             if(c.dateValue > e){
41003                 return ;
41004             }
41005             if(c.dateValue < s){
41006                 return ;
41007             }
41008             ret.push(c);
41009         });
41010         
41011         return ret;    
41012     },
41013     
41014     findBestRow: function(cells)
41015     {
41016         var ret = 0;
41017         
41018         for (var i =0 ; i < cells.length;i++) {
41019             ret  = Math.max(cells[i].rows || 0,ret);
41020         }
41021         return ret;
41022         
41023     },
41024     
41025     
41026     addItem : function(rec)
41027     {
41028         // look for vertical location slot in
41029         var cells = this.findCells(rec);
41030         
41031         rec.row = this.findBestRow(cells);
41032         
41033         // work out the location.
41034         
41035         var crow = false;
41036         var rows = [];
41037         for(var i =0; i < cells.length; i++) {
41038             if (!crow) {
41039                 crow = {
41040                     start : cells[i],
41041                     end :  cells[i]
41042                 };
41043                 continue;
41044             }
41045             if (crow.start.getY() == cells[i].getY()) {
41046                 // on same row.
41047                 crow.end = cells[i];
41048                 continue;
41049             }
41050             // different row.
41051             rows.push(crow);
41052             crow = {
41053                 start: cells[i],
41054                 end : cells[i]
41055             };
41056             
41057         }
41058         
41059         rows.push(crow);
41060         rec.els = [];
41061         rec.rows = rows;
41062         rec.cells = cells;
41063         for (var i = 0; i < cells.length;i++) {
41064             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41065             
41066         }
41067         
41068         
41069     },
41070     
41071     clearEvents: function() {
41072         
41073         if (!this.eventStore.getCount()) {
41074             return;
41075         }
41076         // reset number of rows in cells.
41077         Roo.each(this.cells.elements, function(c){
41078             c.rows = 0;
41079         });
41080         
41081         this.eventStore.each(function(e) {
41082             this.clearEvent(e);
41083         },this);
41084         
41085     },
41086     
41087     clearEvent : function(ev)
41088     {
41089         if (ev.els) {
41090             Roo.each(ev.els, function(el) {
41091                 el.un('mouseenter' ,this.onEventEnter, this);
41092                 el.un('mouseleave' ,this.onEventLeave, this);
41093                 el.remove();
41094             },this);
41095             ev.els = [];
41096         }
41097     },
41098     
41099     
41100     renderEvent : function(ev,ctr) {
41101         if (!ctr) {
41102              ctr = this.view.el.select('.fc-event-container',true).first();
41103         }
41104         
41105          
41106         this.clearEvent(ev);
41107             //code
41108        
41109         
41110         
41111         ev.els = [];
41112         var cells = ev.cells;
41113         var rows = ev.rows;
41114         this.fireEvent('eventrender', this, ev);
41115         
41116         for(var i =0; i < rows.length; i++) {
41117             
41118             cls = '';
41119             if (i == 0) {
41120                 cls += ' fc-event-start';
41121             }
41122             if ((i+1) == rows.length) {
41123                 cls += ' fc-event-end';
41124             }
41125             
41126             //Roo.log(ev.data);
41127             // how many rows should it span..
41128             var cg = this.eventTmpl.append(ctr,Roo.apply({
41129                 fccls : cls
41130                 
41131             }, ev.data) , true);
41132             
41133             
41134             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41135             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41136             cg.on('click', this.onEventClick, this, ev);
41137             
41138             ev.els.push(cg);
41139             
41140             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41141             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41142             //Roo.log(cg);
41143              
41144             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41145             cg.setWidth(ebox.right - sbox.x -2);
41146         }
41147     },
41148     
41149     renderEvents: function()
41150     {   
41151         // first make sure there is enough space..
41152         
41153         if (!this.eventTmpl) {
41154             this.eventTmpl = new Roo.Template(
41155                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41156                     '<div class="fc-event-inner">' +
41157                         '<span class="fc-event-time">{time}</span>' +
41158                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41159                     '</div>' +
41160                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41161                 '</div>'
41162             );
41163                 
41164         }
41165                
41166         
41167         
41168         this.cells.each(function(c) {
41169             //Roo.log(c.select('.fc-day-content div',true).first());
41170             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41171         });
41172         
41173         var ctr = this.view.el.select('.fc-event-container',true).first();
41174         
41175         var cls;
41176         this.eventStore.each(function(ev){
41177             
41178             this.renderEvent(ev);
41179              
41180              
41181         }, this);
41182         this.view.layout();
41183         
41184     },
41185     
41186     onEventEnter: function (e, el,event,d) {
41187         this.fireEvent('evententer', this, el, event);
41188     },
41189     
41190     onEventLeave: function (e, el,event,d) {
41191         this.fireEvent('eventleave', this, el, event);
41192     },
41193     
41194     onEventClick: function (e, el,event,d) {
41195         this.fireEvent('eventclick', this, el, event);
41196     },
41197     
41198     onMonthChange: function () {
41199         this.store.load();
41200     },
41201     
41202     onLoad: function () {
41203         
41204         //Roo.log('calendar onload');
41205 //         
41206         if(this.eventStore.getCount() > 0){
41207             
41208            
41209             
41210             this.eventStore.each(function(d){
41211                 
41212                 
41213                 // FIXME..
41214                 var add =   d.data;
41215                 if (typeof(add.end_dt) == 'undefined')  {
41216                     Roo.log("Missing End time in calendar data: ");
41217                     Roo.log(d);
41218                     return;
41219                 }
41220                 if (typeof(add.start_dt) == 'undefined')  {
41221                     Roo.log("Missing Start time in calendar data: ");
41222                     Roo.log(d);
41223                     return;
41224                 }
41225                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41226                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41227                 add.id = add.id || d.id;
41228                 add.title = add.title || '??';
41229                 
41230                 this.addItem(d);
41231                 
41232              
41233             },this);
41234         }
41235         
41236         this.renderEvents();
41237     }
41238     
41239
41240 });
41241 /*
41242  grid : {
41243                 xtype: 'Grid',
41244                 xns: Roo.grid,
41245                 listeners : {
41246                     render : function ()
41247                     {
41248                         _this.grid = this;
41249                         
41250                         if (!this.view.el.hasClass('course-timesheet')) {
41251                             this.view.el.addClass('course-timesheet');
41252                         }
41253                         if (this.tsStyle) {
41254                             this.ds.load({});
41255                             return; 
41256                         }
41257                         Roo.log('width');
41258                         Roo.log(_this.grid.view.el.getWidth());
41259                         
41260                         
41261                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41262                             '.course-timesheet .x-grid-row' : {
41263                                 height: '80px'
41264                             },
41265                             '.x-grid-row td' : {
41266                                 'vertical-align' : 0
41267                             },
41268                             '.course-edit-link' : {
41269                                 'color' : 'blue',
41270                                 'text-overflow' : 'ellipsis',
41271                                 'overflow' : 'hidden',
41272                                 'white-space' : 'nowrap',
41273                                 'cursor' : 'pointer'
41274                             },
41275                             '.sub-link' : {
41276                                 'color' : 'green'
41277                             },
41278                             '.de-act-sup-link' : {
41279                                 'color' : 'purple',
41280                                 'text-decoration' : 'line-through'
41281                             },
41282                             '.de-act-link' : {
41283                                 'color' : 'red',
41284                                 'text-decoration' : 'line-through'
41285                             },
41286                             '.course-timesheet .course-highlight' : {
41287                                 'border-top-style': 'dashed !important',
41288                                 'border-bottom-bottom': 'dashed !important'
41289                             },
41290                             '.course-timesheet .course-item' : {
41291                                 'font-family'   : 'tahoma, arial, helvetica',
41292                                 'font-size'     : '11px',
41293                                 'overflow'      : 'hidden',
41294                                 'padding-left'  : '10px',
41295                                 'padding-right' : '10px',
41296                                 'padding-top' : '10px' 
41297                             }
41298                             
41299                         }, Roo.id());
41300                                 this.ds.load({});
41301                     }
41302                 },
41303                 autoWidth : true,
41304                 monitorWindowResize : false,
41305                 cellrenderer : function(v,x,r)
41306                 {
41307                     return v;
41308                 },
41309                 sm : {
41310                     xtype: 'CellSelectionModel',
41311                     xns: Roo.grid
41312                 },
41313                 dataSource : {
41314                     xtype: 'Store',
41315                     xns: Roo.data,
41316                     listeners : {
41317                         beforeload : function (_self, options)
41318                         {
41319                             options.params = options.params || {};
41320                             options.params._month = _this.monthField.getValue();
41321                             options.params.limit = 9999;
41322                             options.params['sort'] = 'when_dt';    
41323                             options.params['dir'] = 'ASC';    
41324                             this.proxy.loadResponse = this.loadResponse;
41325                             Roo.log("load?");
41326                             //this.addColumns();
41327                         },
41328                         load : function (_self, records, options)
41329                         {
41330                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41331                                 // if you click on the translation.. you can edit it...
41332                                 var el = Roo.get(this);
41333                                 var id = el.dom.getAttribute('data-id');
41334                                 var d = el.dom.getAttribute('data-date');
41335                                 var t = el.dom.getAttribute('data-time');
41336                                 //var id = this.child('span').dom.textContent;
41337                                 
41338                                 //Roo.log(this);
41339                                 Pman.Dialog.CourseCalendar.show({
41340                                     id : id,
41341                                     when_d : d,
41342                                     when_t : t,
41343                                     productitem_active : id ? 1 : 0
41344                                 }, function() {
41345                                     _this.grid.ds.load({});
41346                                 });
41347                            
41348                            });
41349                            
41350                            _this.panel.fireEvent('resize', [ '', '' ]);
41351                         }
41352                     },
41353                     loadResponse : function(o, success, response){
41354                             // this is overridden on before load..
41355                             
41356                             Roo.log("our code?");       
41357                             //Roo.log(success);
41358                             //Roo.log(response)
41359                             delete this.activeRequest;
41360                             if(!success){
41361                                 this.fireEvent("loadexception", this, o, response);
41362                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41363                                 return;
41364                             }
41365                             var result;
41366                             try {
41367                                 result = o.reader.read(response);
41368                             }catch(e){
41369                                 Roo.log("load exception?");
41370                                 this.fireEvent("loadexception", this, o, response, e);
41371                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41372                                 return;
41373                             }
41374                             Roo.log("ready...");        
41375                             // loop through result.records;
41376                             // and set this.tdate[date] = [] << array of records..
41377                             _this.tdata  = {};
41378                             Roo.each(result.records, function(r){
41379                                 //Roo.log(r.data);
41380                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41381                                     _this.tdata[r.data.when_dt.format('j')] = [];
41382                                 }
41383                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41384                             });
41385                             
41386                             //Roo.log(_this.tdata);
41387                             
41388                             result.records = [];
41389                             result.totalRecords = 6;
41390                     
41391                             // let's generate some duumy records for the rows.
41392                             //var st = _this.dateField.getValue();
41393                             
41394                             // work out monday..
41395                             //st = st.add(Date.DAY, -1 * st.format('w'));
41396                             
41397                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41398                             
41399                             var firstOfMonth = date.getFirstDayOfMonth();
41400                             var days = date.getDaysInMonth();
41401                             var d = 1;
41402                             var firstAdded = false;
41403                             for (var i = 0; i < result.totalRecords ; i++) {
41404                                 //var d= st.add(Date.DAY, i);
41405                                 var row = {};
41406                                 var added = 0;
41407                                 for(var w = 0 ; w < 7 ; w++){
41408                                     if(!firstAdded && firstOfMonth != w){
41409                                         continue;
41410                                     }
41411                                     if(d > days){
41412                                         continue;
41413                                     }
41414                                     firstAdded = true;
41415                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41416                                     row['weekday'+w] = String.format(
41417                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41418                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41419                                                     d,
41420                                                     date.format('Y-m-')+dd
41421                                                 );
41422                                     added++;
41423                                     if(typeof(_this.tdata[d]) != 'undefined'){
41424                                         Roo.each(_this.tdata[d], function(r){
41425                                             var is_sub = '';
41426                                             var deactive = '';
41427                                             var id = r.id;
41428                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41429                                             if(r.parent_id*1>0){
41430                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41431                                                 id = r.parent_id;
41432                                             }
41433                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41434                                                 deactive = 'de-act-link';
41435                                             }
41436                                             
41437                                             row['weekday'+w] += String.format(
41438                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41439                                                     id, //0
41440                                                     r.product_id_name, //1
41441                                                     r.when_dt.format('h:ia'), //2
41442                                                     is_sub, //3
41443                                                     deactive, //4
41444                                                     desc // 5
41445                                             );
41446                                         });
41447                                     }
41448                                     d++;
41449                                 }
41450                                 
41451                                 // only do this if something added..
41452                                 if(added > 0){ 
41453                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41454                                 }
41455                                 
41456                                 
41457                                 // push it twice. (second one with an hour..
41458                                 
41459                             }
41460                             //Roo.log(result);
41461                             this.fireEvent("load", this, o, o.request.arg);
41462                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41463                         },
41464                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41465                     proxy : {
41466                         xtype: 'HttpProxy',
41467                         xns: Roo.data,
41468                         method : 'GET',
41469                         url : baseURL + '/Roo/Shop_course.php'
41470                     },
41471                     reader : {
41472                         xtype: 'JsonReader',
41473                         xns: Roo.data,
41474                         id : 'id',
41475                         fields : [
41476                             {
41477                                 'name': 'id',
41478                                 'type': 'int'
41479                             },
41480                             {
41481                                 'name': 'when_dt',
41482                                 'type': 'string'
41483                             },
41484                             {
41485                                 'name': 'end_dt',
41486                                 'type': 'string'
41487                             },
41488                             {
41489                                 'name': 'parent_id',
41490                                 'type': 'int'
41491                             },
41492                             {
41493                                 'name': 'product_id',
41494                                 'type': 'int'
41495                             },
41496                             {
41497                                 'name': 'productitem_id',
41498                                 'type': 'int'
41499                             },
41500                             {
41501                                 'name': 'guid',
41502                                 'type': 'int'
41503                             }
41504                         ]
41505                     }
41506                 },
41507                 toolbar : {
41508                     xtype: 'Toolbar',
41509                     xns: Roo,
41510                     items : [
41511                         {
41512                             xtype: 'Button',
41513                             xns: Roo.Toolbar,
41514                             listeners : {
41515                                 click : function (_self, e)
41516                                 {
41517                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41518                                     sd.setMonth(sd.getMonth()-1);
41519                                     _this.monthField.setValue(sd.format('Y-m-d'));
41520                                     _this.grid.ds.load({});
41521                                 }
41522                             },
41523                             text : "Back"
41524                         },
41525                         {
41526                             xtype: 'Separator',
41527                             xns: Roo.Toolbar
41528                         },
41529                         {
41530                             xtype: 'MonthField',
41531                             xns: Roo.form,
41532                             listeners : {
41533                                 render : function (_self)
41534                                 {
41535                                     _this.monthField = _self;
41536                                    // _this.monthField.set  today
41537                                 },
41538                                 select : function (combo, date)
41539                                 {
41540                                     _this.grid.ds.load({});
41541                                 }
41542                             },
41543                             value : (function() { return new Date(); })()
41544                         },
41545                         {
41546                             xtype: 'Separator',
41547                             xns: Roo.Toolbar
41548                         },
41549                         {
41550                             xtype: 'TextItem',
41551                             xns: Roo.Toolbar,
41552                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41553                         },
41554                         {
41555                             xtype: 'Fill',
41556                             xns: Roo.Toolbar
41557                         },
41558                         {
41559                             xtype: 'Button',
41560                             xns: Roo.Toolbar,
41561                             listeners : {
41562                                 click : function (_self, e)
41563                                 {
41564                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41565                                     sd.setMonth(sd.getMonth()+1);
41566                                     _this.monthField.setValue(sd.format('Y-m-d'));
41567                                     _this.grid.ds.load({});
41568                                 }
41569                             },
41570                             text : "Next"
41571                         }
41572                     ]
41573                 },
41574                  
41575             }
41576         };
41577         
41578         *//*
41579  * Based on:
41580  * Ext JS Library 1.1.1
41581  * Copyright(c) 2006-2007, Ext JS, LLC.
41582  *
41583  * Originally Released Under LGPL - original licence link has changed is not relivant.
41584  *
41585  * Fork - LGPL
41586  * <script type="text/javascript">
41587  */
41588  
41589 /**
41590  * @class Roo.LoadMask
41591  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41592  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41593  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41594  * element's UpdateManager load indicator and will be destroyed after the initial load.
41595  * @constructor
41596  * Create a new LoadMask
41597  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41598  * @param {Object} config The config object
41599  */
41600 Roo.LoadMask = function(el, config){
41601     this.el = Roo.get(el);
41602     Roo.apply(this, config);
41603     if(this.store){
41604         this.store.on('beforeload', this.onBeforeLoad, this);
41605         this.store.on('load', this.onLoad, this);
41606         this.store.on('loadexception', this.onLoadException, this);
41607         this.removeMask = false;
41608     }else{
41609         var um = this.el.getUpdateManager();
41610         um.showLoadIndicator = false; // disable the default indicator
41611         um.on('beforeupdate', this.onBeforeLoad, this);
41612         um.on('update', this.onLoad, this);
41613         um.on('failure', this.onLoad, this);
41614         this.removeMask = true;
41615     }
41616 };
41617
41618 Roo.LoadMask.prototype = {
41619     /**
41620      * @cfg {Boolean} removeMask
41621      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41622      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41623      */
41624     /**
41625      * @cfg {String} msg
41626      * The text to display in a centered loading message box (defaults to 'Loading...')
41627      */
41628     msg : 'Loading...',
41629     /**
41630      * @cfg {String} msgCls
41631      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41632      */
41633     msgCls : 'x-mask-loading',
41634
41635     /**
41636      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41637      * @type Boolean
41638      */
41639     disabled: false,
41640
41641     /**
41642      * Disables the mask to prevent it from being displayed
41643      */
41644     disable : function(){
41645        this.disabled = true;
41646     },
41647
41648     /**
41649      * Enables the mask so that it can be displayed
41650      */
41651     enable : function(){
41652         this.disabled = false;
41653     },
41654     
41655     onLoadException : function()
41656     {
41657         Roo.log(arguments);
41658         
41659         if (typeof(arguments[3]) != 'undefined') {
41660             Roo.MessageBox.alert("Error loading",arguments[3]);
41661         } 
41662         /*
41663         try {
41664             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41665                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41666             }   
41667         } catch(e) {
41668             
41669         }
41670         */
41671     
41672         
41673         
41674         this.el.unmask(this.removeMask);
41675     },
41676     // private
41677     onLoad : function()
41678     {
41679         this.el.unmask(this.removeMask);
41680     },
41681
41682     // private
41683     onBeforeLoad : function(){
41684         if(!this.disabled){
41685             this.el.mask(this.msg, this.msgCls);
41686         }
41687     },
41688
41689     // private
41690     destroy : function(){
41691         if(this.store){
41692             this.store.un('beforeload', this.onBeforeLoad, this);
41693             this.store.un('load', this.onLoad, this);
41694             this.store.un('loadexception', this.onLoadException, this);
41695         }else{
41696             var um = this.el.getUpdateManager();
41697             um.un('beforeupdate', this.onBeforeLoad, this);
41698             um.un('update', this.onLoad, this);
41699             um.un('failure', this.onLoad, this);
41700         }
41701     }
41702 };/*
41703  * Based on:
41704  * Ext JS Library 1.1.1
41705  * Copyright(c) 2006-2007, Ext JS, LLC.
41706  *
41707  * Originally Released Under LGPL - original licence link has changed is not relivant.
41708  *
41709  * Fork - LGPL
41710  * <script type="text/javascript">
41711  */
41712
41713
41714 /**
41715  * @class Roo.XTemplate
41716  * @extends Roo.Template
41717  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
41718 <pre><code>
41719 var t = new Roo.XTemplate(
41720         '&lt;select name="{name}"&gt;',
41721                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
41722         '&lt;/select&gt;'
41723 );
41724  
41725 // then append, applying the master template values
41726  </code></pre>
41727  *
41728  * Supported features:
41729  *
41730  *  Tags:
41731
41732 <pre><code>
41733       {a_variable} - output encoded.
41734       {a_variable.format:("Y-m-d")} - call a method on the variable
41735       {a_variable:raw} - unencoded output
41736       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
41737       {a_variable:this.method_on_template(...)} - call a method on the template object.
41738  
41739 </code></pre>
41740  *  The tpl tag:
41741 <pre><code>
41742         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
41743         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
41744         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
41745         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
41746   
41747         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
41748         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
41749 </code></pre>
41750  *      
41751  */
41752 Roo.XTemplate = function()
41753 {
41754     Roo.XTemplate.superclass.constructor.apply(this, arguments);
41755     if (this.html) {
41756         this.compile();
41757     }
41758 };
41759
41760
41761 Roo.extend(Roo.XTemplate, Roo.Template, {
41762
41763     /**
41764      * The various sub templates
41765      */
41766     tpls : false,
41767     /**
41768      *
41769      * basic tag replacing syntax
41770      * WORD:WORD()
41771      *
41772      * // you can fake an object call by doing this
41773      *  x.t:(test,tesT) 
41774      * 
41775      */
41776     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
41777
41778     /**
41779      * compile the template
41780      *
41781      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
41782      *
41783      */
41784     compile: function()
41785     {
41786         var s = this.html;
41787      
41788         s = ['<tpl>', s, '</tpl>'].join('');
41789     
41790         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
41791             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
41792             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
41793             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
41794             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
41795             m,
41796             id     = 0,
41797             tpls   = [];
41798     
41799         while(true == !!(m = s.match(re))){
41800             var forMatch   = m[0].match(nameRe),
41801                 ifMatch   = m[0].match(ifRe),
41802                 execMatch   = m[0].match(execRe),
41803                 namedMatch   = m[0].match(namedRe),
41804                 
41805                 exp  = null, 
41806                 fn   = null,
41807                 exec = null,
41808                 name = forMatch && forMatch[1] ? forMatch[1] : '';
41809                 
41810             if (ifMatch) {
41811                 // if - puts fn into test..
41812                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
41813                 if(exp){
41814                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
41815                 }
41816             }
41817             
41818             if (execMatch) {
41819                 // exec - calls a function... returns empty if true is  returned.
41820                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
41821                 if(exp){
41822                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
41823                 }
41824             }
41825             
41826             
41827             if (name) {
41828                 // for = 
41829                 switch(name){
41830                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
41831                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
41832                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
41833                 }
41834             }
41835             var uid = namedMatch ? namedMatch[1] : id;
41836             
41837             
41838             tpls.push({
41839                 id:     namedMatch ? namedMatch[1] : id,
41840                 target: name,
41841                 exec:   exec,
41842                 test:   fn,
41843                 body:   m[1] || ''
41844             });
41845             if (namedMatch) {
41846                 s = s.replace(m[0], '');
41847             } else { 
41848                 s = s.replace(m[0], '{xtpl'+ id + '}');
41849             }
41850             ++id;
41851         }
41852         this.tpls = [];
41853         for(var i = tpls.length-1; i >= 0; --i){
41854             this.compileTpl(tpls[i]);
41855             this.tpls[tpls[i].id] = tpls[i];
41856         }
41857         this.master = tpls[tpls.length-1];
41858         return this;
41859     },
41860     /**
41861      * same as applyTemplate, except it's done to one of the subTemplates
41862      * when using named templates, you can do:
41863      *
41864      * var str = pl.applySubTemplate('your-name', values);
41865      *
41866      * 
41867      * @param {Number} id of the template
41868      * @param {Object} values to apply to template
41869      * @param {Object} parent (normaly the instance of this object)
41870      */
41871     applySubTemplate : function(id, values, parent)
41872     {
41873         
41874         
41875         var t = this.tpls[id];
41876         
41877         
41878         try { 
41879             if(t.test && !t.test.call(this, values, parent)){
41880                 return '';
41881             }
41882         } catch(e) {
41883             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
41884             Roo.log(e.toString());
41885             Roo.log(t.test);
41886             return ''
41887         }
41888         try { 
41889             
41890             if(t.exec && t.exec.call(this, values, parent)){
41891                 return '';
41892             }
41893         } catch(e) {
41894             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
41895             Roo.log(e.toString());
41896             Roo.log(t.exec);
41897             return ''
41898         }
41899         try {
41900             var vs = t.target ? t.target.call(this, values, parent) : values;
41901             parent = t.target ? values : parent;
41902             if(t.target && vs instanceof Array){
41903                 var buf = [];
41904                 for(var i = 0, len = vs.length; i < len; i++){
41905                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
41906                 }
41907                 return buf.join('');
41908             }
41909             return t.compiled.call(this, vs, parent);
41910         } catch (e) {
41911             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
41912             Roo.log(e.toString());
41913             Roo.log(t.compiled);
41914             return '';
41915         }
41916     },
41917
41918     compileTpl : function(tpl)
41919     {
41920         var fm = Roo.util.Format;
41921         var useF = this.disableFormats !== true;
41922         var sep = Roo.isGecko ? "+" : ",";
41923         var undef = function(str) {
41924             Roo.log("Property not found :"  + str);
41925             return '';
41926         };
41927         
41928         var fn = function(m, name, format, args)
41929         {
41930             //Roo.log(arguments);
41931             args = args ? args.replace(/\\'/g,"'") : args;
41932             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
41933             if (typeof(format) == 'undefined') {
41934                 format= 'htmlEncode';
41935             }
41936             if (format == 'raw' ) {
41937                 format = false;
41938             }
41939             
41940             if(name.substr(0, 4) == 'xtpl'){
41941                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
41942             }
41943             
41944             // build an array of options to determine if value is undefined..
41945             
41946             // basically get 'xxxx.yyyy' then do
41947             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
41948             //    (function () { Roo.log("Property not found"); return ''; })() :
41949             //    ......
41950             
41951             var udef_ar = [];
41952             var lookfor = '';
41953             Roo.each(name.split('.'), function(st) {
41954                 lookfor += (lookfor.length ? '.': '') + st;
41955                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
41956             });
41957             
41958             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
41959             
41960             
41961             if(format && useF){
41962                 
41963                 args = args ? ',' + args : "";
41964                  
41965                 if(format.substr(0, 5) != "this."){
41966                     format = "fm." + format + '(';
41967                 }else{
41968                     format = 'this.call("'+ format.substr(5) + '", ';
41969                     args = ", values";
41970                 }
41971                 
41972                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
41973             }
41974              
41975             if (args.length) {
41976                 // called with xxyx.yuu:(test,test)
41977                 // change to ()
41978                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
41979             }
41980             // raw.. - :raw modifier..
41981             return "'"+ sep + udef_st  + name + ")"+sep+"'";
41982             
41983         };
41984         var body;
41985         // branched to use + in gecko and [].join() in others
41986         if(Roo.isGecko){
41987             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
41988                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
41989                     "';};};";
41990         }else{
41991             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
41992             body.push(tpl.body.replace(/(\r\n|\n)/g,
41993                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
41994             body.push("'].join('');};};");
41995             body = body.join('');
41996         }
41997         
41998         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
41999        
42000         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42001         eval(body);
42002         
42003         return this;
42004     },
42005
42006     applyTemplate : function(values){
42007         return this.master.compiled.call(this, values, {});
42008         //var s = this.subs;
42009     },
42010
42011     apply : function(){
42012         return this.applyTemplate.apply(this, arguments);
42013     }
42014
42015  });
42016
42017 Roo.XTemplate.from = function(el){
42018     el = Roo.getDom(el);
42019     return new Roo.XTemplate(el.value || el.innerHTML);
42020 };