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                 Roo.log('run here??????????????');
3638                 Roo.log(status);
3639                 this.proxy.setStatus(status);
3640             }
3641
3642             if(this.afterDragOver){
3643                 /**
3644                  * An empty function by default, but provided so that you can perform a custom action
3645                  * while the dragged item is over the drop target by providing an implementation.
3646                  * @param {Roo.dd.DragDrop} target The drop target
3647                  * @param {Event} e The event object
3648                  * @param {String} id The id of the dragged element
3649                  * @method afterDragOver
3650                  */
3651                 this.afterDragOver(target, e, id);
3652             }
3653         }
3654     },
3655
3656     /**
3657      * An empty function by default, but provided so that you can perform a custom action
3658      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3659      * @param {Roo.dd.DragDrop} target The drop target
3660      * @param {Event} e The event object
3661      * @param {String} id The id of the dragged element
3662      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3663      */
3664     beforeDragOver : function(target, e, id){
3665         return true;
3666     },
3667
3668     // private
3669     onDragOut : function(e, id){
3670         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3671         if(this.beforeDragOut(target, e, id) !== false){
3672             if(target.isNotifyTarget){
3673                 target.notifyOut(this, e, this.dragData);
3674             }
3675             this.proxy.reset();
3676             if(this.afterDragOut){
3677                 /**
3678                  * An empty function by default, but provided so that you can perform a custom action
3679                  * after the dragged item is dragged out of the target without dropping.
3680                  * @param {Roo.dd.DragDrop} target The drop target
3681                  * @param {Event} e The event object
3682                  * @param {String} id The id of the dragged element
3683                  * @method afterDragOut
3684                  */
3685                 this.afterDragOut(target, e, id);
3686             }
3687         }
3688         this.cachedTarget = null;
3689     },
3690
3691     /**
3692      * An empty function by default, but provided so that you can perform a custom action before the dragged
3693      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3694      * @param {Roo.dd.DragDrop} target The drop target
3695      * @param {Event} e The event object
3696      * @param {String} id The id of the dragged element
3697      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3698      */
3699     beforeDragOut : function(target, e, id){
3700         return true;
3701     },
3702     
3703     // private
3704     onDragDrop : function(e, id){
3705         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3706         if(this.beforeDragDrop(target, e, id) !== false){
3707             if(target.isNotifyTarget){
3708                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3709                     this.onValidDrop(target, e, id);
3710                 }else{
3711                     this.onInvalidDrop(target, e, id);
3712                 }
3713             }else{
3714                 this.onValidDrop(target, e, id);
3715             }
3716             
3717             if(this.afterDragDrop){
3718                 /**
3719                  * An empty function by default, but provided so that you can perform a custom action
3720                  * after a valid drag drop has occurred by providing an implementation.
3721                  * @param {Roo.dd.DragDrop} target The drop target
3722                  * @param {Event} e The event object
3723                  * @param {String} id The id of the dropped element
3724                  * @method afterDragDrop
3725                  */
3726                 this.afterDragDrop(target, e, id);
3727             }
3728         }
3729         delete this.cachedTarget;
3730     },
3731
3732     /**
3733      * An empty function by default, but provided so that you can perform a custom action before the dragged
3734      * item is dropped onto the target and optionally cancel the onDragDrop.
3735      * @param {Roo.dd.DragDrop} target The drop target
3736      * @param {Event} e The event object
3737      * @param {String} id The id of the dragged element
3738      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3739      */
3740     beforeDragDrop : function(target, e, id){
3741         return true;
3742     },
3743
3744     // private
3745     onValidDrop : function(target, e, id){
3746         this.hideProxy();
3747         if(this.afterValidDrop){
3748             /**
3749              * An empty function by default, but provided so that you can perform a custom action
3750              * after a valid drop has occurred by providing an implementation.
3751              * @param {Object} target The target DD 
3752              * @param {Event} e The event object
3753              * @param {String} id The id of the dropped element
3754              * @method afterInvalidDrop
3755              */
3756             this.afterValidDrop(target, e, id);
3757         }
3758     },
3759
3760     // private
3761     getRepairXY : function(e, data){
3762         return this.el.getXY();  
3763     },
3764
3765     // private
3766     onInvalidDrop : function(target, e, id){
3767         this.beforeInvalidDrop(target, e, id);
3768         if(this.cachedTarget){
3769             if(this.cachedTarget.isNotifyTarget){
3770                 this.cachedTarget.notifyOut(this, e, this.dragData);
3771             }
3772             this.cacheTarget = null;
3773         }
3774         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3775
3776         if(this.afterInvalidDrop){
3777             /**
3778              * An empty function by default, but provided so that you can perform a custom action
3779              * after an invalid drop has occurred by providing an implementation.
3780              * @param {Event} e The event object
3781              * @param {String} id The id of the dropped element
3782              * @method afterInvalidDrop
3783              */
3784             this.afterInvalidDrop(e, id);
3785         }
3786     },
3787
3788     // private
3789     afterRepair : function(){
3790         if(Roo.enableFx){
3791             this.el.highlight(this.hlColor || "c3daf9");
3792         }
3793         this.dragging = false;
3794     },
3795
3796     /**
3797      * An empty function by default, but provided so that you can perform a custom action after an invalid
3798      * drop has occurred.
3799      * @param {Roo.dd.DragDrop} target The drop target
3800      * @param {Event} e The event object
3801      * @param {String} id The id of the dragged element
3802      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3803      */
3804     beforeInvalidDrop : function(target, e, id){
3805         return true;
3806     },
3807
3808     // private
3809     handleMouseDown : function(e){
3810         if(this.dragging) {
3811             return;
3812         }
3813         var data = this.getDragData(e);
3814         if(data && this.onBeforeDrag(data, e) !== false){
3815             this.dragData = data;
3816             this.proxy.stop();
3817             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3818         } 
3819     },
3820
3821     /**
3822      * An empty function by default, but provided so that you can perform a custom action before the initial
3823      * drag event begins and optionally cancel it.
3824      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3825      * @param {Event} e The event object
3826      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3827      */
3828     onBeforeDrag : function(data, e){
3829         return true;
3830     },
3831
3832     /**
3833      * An empty function by default, but provided so that you can perform a custom action once the initial
3834      * drag event has begun.  The drag cannot be canceled from this function.
3835      * @param {Number} x The x position of the click on the dragged object
3836      * @param {Number} y The y position of the click on the dragged object
3837      */
3838     onStartDrag : Roo.emptyFn,
3839
3840     // private - YUI override
3841     startDrag : function(x, y){
3842         this.proxy.reset();
3843         this.dragging = true;
3844         this.proxy.update("");
3845         this.onInitDrag(x, y);
3846         this.proxy.show();
3847     },
3848
3849     // private
3850     onInitDrag : function(x, y){
3851         var clone = this.el.dom.cloneNode(true);
3852         clone.id = Roo.id(); // prevent duplicate ids
3853         this.proxy.update(clone);
3854         this.onStartDrag(x, y);
3855         return true;
3856     },
3857
3858     /**
3859      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3860      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3861      */
3862     getProxy : function(){
3863         return this.proxy;  
3864     },
3865
3866     /**
3867      * Hides the drag source's {@link Roo.dd.StatusProxy}
3868      */
3869     hideProxy : function(){
3870         this.proxy.hide();  
3871         this.proxy.reset(true);
3872         this.dragging = false;
3873     },
3874
3875     // private
3876     triggerCacheRefresh : function(){
3877         Roo.dd.DDM.refreshCache(this.groups);
3878     },
3879
3880     // private - override to prevent hiding
3881     b4EndDrag: function(e) {
3882     },
3883
3884     // private - override to prevent moving
3885     endDrag : function(e){
3886         this.onEndDrag(this.dragData, e);
3887     },
3888
3889     // private
3890     onEndDrag : function(data, e){
3891     },
3892     
3893     // private - pin to cursor
3894     autoOffset : function(x, y) {
3895         this.setDelta(-12, -20);
3896     }    
3897 });/*
3898  * Based on:
3899  * Ext JS Library 1.1.1
3900  * Copyright(c) 2006-2007, Ext JS, LLC.
3901  *
3902  * Originally Released Under LGPL - original licence link has changed is not relivant.
3903  *
3904  * Fork - LGPL
3905  * <script type="text/javascript">
3906  */
3907
3908
3909 /**
3910  * @class Roo.dd.DropTarget
3911  * @extends Roo.dd.DDTarget
3912  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3913  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3914  * @constructor
3915  * @param {String/HTMLElement/Element} el The container element
3916  * @param {Object} config
3917  */
3918 Roo.dd.DropTarget = function(el, config){
3919     this.el = Roo.get(el);
3920     
3921     var listeners = false; ;
3922     if (config && config.listeners) {
3923         listeners= config.listeners;
3924         delete config.listeners;
3925     }
3926     Roo.apply(this, config);
3927     
3928     if(this.containerScroll){
3929         Roo.dd.ScrollManager.register(this.el);
3930     }
3931     this.addEvents( {
3932          /**
3933          * @scope Roo.dd.DropTarget
3934          */
3935          
3936          /**
3937          * @event enter
3938          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3939          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3940          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3941          * 
3942          * IMPORTANT : it should set this.overClass and this.dropAllowed
3943          * 
3944          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3945          * @param {Event} e The event
3946          * @param {Object} data An object containing arbitrary data supplied by the drag source
3947          */
3948         "enter" : true,
3949         
3950          /**
3951          * @event over
3952          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3953          * This method will be called on every mouse movement while the drag source is over the drop target.
3954          * This default implementation simply returns the dropAllowed config value.
3955          * 
3956          * IMPORTANT : it should set this.dropAllowed
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961          
3962          */
3963         "over" : true,
3964         /**
3965          * @event out
3966          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3967          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3968          * overClass (if any) from the drop element.
3969          * 
3970          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3971          * @param {Event} e The event
3972          * @param {Object} data An object containing arbitrary data supplied by the drag source
3973          */
3974          "out" : true,
3975          
3976         /**
3977          * @event drop
3978          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3979          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3980          * implementation that does something to process the drop event and returns true so that the drag source's
3981          * repair action does not run.
3982          * 
3983          * IMPORTANT : it should set this.success
3984          * 
3985          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3986          * @param {Event} e The event
3987          * @param {Object} data An object containing arbitrary data supplied by the drag source
3988         */
3989          "drop" : true
3990     });
3991             
3992      
3993     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3994         this.el.dom, 
3995         this.ddGroup || this.group,
3996         {
3997             isTarget: true,
3998             listeners : listeners || {} 
3999            
4000         
4001         }
4002     );
4003
4004 };
4005
4006 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
4007     /**
4008      * @cfg {String} overClass
4009      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
4010      */
4011      /**
4012      * @cfg {String} ddGroup
4013      * The drag drop group to handle drop events for
4014      */
4015      
4016     /**
4017      * @cfg {String} dropAllowed
4018      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
4019      */
4020     dropAllowed : "x-dd-drop-ok",
4021     /**
4022      * @cfg {String} dropNotAllowed
4023      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4024      */
4025     dropNotAllowed : "x-dd-drop-nodrop",
4026     /**
4027      * @cfg {boolean} success
4028      * set this after drop listener.. 
4029      */
4030     success : false,
4031     /**
4032      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4033      * if the drop point is valid for over/enter..
4034      */
4035     valid : false,
4036     // private
4037     isTarget : true,
4038
4039     // private
4040     isNotifyTarget : true,
4041     
4042     /**
4043      * @hide
4044      */
4045     notifyEnter : function(dd, e, data)
4046     {
4047         this.valid = true;
4048         this.fireEvent('enter', dd, e, data);
4049         if(this.overClass){
4050             this.el.addClass(this.overClass);
4051         }
4052         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4053             this.valid ? this.dropAllowed : this.dropNotAllowed
4054         );
4055     },
4056
4057     /**
4058      * @hide
4059      */
4060     notifyOver : function(dd, e, data)
4061     {
4062         Roo.log('in?????????????????????????????????');
4063         this.valid = true;
4064         this.fireEvent('over', dd, e, data);
4065         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4066             this.valid ? this.dropAllowed : this.dropNotAllowed
4067         );
4068     },
4069
4070     /**
4071      * @hide
4072      */
4073     notifyOut : function(dd, e, data)
4074     {
4075         this.fireEvent('out', dd, e, data);
4076         if(this.overClass){
4077             this.el.removeClass(this.overClass);
4078         }
4079     },
4080
4081     /**
4082      * @hide
4083      */
4084     notifyDrop : function(dd, e, data)
4085     {
4086         this.success = false;
4087         this.fireEvent('drop', dd, e, data);
4088         return this.success;
4089     }
4090 });/*
4091  * Based on:
4092  * Ext JS Library 1.1.1
4093  * Copyright(c) 2006-2007, Ext JS, LLC.
4094  *
4095  * Originally Released Under LGPL - original licence link has changed is not relivant.
4096  *
4097  * Fork - LGPL
4098  * <script type="text/javascript">
4099  */
4100
4101
4102 /**
4103  * @class Roo.dd.DragZone
4104  * @extends Roo.dd.DragSource
4105  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4106  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4107  * @constructor
4108  * @param {String/HTMLElement/Element} el The container element
4109  * @param {Object} config
4110  */
4111 Roo.dd.DragZone = function(el, config){
4112     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4113     if(this.containerScroll){
4114         Roo.dd.ScrollManager.register(this.el);
4115     }
4116 };
4117
4118 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4119     /**
4120      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4121      * for auto scrolling during drag operations.
4122      */
4123     /**
4124      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4125      * method after a failed drop (defaults to "c3daf9" - light blue)
4126      */
4127
4128     /**
4129      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4130      * for a valid target to drag based on the mouse down. Override this method
4131      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4132      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4133      * @param {EventObject} e The mouse down event
4134      * @return {Object} The dragData
4135      */
4136     getDragData : function(e){
4137         return Roo.dd.Registry.getHandleFromEvent(e);
4138     },
4139     
4140     /**
4141      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4142      * this.dragData.ddel
4143      * @param {Number} x The x position of the click on the dragged object
4144      * @param {Number} y The y position of the click on the dragged object
4145      * @return {Boolean} true to continue the drag, false to cancel
4146      */
4147     onInitDrag : function(x, y){
4148         this.proxy.update(this.dragData.ddel.cloneNode(true));
4149         this.onStartDrag(x, y);
4150         return true;
4151     },
4152     
4153     /**
4154      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4155      */
4156     afterRepair : function(){
4157         if(Roo.enableFx){
4158             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4159         }
4160         this.dragging = false;
4161     },
4162
4163     /**
4164      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4165      * the XY of this.dragData.ddel
4166      * @param {EventObject} e The mouse up event
4167      * @return {Array} The xy location (e.g. [100, 200])
4168      */
4169     getRepairXY : function(e){
4170         return Roo.Element.fly(this.dragData.ddel).getXY();  
4171     }
4172 });/*
4173  * Based on:
4174  * Ext JS Library 1.1.1
4175  * Copyright(c) 2006-2007, Ext JS, LLC.
4176  *
4177  * Originally Released Under LGPL - original licence link has changed is not relivant.
4178  *
4179  * Fork - LGPL
4180  * <script type="text/javascript">
4181  */
4182 /**
4183  * @class Roo.dd.DropZone
4184  * @extends Roo.dd.DropTarget
4185  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4186  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4187  * @constructor
4188  * @param {String/HTMLElement/Element} el The container element
4189  * @param {Object} config
4190  */
4191 Roo.dd.DropZone = function(el, config){
4192     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4193 };
4194
4195 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4196     /**
4197      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4198      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4199      * provide your own custom lookup.
4200      * @param {Event} e The event
4201      * @return {Object} data The custom data
4202      */
4203     getTargetFromEvent : function(e){
4204         return Roo.dd.Registry.getTargetFromEvent(e);
4205     },
4206
4207     /**
4208      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4209      * that it has registered.  This method has no default implementation and should be overridden to provide
4210      * node-specific processing if necessary.
4211      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4212      * {@link #getTargetFromEvent} for this node)
4213      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4214      * @param {Event} e The event
4215      * @param {Object} data An object containing arbitrary data supplied by the drag source
4216      */
4217     onNodeEnter : function(n, dd, e, data){
4218         
4219     },
4220
4221     /**
4222      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4223      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4224      * overridden to provide the proper feedback.
4225      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4226      * {@link #getTargetFromEvent} for this node)
4227      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4228      * @param {Event} e The event
4229      * @param {Object} data An object containing arbitrary data supplied by the drag source
4230      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4231      * underlying {@link Roo.dd.StatusProxy} can be updated
4232      */
4233     onNodeOver : function(n, dd, e, data){
4234         return this.dropAllowed;
4235     },
4236
4237     /**
4238      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4239      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4240      * node-specific processing if necessary.
4241      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4242      * {@link #getTargetFromEvent} for this node)
4243      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4244      * @param {Event} e The event
4245      * @param {Object} data An object containing arbitrary data supplied by the drag source
4246      */
4247     onNodeOut : function(n, dd, e, data){
4248         
4249     },
4250
4251     /**
4252      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4253      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4254      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4255      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4256      * {@link #getTargetFromEvent} for this node)
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onNodeDrop : function(n, dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4268      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4269      * it should be overridden to provide the proper feedback if necessary.
4270      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4271      * @param {Event} e The event
4272      * @param {Object} data An object containing arbitrary data supplied by the drag source
4273      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4274      * underlying {@link Roo.dd.StatusProxy} can be updated
4275      */
4276     onContainerOver : function(dd, e, data){
4277         return this.dropNotAllowed;
4278     },
4279
4280     /**
4281      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4282      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4283      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4284      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4285      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4286      * @param {Event} e The event
4287      * @param {Object} data An object containing arbitrary data supplied by the drag source
4288      * @return {Boolean} True if the drop was valid, else false
4289      */
4290     onContainerDrop : function(dd, e, data){
4291         return false;
4292     },
4293
4294     /**
4295      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4296      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4297      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4298      * you should override this method and provide a custom implementation.
4299      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4300      * @param {Event} e The event
4301      * @param {Object} data An object containing arbitrary data supplied by the drag source
4302      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4303      * underlying {@link Roo.dd.StatusProxy} can be updated
4304      */
4305     notifyEnter : function(dd, e, data){
4306         return this.dropNotAllowed;
4307     },
4308
4309     /**
4310      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4311      * This method will be called on every mouse movement while the drag source is over the drop zone.
4312      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4313      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4314      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4315      * registered node, it will call {@link #onContainerOver}.
4316      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4317      * @param {Event} e The event
4318      * @param {Object} data An object containing arbitrary data supplied by the drag source
4319      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4320      * underlying {@link Roo.dd.StatusProxy} can be updated
4321      */
4322     notifyOver : function(dd, e, data){
4323         var n = this.getTargetFromEvent(e);
4324         if(!n){ // not over valid drop target
4325             if(this.lastOverNode){
4326                 this.onNodeOut(this.lastOverNode, dd, e, data);
4327                 this.lastOverNode = null;
4328             }
4329             return this.onContainerOver(dd, e, data);
4330         }
4331         if(this.lastOverNode != n){
4332             if(this.lastOverNode){
4333                 this.onNodeOut(this.lastOverNode, dd, e, data);
4334             }
4335             this.onNodeEnter(n, dd, e, data);
4336             this.lastOverNode = n;
4337         }
4338         return this.onNodeOver(n, dd, e, data);
4339     },
4340
4341     /**
4342      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4343      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4344      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4345      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4346      * @param {Event} e The event
4347      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4348      */
4349     notifyOut : function(dd, e, data){
4350         if(this.lastOverNode){
4351             this.onNodeOut(this.lastOverNode, dd, e, data);
4352             this.lastOverNode = null;
4353         }
4354     },
4355
4356     /**
4357      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4358      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4359      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4360      * otherwise it will call {@link #onContainerDrop}.
4361      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4362      * @param {Event} e The event
4363      * @param {Object} data An object containing arbitrary data supplied by the drag source
4364      * @return {Boolean} True if the drop was valid, else false
4365      */
4366     notifyDrop : function(dd, e, data){
4367         if(this.lastOverNode){
4368             this.onNodeOut(this.lastOverNode, dd, e, data);
4369             this.lastOverNode = null;
4370         }
4371         var n = this.getTargetFromEvent(e);
4372         return n ?
4373             this.onNodeDrop(n, dd, e, data) :
4374             this.onContainerDrop(dd, e, data);
4375     },
4376
4377     // private
4378     triggerCacheRefresh : function(){
4379         Roo.dd.DDM.refreshCache(this.groups);
4380     }  
4381 });/*
4382  * Based on:
4383  * Ext JS Library 1.1.1
4384  * Copyright(c) 2006-2007, Ext JS, LLC.
4385  *
4386  * Originally Released Under LGPL - original licence link has changed is not relivant.
4387  *
4388  * Fork - LGPL
4389  * <script type="text/javascript">
4390  */
4391
4392
4393 /**
4394  * @class Roo.data.SortTypes
4395  * @singleton
4396  * Defines the default sorting (casting?) comparison functions used when sorting data.
4397  */
4398 Roo.data.SortTypes = {
4399     /**
4400      * Default sort that does nothing
4401      * @param {Mixed} s The value being converted
4402      * @return {Mixed} The comparison value
4403      */
4404     none : function(s){
4405         return s;
4406     },
4407     
4408     /**
4409      * The regular expression used to strip tags
4410      * @type {RegExp}
4411      * @property
4412      */
4413     stripTagsRE : /<\/?[^>]+>/gi,
4414     
4415     /**
4416      * Strips all HTML tags to sort on text only
4417      * @param {Mixed} s The value being converted
4418      * @return {String} The comparison value
4419      */
4420     asText : function(s){
4421         return String(s).replace(this.stripTagsRE, "");
4422     },
4423     
4424     /**
4425      * Strips all HTML tags to sort on text only - Case insensitive
4426      * @param {Mixed} s The value being converted
4427      * @return {String} The comparison value
4428      */
4429     asUCText : function(s){
4430         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4431     },
4432     
4433     /**
4434      * Case insensitive string
4435      * @param {Mixed} s The value being converted
4436      * @return {String} The comparison value
4437      */
4438     asUCString : function(s) {
4439         return String(s).toUpperCase();
4440     },
4441     
4442     /**
4443      * Date sorting
4444      * @param {Mixed} s The value being converted
4445      * @return {Number} The comparison value
4446      */
4447     asDate : function(s) {
4448         if(!s){
4449             return 0;
4450         }
4451         if(s instanceof Date){
4452             return s.getTime();
4453         }
4454         return Date.parse(String(s));
4455     },
4456     
4457     /**
4458      * Float sorting
4459      * @param {Mixed} s The value being converted
4460      * @return {Float} The comparison value
4461      */
4462     asFloat : function(s) {
4463         var val = parseFloat(String(s).replace(/,/g, ""));
4464         if(isNaN(val)) val = 0;
4465         return val;
4466     },
4467     
4468     /**
4469      * Integer sorting
4470      * @param {Mixed} s The value being converted
4471      * @return {Number} The comparison value
4472      */
4473     asInt : function(s) {
4474         var val = parseInt(String(s).replace(/,/g, ""));
4475         if(isNaN(val)) val = 0;
4476         return val;
4477     }
4478 };/*
4479  * Based on:
4480  * Ext JS Library 1.1.1
4481  * Copyright(c) 2006-2007, Ext JS, LLC.
4482  *
4483  * Originally Released Under LGPL - original licence link has changed is not relivant.
4484  *
4485  * Fork - LGPL
4486  * <script type="text/javascript">
4487  */
4488
4489 /**
4490 * @class Roo.data.Record
4491  * Instances of this class encapsulate both record <em>definition</em> information, and record
4492  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4493  * to access Records cached in an {@link Roo.data.Store} object.<br>
4494  * <p>
4495  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4496  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4497  * objects.<br>
4498  * <p>
4499  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4500  * @constructor
4501  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4502  * {@link #create}. The parameters are the same.
4503  * @param {Array} data An associative Array of data values keyed by the field name.
4504  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4505  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4506  * not specified an integer id is generated.
4507  */
4508 Roo.data.Record = function(data, id){
4509     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4510     this.data = data;
4511 };
4512
4513 /**
4514  * Generate a constructor for a specific record layout.
4515  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4516  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4517  * Each field definition object may contain the following properties: <ul>
4518  * <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,
4519  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4520  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4521  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4522  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4523  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4524  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4525  * this may be omitted.</p></li>
4526  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4527  * <ul><li>auto (Default, implies no conversion)</li>
4528  * <li>string</li>
4529  * <li>int</li>
4530  * <li>float</li>
4531  * <li>boolean</li>
4532  * <li>date</li></ul></p></li>
4533  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4534  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4535  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4536  * by the Reader into an object that will be stored in the Record. It is passed the
4537  * following parameters:<ul>
4538  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4539  * </ul></p></li>
4540  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4541  * </ul>
4542  * <br>usage:<br><pre><code>
4543 var TopicRecord = Roo.data.Record.create(
4544     {name: 'title', mapping: 'topic_title'},
4545     {name: 'author', mapping: 'username'},
4546     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4547     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4548     {name: 'lastPoster', mapping: 'user2'},
4549     {name: 'excerpt', mapping: 'post_text'}
4550 );
4551
4552 var myNewRecord = new TopicRecord({
4553     title: 'Do my job please',
4554     author: 'noobie',
4555     totalPosts: 1,
4556     lastPost: new Date(),
4557     lastPoster: 'Animal',
4558     excerpt: 'No way dude!'
4559 });
4560 myStore.add(myNewRecord);
4561 </code></pre>
4562  * @method create
4563  * @static
4564  */
4565 Roo.data.Record.create = function(o){
4566     var f = function(){
4567         f.superclass.constructor.apply(this, arguments);
4568     };
4569     Roo.extend(f, Roo.data.Record);
4570     var p = f.prototype;
4571     p.fields = new Roo.util.MixedCollection(false, function(field){
4572         return field.name;
4573     });
4574     for(var i = 0, len = o.length; i < len; i++){
4575         p.fields.add(new Roo.data.Field(o[i]));
4576     }
4577     f.getField = function(name){
4578         return p.fields.get(name);  
4579     };
4580     return f;
4581 };
4582
4583 Roo.data.Record.AUTO_ID = 1000;
4584 Roo.data.Record.EDIT = 'edit';
4585 Roo.data.Record.REJECT = 'reject';
4586 Roo.data.Record.COMMIT = 'commit';
4587
4588 Roo.data.Record.prototype = {
4589     /**
4590      * Readonly flag - true if this record has been modified.
4591      * @type Boolean
4592      */
4593     dirty : false,
4594     editing : false,
4595     error: null,
4596     modified: null,
4597
4598     // private
4599     join : function(store){
4600         this.store = store;
4601     },
4602
4603     /**
4604      * Set the named field to the specified value.
4605      * @param {String} name The name of the field to set.
4606      * @param {Object} value The value to set the field to.
4607      */
4608     set : function(name, value){
4609         if(this.data[name] == value){
4610             return;
4611         }
4612         this.dirty = true;
4613         if(!this.modified){
4614             this.modified = {};
4615         }
4616         if(typeof this.modified[name] == 'undefined'){
4617             this.modified[name] = this.data[name];
4618         }
4619         this.data[name] = value;
4620         if(!this.editing && this.store){
4621             this.store.afterEdit(this);
4622         }       
4623     },
4624
4625     /**
4626      * Get the value of the named field.
4627      * @param {String} name The name of the field to get the value of.
4628      * @return {Object} The value of the field.
4629      */
4630     get : function(name){
4631         return this.data[name]; 
4632     },
4633
4634     // private
4635     beginEdit : function(){
4636         this.editing = true;
4637         this.modified = {}; 
4638     },
4639
4640     // private
4641     cancelEdit : function(){
4642         this.editing = false;
4643         delete this.modified;
4644     },
4645
4646     // private
4647     endEdit : function(){
4648         this.editing = false;
4649         if(this.dirty && this.store){
4650             this.store.afterEdit(this);
4651         }
4652     },
4653
4654     /**
4655      * Usually called by the {@link Roo.data.Store} which owns the Record.
4656      * Rejects all changes made to the Record since either creation, or the last commit operation.
4657      * Modified fields are reverted to their original values.
4658      * <p>
4659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4660      * of reject operations.
4661      */
4662     reject : function(){
4663         var m = this.modified;
4664         for(var n in m){
4665             if(typeof m[n] != "function"){
4666                 this.data[n] = m[n];
4667             }
4668         }
4669         this.dirty = false;
4670         delete this.modified;
4671         this.editing = false;
4672         if(this.store){
4673             this.store.afterReject(this);
4674         }
4675     },
4676
4677     /**
4678      * Usually called by the {@link Roo.data.Store} which owns the Record.
4679      * Commits all changes made to the Record since either creation, or the last commit operation.
4680      * <p>
4681      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4682      * of commit operations.
4683      */
4684     commit : function(){
4685         this.dirty = false;
4686         delete this.modified;
4687         this.editing = false;
4688         if(this.store){
4689             this.store.afterCommit(this);
4690         }
4691     },
4692
4693     // private
4694     hasError : function(){
4695         return this.error != null;
4696     },
4697
4698     // private
4699     clearError : function(){
4700         this.error = null;
4701     },
4702
4703     /**
4704      * Creates a copy of this record.
4705      * @param {String} id (optional) A new record id if you don't want to use this record's id
4706      * @return {Record}
4707      */
4708     copy : function(newId) {
4709         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4710     }
4711 };/*
4712  * Based on:
4713  * Ext JS Library 1.1.1
4714  * Copyright(c) 2006-2007, Ext JS, LLC.
4715  *
4716  * Originally Released Under LGPL - original licence link has changed is not relivant.
4717  *
4718  * Fork - LGPL
4719  * <script type="text/javascript">
4720  */
4721
4722
4723
4724 /**
4725  * @class Roo.data.Store
4726  * @extends Roo.util.Observable
4727  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4728  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4729  * <p>
4730  * 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
4731  * has no knowledge of the format of the data returned by the Proxy.<br>
4732  * <p>
4733  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4734  * instances from the data object. These records are cached and made available through accessor functions.
4735  * @constructor
4736  * Creates a new Store.
4737  * @param {Object} config A config object containing the objects needed for the Store to access data,
4738  * and read the data into Records.
4739  */
4740 Roo.data.Store = function(config){
4741     this.data = new Roo.util.MixedCollection(false);
4742     this.data.getKey = function(o){
4743         return o.id;
4744     };
4745     this.baseParams = {};
4746     // private
4747     this.paramNames = {
4748         "start" : "start",
4749         "limit" : "limit",
4750         "sort" : "sort",
4751         "dir" : "dir",
4752         "multisort" : "_multisort"
4753     };
4754
4755     if(config && config.data){
4756         this.inlineData = config.data;
4757         delete config.data;
4758     }
4759
4760     Roo.apply(this, config);
4761     
4762     if(this.reader){ // reader passed
4763         this.reader = Roo.factory(this.reader, Roo.data);
4764         this.reader.xmodule = this.xmodule || false;
4765         if(!this.recordType){
4766             this.recordType = this.reader.recordType;
4767         }
4768         if(this.reader.onMetaChange){
4769             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4770         }
4771     }
4772
4773     if(this.recordType){
4774         this.fields = this.recordType.prototype.fields;
4775     }
4776     this.modified = [];
4777
4778     this.addEvents({
4779         /**
4780          * @event datachanged
4781          * Fires when the data cache has changed, and a widget which is using this Store
4782          * as a Record cache should refresh its view.
4783          * @param {Store} this
4784          */
4785         datachanged : true,
4786         /**
4787          * @event metachange
4788          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4789          * @param {Store} this
4790          * @param {Object} meta The JSON metadata
4791          */
4792         metachange : true,
4793         /**
4794          * @event add
4795          * Fires when Records have been added to the Store
4796          * @param {Store} this
4797          * @param {Roo.data.Record[]} records The array of Records added
4798          * @param {Number} index The index at which the record(s) were added
4799          */
4800         add : true,
4801         /**
4802          * @event remove
4803          * Fires when a Record has been removed from the Store
4804          * @param {Store} this
4805          * @param {Roo.data.Record} record The Record that was removed
4806          * @param {Number} index The index at which the record was removed
4807          */
4808         remove : true,
4809         /**
4810          * @event update
4811          * Fires when a Record has been updated
4812          * @param {Store} this
4813          * @param {Roo.data.Record} record The Record that was updated
4814          * @param {String} operation The update operation being performed.  Value may be one of:
4815          * <pre><code>
4816  Roo.data.Record.EDIT
4817  Roo.data.Record.REJECT
4818  Roo.data.Record.COMMIT
4819          * </code></pre>
4820          */
4821         update : true,
4822         /**
4823          * @event clear
4824          * Fires when the data cache has been cleared.
4825          * @param {Store} this
4826          */
4827         clear : true,
4828         /**
4829          * @event beforeload
4830          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4831          * the load action will be canceled.
4832          * @param {Store} this
4833          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4834          */
4835         beforeload : true,
4836         /**
4837          * @event beforeloadadd
4838          * Fires after a new set of Records has been loaded.
4839          * @param {Store} this
4840          * @param {Roo.data.Record[]} records The Records that were loaded
4841          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4842          */
4843         beforeloadadd : true,
4844         /**
4845          * @event load
4846          * Fires after a new set of Records has been loaded, before they are added to the store.
4847          * @param {Store} this
4848          * @param {Roo.data.Record[]} records The Records that were loaded
4849          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4850          * @params {Object} return from reader
4851          */
4852         load : true,
4853         /**
4854          * @event loadexception
4855          * Fires if an exception occurs in the Proxy during loading.
4856          * Called with the signature of the Proxy's "loadexception" event.
4857          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4858          * 
4859          * @param {Proxy} 
4860          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4861          * @param {Object} load options 
4862          * @param {Object} jsonData from your request (normally this contains the Exception)
4863          */
4864         loadexception : true
4865     });
4866     
4867     if(this.proxy){
4868         this.proxy = Roo.factory(this.proxy, Roo.data);
4869         this.proxy.xmodule = this.xmodule || false;
4870         this.relayEvents(this.proxy,  ["loadexception"]);
4871     }
4872     this.sortToggle = {};
4873     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4874
4875     Roo.data.Store.superclass.constructor.call(this);
4876
4877     if(this.inlineData){
4878         this.loadData(this.inlineData);
4879         delete this.inlineData;
4880     }
4881 };
4882
4883 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4884      /**
4885     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4886     * without a remote query - used by combo/forms at present.
4887     */
4888     
4889     /**
4890     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4891     */
4892     /**
4893     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4894     */
4895     /**
4896     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4897     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4898     */
4899     /**
4900     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4901     * on any HTTP request
4902     */
4903     /**
4904     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4905     */
4906     /**
4907     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4908     */
4909     multiSort: false,
4910     /**
4911     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4912     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4913     */
4914     remoteSort : false,
4915
4916     /**
4917     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4918      * loaded or when a record is removed. (defaults to false).
4919     */
4920     pruneModifiedRecords : false,
4921
4922     // private
4923     lastOptions : null,
4924
4925     /**
4926      * Add Records to the Store and fires the add event.
4927      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4928      */
4929     add : function(records){
4930         records = [].concat(records);
4931         for(var i = 0, len = records.length; i < len; i++){
4932             records[i].join(this);
4933         }
4934         var index = this.data.length;
4935         this.data.addAll(records);
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Remove a Record from the Store and fires the remove event.
4941      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4942      */
4943     remove : function(record){
4944         var index = this.data.indexOf(record);
4945         this.data.removeAt(index);
4946         if(this.pruneModifiedRecords){
4947             this.modified.remove(record);
4948         }
4949         this.fireEvent("remove", this, record, index);
4950     },
4951
4952     /**
4953      * Remove all Records from the Store and fires the clear event.
4954      */
4955     removeAll : function(){
4956         this.data.clear();
4957         if(this.pruneModifiedRecords){
4958             this.modified = [];
4959         }
4960         this.fireEvent("clear", this);
4961     },
4962
4963     /**
4964      * Inserts Records to the Store at the given index and fires the add event.
4965      * @param {Number} index The start index at which to insert the passed Records.
4966      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4967      */
4968     insert : function(index, records){
4969         records = [].concat(records);
4970         for(var i = 0, len = records.length; i < len; i++){
4971             this.data.insert(index, records[i]);
4972             records[i].join(this);
4973         }
4974         this.fireEvent("add", this, records, index);
4975     },
4976
4977     /**
4978      * Get the index within the cache of the passed Record.
4979      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4980      * @return {Number} The index of the passed Record. Returns -1 if not found.
4981      */
4982     indexOf : function(record){
4983         return this.data.indexOf(record);
4984     },
4985
4986     /**
4987      * Get the index within the cache of the Record with the passed id.
4988      * @param {String} id The id of the Record to find.
4989      * @return {Number} The index of the Record. Returns -1 if not found.
4990      */
4991     indexOfId : function(id){
4992         return this.data.indexOfKey(id);
4993     },
4994
4995     /**
4996      * Get the Record with the specified id.
4997      * @param {String} id The id of the Record to find.
4998      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4999      */
5000     getById : function(id){
5001         return this.data.key(id);
5002     },
5003
5004     /**
5005      * Get the Record at the specified index.
5006      * @param {Number} index The index of the Record to find.
5007      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
5008      */
5009     getAt : function(index){
5010         return this.data.itemAt(index);
5011     },
5012
5013     /**
5014      * Returns a range of Records between specified indices.
5015      * @param {Number} startIndex (optional) The starting index (defaults to 0)
5016      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
5017      * @return {Roo.data.Record[]} An array of Records
5018      */
5019     getRange : function(start, end){
5020         return this.data.getRange(start, end);
5021     },
5022
5023     // private
5024     storeOptions : function(o){
5025         o = Roo.apply({}, o);
5026         delete o.callback;
5027         delete o.scope;
5028         this.lastOptions = o;
5029     },
5030
5031     /**
5032      * Loads the Record cache from the configured Proxy using the configured Reader.
5033      * <p>
5034      * If using remote paging, then the first load call must specify the <em>start</em>
5035      * and <em>limit</em> properties in the options.params property to establish the initial
5036      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5037      * <p>
5038      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5039      * and this call will return before the new data has been loaded. Perform any post-processing
5040      * in a callback function, or in a "load" event handler.</strong>
5041      * <p>
5042      * @param {Object} options An object containing properties which control loading options:<ul>
5043      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5044      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5045      * passed the following arguments:<ul>
5046      * <li>r : Roo.data.Record[]</li>
5047      * <li>options: Options object from the load call</li>
5048      * <li>success: Boolean success indicator</li></ul></li>
5049      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5050      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5051      * </ul>
5052      */
5053     load : function(options){
5054         options = options || {};
5055         if(this.fireEvent("beforeload", this, options) !== false){
5056             this.storeOptions(options);
5057             var p = Roo.apply(options.params || {}, this.baseParams);
5058             // if meta was not loaded from remote source.. try requesting it.
5059             if (!this.reader.metaFromRemote) {
5060                 p._requestMeta = 1;
5061             }
5062             if(this.sortInfo && this.remoteSort){
5063                 var pn = this.paramNames;
5064                 p[pn["sort"]] = this.sortInfo.field;
5065                 p[pn["dir"]] = this.sortInfo.direction;
5066             }
5067             if (this.multiSort) {
5068                 var pn = this.paramNames;
5069                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5070             }
5071             
5072             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5073         }
5074     },
5075
5076     /**
5077      * Reloads the Record cache from the configured Proxy using the configured Reader and
5078      * the options from the last load operation performed.
5079      * @param {Object} options (optional) An object containing properties which may override the options
5080      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5081      * the most recently used options are reused).
5082      */
5083     reload : function(options){
5084         this.load(Roo.applyIf(options||{}, this.lastOptions));
5085     },
5086
5087     // private
5088     // Called as a callback by the Reader during a load operation.
5089     loadRecords : function(o, options, success){
5090         if(!o || success === false){
5091             if(success !== false){
5092                 this.fireEvent("load", this, [], options, o);
5093             }
5094             if(options.callback){
5095                 options.callback.call(options.scope || this, [], options, false);
5096             }
5097             return;
5098         }
5099         // if data returned failure - throw an exception.
5100         if (o.success === false) {
5101             // show a message if no listener is registered.
5102             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5103                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5104             }
5105             // loadmask wil be hooked into this..
5106             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5107             return;
5108         }
5109         var r = o.records, t = o.totalRecords || r.length;
5110         
5111         this.fireEvent("beforeloadadd", this, r, options, o);
5112         
5113         if(!options || options.add !== true){
5114             if(this.pruneModifiedRecords){
5115                 this.modified = [];
5116             }
5117             for(var i = 0, len = r.length; i < len; i++){
5118                 r[i].join(this);
5119             }
5120             if(this.snapshot){
5121                 this.data = this.snapshot;
5122                 delete this.snapshot;
5123             }
5124             this.data.clear();
5125             this.data.addAll(r);
5126             this.totalLength = t;
5127             this.applySort();
5128             this.fireEvent("datachanged", this);
5129         }else{
5130             this.totalLength = Math.max(t, this.data.length+r.length);
5131             this.add(r);
5132         }
5133         this.fireEvent("load", this, r, options, o);
5134         if(options.callback){
5135             options.callback.call(options.scope || this, r, options, true);
5136         }
5137     },
5138
5139
5140     /**
5141      * Loads data from a passed data block. A Reader which understands the format of the data
5142      * must have been configured in the constructor.
5143      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5144      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5145      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5146      */
5147     loadData : function(o, append){
5148         var r = this.reader.readRecords(o);
5149         this.loadRecords(r, {add: append}, true);
5150     },
5151
5152     /**
5153      * Gets the number of cached records.
5154      * <p>
5155      * <em>If using paging, this may not be the total size of the dataset. If the data object
5156      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5157      * the data set size</em>
5158      */
5159     getCount : function(){
5160         return this.data.length || 0;
5161     },
5162
5163     /**
5164      * Gets the total number of records in the dataset as returned by the server.
5165      * <p>
5166      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5167      * the dataset size</em>
5168      */
5169     getTotalCount : function(){
5170         return this.totalLength || 0;
5171     },
5172
5173     /**
5174      * Returns the sort state of the Store as an object with two properties:
5175      * <pre><code>
5176  field {String} The name of the field by which the Records are sorted
5177  direction {String} The sort order, "ASC" or "DESC"
5178      * </code></pre>
5179      */
5180     getSortState : function(){
5181         return this.sortInfo;
5182     },
5183
5184     // private
5185     applySort : function(){
5186         if(this.sortInfo && !this.remoteSort){
5187             var s = this.sortInfo, f = s.field;
5188             var st = this.fields.get(f).sortType;
5189             var fn = function(r1, r2){
5190                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5191                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5192             };
5193             this.data.sort(s.direction, fn);
5194             if(this.snapshot && this.snapshot != this.data){
5195                 this.snapshot.sort(s.direction, fn);
5196             }
5197         }
5198     },
5199
5200     /**
5201      * Sets the default sort column and order to be used by the next load operation.
5202      * @param {String} fieldName The name of the field to sort by.
5203      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5204      */
5205     setDefaultSort : function(field, dir){
5206         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5207     },
5208
5209     /**
5210      * Sort the Records.
5211      * If remote sorting is used, the sort is performed on the server, and the cache is
5212      * reloaded. If local sorting is used, the cache is sorted internally.
5213      * @param {String} fieldName The name of the field to sort by.
5214      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5215      */
5216     sort : function(fieldName, dir){
5217         var f = this.fields.get(fieldName);
5218         if(!dir){
5219             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5220             
5221             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5222                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5223             }else{
5224                 dir = f.sortDir;
5225             }
5226         }
5227         this.sortToggle[f.name] = dir;
5228         this.sortInfo = {field: f.name, direction: dir};
5229         if(!this.remoteSort){
5230             this.applySort();
5231             this.fireEvent("datachanged", this);
5232         }else{
5233             this.load(this.lastOptions);
5234         }
5235     },
5236
5237     /**
5238      * Calls the specified function for each of the Records in the cache.
5239      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5240      * Returning <em>false</em> aborts and exits the iteration.
5241      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5242      */
5243     each : function(fn, scope){
5244         this.data.each(fn, scope);
5245     },
5246
5247     /**
5248      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5249      * (e.g., during paging).
5250      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5251      */
5252     getModifiedRecords : function(){
5253         return this.modified;
5254     },
5255
5256     // private
5257     createFilterFn : function(property, value, anyMatch){
5258         if(!value.exec){ // not a regex
5259             value = String(value);
5260             if(value.length == 0){
5261                 return false;
5262             }
5263             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5264         }
5265         return function(r){
5266             return value.test(r.data[property]);
5267         };
5268     },
5269
5270     /**
5271      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5272      * @param {String} property A field on your records
5273      * @param {Number} start The record index to start at (defaults to 0)
5274      * @param {Number} end The last record index to include (defaults to length - 1)
5275      * @return {Number} The sum
5276      */
5277     sum : function(property, start, end){
5278         var rs = this.data.items, v = 0;
5279         start = start || 0;
5280         end = (end || end === 0) ? end : rs.length-1;
5281
5282         for(var i = start; i <= end; i++){
5283             v += (rs[i].data[property] || 0);
5284         }
5285         return v;
5286     },
5287
5288     /**
5289      * Filter the records by a specified property.
5290      * @param {String} field A field on your records
5291      * @param {String/RegExp} value Either a string that the field
5292      * should start with or a RegExp to test against the field
5293      * @param {Boolean} anyMatch True to match any part not just the beginning
5294      */
5295     filter : function(property, value, anyMatch){
5296         var fn = this.createFilterFn(property, value, anyMatch);
5297         return fn ? this.filterBy(fn) : this.clearFilter();
5298     },
5299
5300     /**
5301      * Filter by a function. The specified function will be called with each
5302      * record in this data source. If the function returns true the record is included,
5303      * otherwise it is filtered.
5304      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5305      * @param {Object} scope (optional) The scope of the function (defaults to this)
5306      */
5307     filterBy : function(fn, scope){
5308         this.snapshot = this.snapshot || this.data;
5309         this.data = this.queryBy(fn, scope||this);
5310         this.fireEvent("datachanged", this);
5311     },
5312
5313     /**
5314      * Query the records by a specified property.
5315      * @param {String} field A field on your records
5316      * @param {String/RegExp} value Either a string that the field
5317      * should start with or a RegExp to test against the field
5318      * @param {Boolean} anyMatch True to match any part not just the beginning
5319      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5320      */
5321     query : function(property, value, anyMatch){
5322         var fn = this.createFilterFn(property, value, anyMatch);
5323         return fn ? this.queryBy(fn) : this.data.clone();
5324     },
5325
5326     /**
5327      * Query by a function. The specified function will be called with each
5328      * record in this data source. If the function returns true the record is included
5329      * in the results.
5330      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5331      * @param {Object} scope (optional) The scope of the function (defaults to this)
5332       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5333      **/
5334     queryBy : function(fn, scope){
5335         var data = this.snapshot || this.data;
5336         return data.filterBy(fn, scope||this);
5337     },
5338
5339     /**
5340      * Collects unique values for a particular dataIndex from this store.
5341      * @param {String} dataIndex The property to collect
5342      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5343      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5344      * @return {Array} An array of the unique values
5345      **/
5346     collect : function(dataIndex, allowNull, bypassFilter){
5347         var d = (bypassFilter === true && this.snapshot) ?
5348                 this.snapshot.items : this.data.items;
5349         var v, sv, r = [], l = {};
5350         for(var i = 0, len = d.length; i < len; i++){
5351             v = d[i].data[dataIndex];
5352             sv = String(v);
5353             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5354                 l[sv] = true;
5355                 r[r.length] = v;
5356             }
5357         }
5358         return r;
5359     },
5360
5361     /**
5362      * Revert to a view of the Record cache with no filtering applied.
5363      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5364      */
5365     clearFilter : function(suppressEvent){
5366         if(this.snapshot && this.snapshot != this.data){
5367             this.data = this.snapshot;
5368             delete this.snapshot;
5369             if(suppressEvent !== true){
5370                 this.fireEvent("datachanged", this);
5371             }
5372         }
5373     },
5374
5375     // private
5376     afterEdit : function(record){
5377         if(this.modified.indexOf(record) == -1){
5378             this.modified.push(record);
5379         }
5380         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5381     },
5382     
5383     // private
5384     afterReject : function(record){
5385         this.modified.remove(record);
5386         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5387     },
5388
5389     // private
5390     afterCommit : function(record){
5391         this.modified.remove(record);
5392         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5393     },
5394
5395     /**
5396      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5397      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5398      */
5399     commitChanges : function(){
5400         var m = this.modified.slice(0);
5401         this.modified = [];
5402         for(var i = 0, len = m.length; i < len; i++){
5403             m[i].commit();
5404         }
5405     },
5406
5407     /**
5408      * Cancel outstanding changes on all changed records.
5409      */
5410     rejectChanges : function(){
5411         var m = this.modified.slice(0);
5412         this.modified = [];
5413         for(var i = 0, len = m.length; i < len; i++){
5414             m[i].reject();
5415         }
5416     },
5417
5418     onMetaChange : function(meta, rtype, o){
5419         this.recordType = rtype;
5420         this.fields = rtype.prototype.fields;
5421         delete this.snapshot;
5422         this.sortInfo = meta.sortInfo || this.sortInfo;
5423         this.modified = [];
5424         this.fireEvent('metachange', this, this.reader.meta);
5425     },
5426     
5427     moveIndex : function(data, type)
5428     {
5429         var index = this.indexOf(data);
5430         
5431         var newIndex = index + type;
5432         
5433         this.remove(data);
5434         
5435         this.insert(newIndex, data);
5436         
5437     }
5438 });/*
5439  * Based on:
5440  * Ext JS Library 1.1.1
5441  * Copyright(c) 2006-2007, Ext JS, LLC.
5442  *
5443  * Originally Released Under LGPL - original licence link has changed is not relivant.
5444  *
5445  * Fork - LGPL
5446  * <script type="text/javascript">
5447  */
5448
5449 /**
5450  * @class Roo.data.SimpleStore
5451  * @extends Roo.data.Store
5452  * Small helper class to make creating Stores from Array data easier.
5453  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5454  * @cfg {Array} fields An array of field definition objects, or field name strings.
5455  * @cfg {Array} data The multi-dimensional array of data
5456  * @constructor
5457  * @param {Object} config
5458  */
5459 Roo.data.SimpleStore = function(config){
5460     Roo.data.SimpleStore.superclass.constructor.call(this, {
5461         isLocal : true,
5462         reader: new Roo.data.ArrayReader({
5463                 id: config.id
5464             },
5465             Roo.data.Record.create(config.fields)
5466         ),
5467         proxy : new Roo.data.MemoryProxy(config.data)
5468     });
5469     this.load();
5470 };
5471 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5472  * Based on:
5473  * Ext JS Library 1.1.1
5474  * Copyright(c) 2006-2007, Ext JS, LLC.
5475  *
5476  * Originally Released Under LGPL - original licence link has changed is not relivant.
5477  *
5478  * Fork - LGPL
5479  * <script type="text/javascript">
5480  */
5481
5482 /**
5483 /**
5484  * @extends Roo.data.Store
5485  * @class Roo.data.JsonStore
5486  * Small helper class to make creating Stores for JSON data easier. <br/>
5487 <pre><code>
5488 var store = new Roo.data.JsonStore({
5489     url: 'get-images.php',
5490     root: 'images',
5491     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5492 });
5493 </code></pre>
5494  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5495  * JsonReader and HttpProxy (unless inline data is provided).</b>
5496  * @cfg {Array} fields An array of field definition objects, or field name strings.
5497  * @constructor
5498  * @param {Object} config
5499  */
5500 Roo.data.JsonStore = function(c){
5501     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5502         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5503         reader: new Roo.data.JsonReader(c, c.fields)
5504     }));
5505 };
5506 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5507  * Based on:
5508  * Ext JS Library 1.1.1
5509  * Copyright(c) 2006-2007, Ext JS, LLC.
5510  *
5511  * Originally Released Under LGPL - original licence link has changed is not relivant.
5512  *
5513  * Fork - LGPL
5514  * <script type="text/javascript">
5515  */
5516
5517  
5518 Roo.data.Field = function(config){
5519     if(typeof config == "string"){
5520         config = {name: config};
5521     }
5522     Roo.apply(this, config);
5523     
5524     if(!this.type){
5525         this.type = "auto";
5526     }
5527     
5528     var st = Roo.data.SortTypes;
5529     // named sortTypes are supported, here we look them up
5530     if(typeof this.sortType == "string"){
5531         this.sortType = st[this.sortType];
5532     }
5533     
5534     // set default sortType for strings and dates
5535     if(!this.sortType){
5536         switch(this.type){
5537             case "string":
5538                 this.sortType = st.asUCString;
5539                 break;
5540             case "date":
5541                 this.sortType = st.asDate;
5542                 break;
5543             default:
5544                 this.sortType = st.none;
5545         }
5546     }
5547
5548     // define once
5549     var stripRe = /[\$,%]/g;
5550
5551     // prebuilt conversion function for this field, instead of
5552     // switching every time we're reading a value
5553     if(!this.convert){
5554         var cv, dateFormat = this.dateFormat;
5555         switch(this.type){
5556             case "":
5557             case "auto":
5558             case undefined:
5559                 cv = function(v){ return v; };
5560                 break;
5561             case "string":
5562                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5563                 break;
5564             case "int":
5565                 cv = function(v){
5566                     return v !== undefined && v !== null && v !== '' ?
5567                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5568                     };
5569                 break;
5570             case "float":
5571                 cv = function(v){
5572                     return v !== undefined && v !== null && v !== '' ?
5573                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5574                     };
5575                 break;
5576             case "bool":
5577             case "boolean":
5578                 cv = function(v){ return v === true || v === "true" || v == 1; };
5579                 break;
5580             case "date":
5581                 cv = function(v){
5582                     if(!v){
5583                         return '';
5584                     }
5585                     if(v instanceof Date){
5586                         return v;
5587                     }
5588                     if(dateFormat){
5589                         if(dateFormat == "timestamp"){
5590                             return new Date(v*1000);
5591                         }
5592                         return Date.parseDate(v, dateFormat);
5593                     }
5594                     var parsed = Date.parse(v);
5595                     return parsed ? new Date(parsed) : null;
5596                 };
5597              break;
5598             
5599         }
5600         this.convert = cv;
5601     }
5602 };
5603
5604 Roo.data.Field.prototype = {
5605     dateFormat: null,
5606     defaultValue: "",
5607     mapping: null,
5608     sortType : null,
5609     sortDir : "ASC"
5610 };/*
5611  * Based on:
5612  * Ext JS Library 1.1.1
5613  * Copyright(c) 2006-2007, Ext JS, LLC.
5614  *
5615  * Originally Released Under LGPL - original licence link has changed is not relivant.
5616  *
5617  * Fork - LGPL
5618  * <script type="text/javascript">
5619  */
5620  
5621 // Base class for reading structured data from a data source.  This class is intended to be
5622 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5623
5624 /**
5625  * @class Roo.data.DataReader
5626  * Base class for reading structured data from a data source.  This class is intended to be
5627  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5628  */
5629
5630 Roo.data.DataReader = function(meta, recordType){
5631     
5632     this.meta = meta;
5633     
5634     this.recordType = recordType instanceof Array ? 
5635         Roo.data.Record.create(recordType) : recordType;
5636 };
5637
5638 Roo.data.DataReader.prototype = {
5639      /**
5640      * Create an empty record
5641      * @param {Object} data (optional) - overlay some values
5642      * @return {Roo.data.Record} record created.
5643      */
5644     newRow :  function(d) {
5645         var da =  {};
5646         this.recordType.prototype.fields.each(function(c) {
5647             switch( c.type) {
5648                 case 'int' : da[c.name] = 0; break;
5649                 case 'date' : da[c.name] = new Date(); break;
5650                 case 'float' : da[c.name] = 0.0; break;
5651                 case 'boolean' : da[c.name] = false; break;
5652                 default : da[c.name] = ""; break;
5653             }
5654             
5655         });
5656         return new this.recordType(Roo.apply(da, d));
5657     }
5658     
5659 };/*
5660  * Based on:
5661  * Ext JS Library 1.1.1
5662  * Copyright(c) 2006-2007, Ext JS, LLC.
5663  *
5664  * Originally Released Under LGPL - original licence link has changed is not relivant.
5665  *
5666  * Fork - LGPL
5667  * <script type="text/javascript">
5668  */
5669
5670 /**
5671  * @class Roo.data.DataProxy
5672  * @extends Roo.data.Observable
5673  * This class is an abstract base class for implementations which provide retrieval of
5674  * unformatted data objects.<br>
5675  * <p>
5676  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5677  * (of the appropriate type which knows how to parse the data object) to provide a block of
5678  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5679  * <p>
5680  * Custom implementations must implement the load method as described in
5681  * {@link Roo.data.HttpProxy#load}.
5682  */
5683 Roo.data.DataProxy = function(){
5684     this.addEvents({
5685         /**
5686          * @event beforeload
5687          * Fires before a network request is made to retrieve a data object.
5688          * @param {Object} This DataProxy object.
5689          * @param {Object} params The params parameter to the load function.
5690          */
5691         beforeload : true,
5692         /**
5693          * @event load
5694          * Fires before the load method's callback is called.
5695          * @param {Object} This DataProxy object.
5696          * @param {Object} o The data object.
5697          * @param {Object} arg The callback argument object passed to the load function.
5698          */
5699         load : true,
5700         /**
5701          * @event loadexception
5702          * Fires if an Exception occurs during data retrieval.
5703          * @param {Object} This DataProxy object.
5704          * @param {Object} o The data object.
5705          * @param {Object} arg The callback argument object passed to the load function.
5706          * @param {Object} e The Exception.
5707          */
5708         loadexception : true
5709     });
5710     Roo.data.DataProxy.superclass.constructor.call(this);
5711 };
5712
5713 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5714
5715     /**
5716      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5717      */
5718 /*
5719  * Based on:
5720  * Ext JS Library 1.1.1
5721  * Copyright(c) 2006-2007, Ext JS, LLC.
5722  *
5723  * Originally Released Under LGPL - original licence link has changed is not relivant.
5724  *
5725  * Fork - LGPL
5726  * <script type="text/javascript">
5727  */
5728 /**
5729  * @class Roo.data.MemoryProxy
5730  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5731  * to the Reader when its load method is called.
5732  * @constructor
5733  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5734  */
5735 Roo.data.MemoryProxy = function(data){
5736     if (data.data) {
5737         data = data.data;
5738     }
5739     Roo.data.MemoryProxy.superclass.constructor.call(this);
5740     this.data = data;
5741 };
5742
5743 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5744     /**
5745      * Load data from the requested source (in this case an in-memory
5746      * data object passed to the constructor), read the data object into
5747      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5748      * process that block using the passed callback.
5749      * @param {Object} params This parameter is not used by the MemoryProxy class.
5750      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5751      * object into a block of Roo.data.Records.
5752      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5753      * The function must be passed <ul>
5754      * <li>The Record block object</li>
5755      * <li>The "arg" argument from the load function</li>
5756      * <li>A boolean success indicator</li>
5757      * </ul>
5758      * @param {Object} scope The scope in which to call the callback
5759      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5760      */
5761     load : function(params, reader, callback, scope, arg){
5762         params = params || {};
5763         var result;
5764         try {
5765             result = reader.readRecords(this.data);
5766         }catch(e){
5767             this.fireEvent("loadexception", this, arg, null, e);
5768             callback.call(scope, null, arg, false);
5769             return;
5770         }
5771         callback.call(scope, result, arg, true);
5772     },
5773     
5774     // private
5775     update : function(params, records){
5776         
5777     }
5778 });/*
5779  * Based on:
5780  * Ext JS Library 1.1.1
5781  * Copyright(c) 2006-2007, Ext JS, LLC.
5782  *
5783  * Originally Released Under LGPL - original licence link has changed is not relivant.
5784  *
5785  * Fork - LGPL
5786  * <script type="text/javascript">
5787  */
5788 /**
5789  * @class Roo.data.HttpProxy
5790  * @extends Roo.data.DataProxy
5791  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5792  * configured to reference a certain URL.<br><br>
5793  * <p>
5794  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5795  * from which the running page was served.<br><br>
5796  * <p>
5797  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5798  * <p>
5799  * Be aware that to enable the browser to parse an XML document, the server must set
5800  * the Content-Type header in the HTTP response to "text/xml".
5801  * @constructor
5802  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5803  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5804  * will be used to make the request.
5805  */
5806 Roo.data.HttpProxy = function(conn){
5807     Roo.data.HttpProxy.superclass.constructor.call(this);
5808     // is conn a conn config or a real conn?
5809     this.conn = conn;
5810     this.useAjax = !conn || !conn.events;
5811   
5812 };
5813
5814 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5815     // thse are take from connection...
5816     
5817     /**
5818      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5819      */
5820     /**
5821      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5822      * extra parameters to each request made by this object. (defaults to undefined)
5823      */
5824     /**
5825      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5826      *  to each request made by this object. (defaults to undefined)
5827      */
5828     /**
5829      * @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)
5830      */
5831     /**
5832      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5833      */
5834      /**
5835      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5836      * @type Boolean
5837      */
5838   
5839
5840     /**
5841      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5842      * @type Boolean
5843      */
5844     /**
5845      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5846      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5847      * a finer-grained basis than the DataProxy events.
5848      */
5849     getConnection : function(){
5850         return this.useAjax ? Roo.Ajax : this.conn;
5851     },
5852
5853     /**
5854      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5855      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5856      * process that block using the passed callback.
5857      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5858      * for the request to the remote server.
5859      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5860      * object into a block of Roo.data.Records.
5861      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5862      * The function must be passed <ul>
5863      * <li>The Record block object</li>
5864      * <li>The "arg" argument from the load function</li>
5865      * <li>A boolean success indicator</li>
5866      * </ul>
5867      * @param {Object} scope The scope in which to call the callback
5868      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5869      */
5870     load : function(params, reader, callback, scope, arg){
5871         if(this.fireEvent("beforeload", this, params) !== false){
5872             var  o = {
5873                 params : params || {},
5874                 request: {
5875                     callback : callback,
5876                     scope : scope,
5877                     arg : arg
5878                 },
5879                 reader: reader,
5880                 callback : this.loadResponse,
5881                 scope: this
5882             };
5883             if(this.useAjax){
5884                 Roo.applyIf(o, this.conn);
5885                 if(this.activeRequest){
5886                     Roo.Ajax.abort(this.activeRequest);
5887                 }
5888                 this.activeRequest = Roo.Ajax.request(o);
5889             }else{
5890                 this.conn.request(o);
5891             }
5892         }else{
5893             callback.call(scope||this, null, arg, false);
5894         }
5895     },
5896
5897     // private
5898     loadResponse : function(o, success, response){
5899         delete this.activeRequest;
5900         if(!success){
5901             this.fireEvent("loadexception", this, o, response);
5902             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5903             return;
5904         }
5905         var result;
5906         try {
5907             result = o.reader.read(response);
5908         }catch(e){
5909             this.fireEvent("loadexception", this, o, response, e);
5910             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5911             return;
5912         }
5913         
5914         this.fireEvent("load", this, o, o.request.arg);
5915         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5916     },
5917
5918     // private
5919     update : function(dataSet){
5920
5921     },
5922
5923     // private
5924     updateResponse : function(dataSet){
5925
5926     }
5927 });/*
5928  * Based on:
5929  * Ext JS Library 1.1.1
5930  * Copyright(c) 2006-2007, Ext JS, LLC.
5931  *
5932  * Originally Released Under LGPL - original licence link has changed is not relivant.
5933  *
5934  * Fork - LGPL
5935  * <script type="text/javascript">
5936  */
5937
5938 /**
5939  * @class Roo.data.ScriptTagProxy
5940  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5941  * other than the originating domain of the running page.<br><br>
5942  * <p>
5943  * <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
5944  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5945  * <p>
5946  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5947  * source code that is used as the source inside a &lt;script> tag.<br><br>
5948  * <p>
5949  * In order for the browser to process the returned data, the server must wrap the data object
5950  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5951  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5952  * depending on whether the callback name was passed:
5953  * <p>
5954  * <pre><code>
5955 boolean scriptTag = false;
5956 String cb = request.getParameter("callback");
5957 if (cb != null) {
5958     scriptTag = true;
5959     response.setContentType("text/javascript");
5960 } else {
5961     response.setContentType("application/x-json");
5962 }
5963 Writer out = response.getWriter();
5964 if (scriptTag) {
5965     out.write(cb + "(");
5966 }
5967 out.print(dataBlock.toJsonString());
5968 if (scriptTag) {
5969     out.write(");");
5970 }
5971 </pre></code>
5972  *
5973  * @constructor
5974  * @param {Object} config A configuration object.
5975  */
5976 Roo.data.ScriptTagProxy = function(config){
5977     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5978     Roo.apply(this, config);
5979     this.head = document.getElementsByTagName("head")[0];
5980 };
5981
5982 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5983
5984 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5985     /**
5986      * @cfg {String} url The URL from which to request the data object.
5987      */
5988     /**
5989      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5990      */
5991     timeout : 30000,
5992     /**
5993      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5994      * the server the name of the callback function set up by the load call to process the returned data object.
5995      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5996      * javascript output which calls this named function passing the data object as its only parameter.
5997      */
5998     callbackParam : "callback",
5999     /**
6000      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
6001      * name to the request.
6002      */
6003     nocache : true,
6004
6005     /**
6006      * Load data from the configured URL, read the data object into
6007      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6008      * process that block using the passed callback.
6009      * @param {Object} params An object containing properties which are to be used as HTTP parameters
6010      * for the request to the remote server.
6011      * @param {Roo.data.DataReader} reader The Reader object which converts the data
6012      * object into a block of Roo.data.Records.
6013      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
6014      * The function must be passed <ul>
6015      * <li>The Record block object</li>
6016      * <li>The "arg" argument from the load function</li>
6017      * <li>A boolean success indicator</li>
6018      * </ul>
6019      * @param {Object} scope The scope in which to call the callback
6020      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6021      */
6022     load : function(params, reader, callback, scope, arg){
6023         if(this.fireEvent("beforeload", this, params) !== false){
6024
6025             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
6026
6027             var url = this.url;
6028             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
6029             if(this.nocache){
6030                 url += "&_dc=" + (new Date().getTime());
6031             }
6032             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
6033             var trans = {
6034                 id : transId,
6035                 cb : "stcCallback"+transId,
6036                 scriptId : "stcScript"+transId,
6037                 params : params,
6038                 arg : arg,
6039                 url : url,
6040                 callback : callback,
6041                 scope : scope,
6042                 reader : reader
6043             };
6044             var conn = this;
6045
6046             window[trans.cb] = function(o){
6047                 conn.handleResponse(o, trans);
6048             };
6049
6050             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6051
6052             if(this.autoAbort !== false){
6053                 this.abort();
6054             }
6055
6056             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6057
6058             var script = document.createElement("script");
6059             script.setAttribute("src", url);
6060             script.setAttribute("type", "text/javascript");
6061             script.setAttribute("id", trans.scriptId);
6062             this.head.appendChild(script);
6063
6064             this.trans = trans;
6065         }else{
6066             callback.call(scope||this, null, arg, false);
6067         }
6068     },
6069
6070     // private
6071     isLoading : function(){
6072         return this.trans ? true : false;
6073     },
6074
6075     /**
6076      * Abort the current server request.
6077      */
6078     abort : function(){
6079         if(this.isLoading()){
6080             this.destroyTrans(this.trans);
6081         }
6082     },
6083
6084     // private
6085     destroyTrans : function(trans, isLoaded){
6086         this.head.removeChild(document.getElementById(trans.scriptId));
6087         clearTimeout(trans.timeoutId);
6088         if(isLoaded){
6089             window[trans.cb] = undefined;
6090             try{
6091                 delete window[trans.cb];
6092             }catch(e){}
6093         }else{
6094             // if hasn't been loaded, wait for load to remove it to prevent script error
6095             window[trans.cb] = function(){
6096                 window[trans.cb] = undefined;
6097                 try{
6098                     delete window[trans.cb];
6099                 }catch(e){}
6100             };
6101         }
6102     },
6103
6104     // private
6105     handleResponse : function(o, trans){
6106         this.trans = false;
6107         this.destroyTrans(trans, true);
6108         var result;
6109         try {
6110             result = trans.reader.readRecords(o);
6111         }catch(e){
6112             this.fireEvent("loadexception", this, o, trans.arg, e);
6113             trans.callback.call(trans.scope||window, null, trans.arg, false);
6114             return;
6115         }
6116         this.fireEvent("load", this, o, trans.arg);
6117         trans.callback.call(trans.scope||window, result, trans.arg, true);
6118     },
6119
6120     // private
6121     handleFailure : function(trans){
6122         this.trans = false;
6123         this.destroyTrans(trans, false);
6124         this.fireEvent("loadexception", this, null, trans.arg);
6125         trans.callback.call(trans.scope||window, null, trans.arg, false);
6126     }
6127 });/*
6128  * Based on:
6129  * Ext JS Library 1.1.1
6130  * Copyright(c) 2006-2007, Ext JS, LLC.
6131  *
6132  * Originally Released Under LGPL - original licence link has changed is not relivant.
6133  *
6134  * Fork - LGPL
6135  * <script type="text/javascript">
6136  */
6137
6138 /**
6139  * @class Roo.data.JsonReader
6140  * @extends Roo.data.DataReader
6141  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6142  * based on mappings in a provided Roo.data.Record constructor.
6143  * 
6144  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6145  * in the reply previously. 
6146  * 
6147  * <p>
6148  * Example code:
6149  * <pre><code>
6150 var RecordDef = Roo.data.Record.create([
6151     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6152     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6153 ]);
6154 var myReader = new Roo.data.JsonReader({
6155     totalProperty: "results",    // The property which contains the total dataset size (optional)
6156     root: "rows",                // The property which contains an Array of row objects
6157     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6158 }, RecordDef);
6159 </code></pre>
6160  * <p>
6161  * This would consume a JSON file like this:
6162  * <pre><code>
6163 { 'results': 2, 'rows': [
6164     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6165     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6166 }
6167 </code></pre>
6168  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6169  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6170  * paged from the remote server.
6171  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6172  * @cfg {String} root name of the property which contains the Array of row objects.
6173  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6174  * @cfg {Array} fields Array of field definition objects
6175  * @constructor
6176  * Create a new JsonReader
6177  * @param {Object} meta Metadata configuration options
6178  * @param {Object} recordType Either an Array of field definition objects,
6179  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6180  */
6181 Roo.data.JsonReader = function(meta, recordType){
6182     
6183     meta = meta || {};
6184     // set some defaults:
6185     Roo.applyIf(meta, {
6186         totalProperty: 'total',
6187         successProperty : 'success',
6188         root : 'data',
6189         id : 'id'
6190     });
6191     
6192     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6193 };
6194 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6195     
6196     /**
6197      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6198      * Used by Store query builder to append _requestMeta to params.
6199      * 
6200      */
6201     metaFromRemote : false,
6202     /**
6203      * This method is only used by a DataProxy which has retrieved data from a remote server.
6204      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6205      * @return {Object} data A data block which is used by an Roo.data.Store object as
6206      * a cache of Roo.data.Records.
6207      */
6208     read : function(response){
6209         var json = response.responseText;
6210        
6211         var o = /* eval:var:o */ eval("("+json+")");
6212         if(!o) {
6213             throw {message: "JsonReader.read: Json object not found"};
6214         }
6215         
6216         if(o.metaData){
6217             
6218             delete this.ef;
6219             this.metaFromRemote = true;
6220             this.meta = o.metaData;
6221             this.recordType = Roo.data.Record.create(o.metaData.fields);
6222             this.onMetaChange(this.meta, this.recordType, o);
6223         }
6224         return this.readRecords(o);
6225     },
6226
6227     // private function a store will implement
6228     onMetaChange : function(meta, recordType, o){
6229
6230     },
6231
6232     /**
6233          * @ignore
6234          */
6235     simpleAccess: function(obj, subsc) {
6236         return obj[subsc];
6237     },
6238
6239         /**
6240          * @ignore
6241          */
6242     getJsonAccessor: function(){
6243         var re = /[\[\.]/;
6244         return function(expr) {
6245             try {
6246                 return(re.test(expr))
6247                     ? new Function("obj", "return obj." + expr)
6248                     : function(obj){
6249                         return obj[expr];
6250                     };
6251             } catch(e){}
6252             return Roo.emptyFn;
6253         };
6254     }(),
6255
6256     /**
6257      * Create a data block containing Roo.data.Records from an XML document.
6258      * @param {Object} o An object which contains an Array of row objects in the property specified
6259      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6260      * which contains the total size of the dataset.
6261      * @return {Object} data A data block which is used by an Roo.data.Store object as
6262      * a cache of Roo.data.Records.
6263      */
6264     readRecords : function(o){
6265         /**
6266          * After any data loads, the raw JSON data is available for further custom processing.
6267          * @type Object
6268          */
6269         this.o = o;
6270         var s = this.meta, Record = this.recordType,
6271             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
6272
6273 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6274         if (!this.ef) {
6275             if(s.totalProperty) {
6276                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6277                 }
6278                 if(s.successProperty) {
6279                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6280                 }
6281                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6282                 if (s.id) {
6283                         var g = this.getJsonAccessor(s.id);
6284                         this.getId = function(rec) {
6285                                 var r = g(rec);  
6286                                 return (r === undefined || r === "") ? null : r;
6287                         };
6288                 } else {
6289                         this.getId = function(){return null;};
6290                 }
6291             this.ef = [];
6292             for(var jj = 0; jj < fl; jj++){
6293                 f = fi[jj];
6294                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6295                 this.ef[jj] = this.getJsonAccessor(map);
6296             }
6297         }
6298
6299         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6300         if(s.totalProperty){
6301             var vt = parseInt(this.getTotal(o), 10);
6302             if(!isNaN(vt)){
6303                 totalRecords = vt;
6304             }
6305         }
6306         if(s.successProperty){
6307             var vs = this.getSuccess(o);
6308             if(vs === false || vs === 'false'){
6309                 success = false;
6310             }
6311         }
6312         var records = [];
6313         for(var i = 0; i < c; i++){
6314                 var n = root[i];
6315             var values = {};
6316             var id = this.getId(n);
6317             for(var j = 0; j < fl; j++){
6318                 f = fi[j];
6319             var v = this.ef[j](n);
6320             if (!f.convert) {
6321                 Roo.log('missing convert for ' + f.name);
6322                 Roo.log(f);
6323                 continue;
6324             }
6325             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6326             }
6327             var record = new Record(values, id);
6328             record.json = n;
6329             records[i] = record;
6330         }
6331         return {
6332             raw : o,
6333             success : success,
6334             records : records,
6335             totalRecords : totalRecords
6336         };
6337     }
6338 });/*
6339  * Based on:
6340  * Ext JS Library 1.1.1
6341  * Copyright(c) 2006-2007, Ext JS, LLC.
6342  *
6343  * Originally Released Under LGPL - original licence link has changed is not relivant.
6344  *
6345  * Fork - LGPL
6346  * <script type="text/javascript">
6347  */
6348
6349 /**
6350  * @class Roo.data.XmlReader
6351  * @extends Roo.data.DataReader
6352  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6353  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6354  * <p>
6355  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6356  * header in the HTTP response must be set to "text/xml".</em>
6357  * <p>
6358  * Example code:
6359  * <pre><code>
6360 var RecordDef = Roo.data.Record.create([
6361    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6362    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6363 ]);
6364 var myReader = new Roo.data.XmlReader({
6365    totalRecords: "results", // The element which contains the total dataset size (optional)
6366    record: "row",           // The repeated element which contains row information
6367    id: "id"                 // The element within the row that provides an ID for the record (optional)
6368 }, RecordDef);
6369 </code></pre>
6370  * <p>
6371  * This would consume an XML file like this:
6372  * <pre><code>
6373 &lt;?xml?>
6374 &lt;dataset>
6375  &lt;results>2&lt;/results>
6376  &lt;row>
6377    &lt;id>1&lt;/id>
6378    &lt;name>Bill&lt;/name>
6379    &lt;occupation>Gardener&lt;/occupation>
6380  &lt;/row>
6381  &lt;row>
6382    &lt;id>2&lt;/id>
6383    &lt;name>Ben&lt;/name>
6384    &lt;occupation>Horticulturalist&lt;/occupation>
6385  &lt;/row>
6386 &lt;/dataset>
6387 </code></pre>
6388  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6389  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6390  * paged from the remote server.
6391  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6392  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6393  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6394  * a record identifier value.
6395  * @constructor
6396  * Create a new XmlReader
6397  * @param {Object} meta Metadata configuration options
6398  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6399  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6400  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6401  */
6402 Roo.data.XmlReader = function(meta, recordType){
6403     meta = meta || {};
6404     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6405 };
6406 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6407     /**
6408      * This method is only used by a DataProxy which has retrieved data from a remote server.
6409          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6410          * to contain a method called 'responseXML' that returns an XML document object.
6411      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6412      * a cache of Roo.data.Records.
6413      */
6414     read : function(response){
6415         var doc = response.responseXML;
6416         if(!doc) {
6417             throw {message: "XmlReader.read: XML Document not available"};
6418         }
6419         return this.readRecords(doc);
6420     },
6421
6422     /**
6423      * Create a data block containing Roo.data.Records from an XML document.
6424          * @param {Object} doc A parsed XML document.
6425      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6426      * a cache of Roo.data.Records.
6427      */
6428     readRecords : function(doc){
6429         /**
6430          * After any data loads/reads, the raw XML Document is available for further custom processing.
6431          * @type XMLDocument
6432          */
6433         this.xmlData = doc;
6434         var root = doc.documentElement || doc;
6435         var q = Roo.DomQuery;
6436         var recordType = this.recordType, fields = recordType.prototype.fields;
6437         var sid = this.meta.id;
6438         var totalRecords = 0, success = true;
6439         if(this.meta.totalRecords){
6440             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6441         }
6442         
6443         if(this.meta.success){
6444             var sv = q.selectValue(this.meta.success, root, true);
6445             success = sv !== false && sv !== 'false';
6446         }
6447         var records = [];
6448         var ns = q.select(this.meta.record, root);
6449         for(var i = 0, len = ns.length; i < len; i++) {
6450                 var n = ns[i];
6451                 var values = {};
6452                 var id = sid ? q.selectValue(sid, n) : undefined;
6453                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6454                     var f = fields.items[j];
6455                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6456                     v = f.convert(v);
6457                     values[f.name] = v;
6458                 }
6459                 var record = new recordType(values, id);
6460                 record.node = n;
6461                 records[records.length] = record;
6462             }
6463
6464             return {
6465                 success : success,
6466                 records : records,
6467                 totalRecords : totalRecords || records.length
6468             };
6469     }
6470 });/*
6471  * Based on:
6472  * Ext JS Library 1.1.1
6473  * Copyright(c) 2006-2007, Ext JS, LLC.
6474  *
6475  * Originally Released Under LGPL - original licence link has changed is not relivant.
6476  *
6477  * Fork - LGPL
6478  * <script type="text/javascript">
6479  */
6480
6481 /**
6482  * @class Roo.data.ArrayReader
6483  * @extends Roo.data.DataReader
6484  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6485  * Each element of that Array represents a row of data fields. The
6486  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6487  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6488  * <p>
6489  * Example code:.
6490  * <pre><code>
6491 var RecordDef = Roo.data.Record.create([
6492     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6493     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6494 ]);
6495 var myReader = new Roo.data.ArrayReader({
6496     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6497 }, RecordDef);
6498 </code></pre>
6499  * <p>
6500  * This would consume an Array like this:
6501  * <pre><code>
6502 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6503   </code></pre>
6504  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6505  * @constructor
6506  * Create a new JsonReader
6507  * @param {Object} meta Metadata configuration options.
6508  * @param {Object} recordType Either an Array of field definition objects
6509  * as specified to {@link Roo.data.Record#create},
6510  * or an {@link Roo.data.Record} object
6511  * created using {@link Roo.data.Record#create}.
6512  */
6513 Roo.data.ArrayReader = function(meta, recordType){
6514     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6515 };
6516
6517 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6518     /**
6519      * Create a data block containing Roo.data.Records from an XML document.
6520      * @param {Object} o An Array of row objects which represents the dataset.
6521      * @return {Object} data A data block which is used by an Roo.data.Store object as
6522      * a cache of Roo.data.Records.
6523      */
6524     readRecords : function(o){
6525         var sid = this.meta ? this.meta.id : null;
6526         var recordType = this.recordType, fields = recordType.prototype.fields;
6527         var records = [];
6528         var root = o;
6529             for(var i = 0; i < root.length; i++){
6530                     var n = root[i];
6531                 var values = {};
6532                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6533                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6534                 var f = fields.items[j];
6535                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6536                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6537                 v = f.convert(v);
6538                 values[f.name] = v;
6539             }
6540                 var record = new recordType(values, id);
6541                 record.json = n;
6542                 records[records.length] = record;
6543             }
6544             return {
6545                 records : records,
6546                 totalRecords : records.length
6547             };
6548     }
6549 });/*
6550  * Based on:
6551  * Ext JS Library 1.1.1
6552  * Copyright(c) 2006-2007, Ext JS, LLC.
6553  *
6554  * Originally Released Under LGPL - original licence link has changed is not relivant.
6555  *
6556  * Fork - LGPL
6557  * <script type="text/javascript">
6558  */
6559
6560
6561 /**
6562  * @class Roo.data.Tree
6563  * @extends Roo.util.Observable
6564  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6565  * in the tree have most standard DOM functionality.
6566  * @constructor
6567  * @param {Node} root (optional) The root node
6568  */
6569 Roo.data.Tree = function(root){
6570    this.nodeHash = {};
6571    /**
6572     * The root node for this tree
6573     * @type Node
6574     */
6575    this.root = null;
6576    if(root){
6577        this.setRootNode(root);
6578    }
6579    this.addEvents({
6580        /**
6581         * @event append
6582         * Fires when a new child node is appended to a node in this tree.
6583         * @param {Tree} tree The owner tree
6584         * @param {Node} parent The parent node
6585         * @param {Node} node The newly appended node
6586         * @param {Number} index The index of the newly appended node
6587         */
6588        "append" : true,
6589        /**
6590         * @event remove
6591         * Fires when a child node is removed from a node in this tree.
6592         * @param {Tree} tree The owner tree
6593         * @param {Node} parent The parent node
6594         * @param {Node} node The child node removed
6595         */
6596        "remove" : true,
6597        /**
6598         * @event move
6599         * Fires when a node is moved to a new location in the tree
6600         * @param {Tree} tree The owner tree
6601         * @param {Node} node The node moved
6602         * @param {Node} oldParent The old parent of this node
6603         * @param {Node} newParent The new parent of this node
6604         * @param {Number} index The index it was moved to
6605         */
6606        "move" : true,
6607        /**
6608         * @event insert
6609         * Fires when a new child node is inserted in a node in this tree.
6610         * @param {Tree} tree The owner tree
6611         * @param {Node} parent The parent node
6612         * @param {Node} node The child node inserted
6613         * @param {Node} refNode The child node the node was inserted before
6614         */
6615        "insert" : true,
6616        /**
6617         * @event beforeappend
6618         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6619         * @param {Tree} tree The owner tree
6620         * @param {Node} parent The parent node
6621         * @param {Node} node The child node to be appended
6622         */
6623        "beforeappend" : true,
6624        /**
6625         * @event beforeremove
6626         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6627         * @param {Tree} tree The owner tree
6628         * @param {Node} parent The parent node
6629         * @param {Node} node The child node to be removed
6630         */
6631        "beforeremove" : true,
6632        /**
6633         * @event beforemove
6634         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6635         * @param {Tree} tree The owner tree
6636         * @param {Node} node The node being moved
6637         * @param {Node} oldParent The parent of the node
6638         * @param {Node} newParent The new parent the node is moving to
6639         * @param {Number} index The index it is being moved to
6640         */
6641        "beforemove" : true,
6642        /**
6643         * @event beforeinsert
6644         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6645         * @param {Tree} tree The owner tree
6646         * @param {Node} parent The parent node
6647         * @param {Node} node The child node to be inserted
6648         * @param {Node} refNode The child node the node is being inserted before
6649         */
6650        "beforeinsert" : true
6651    });
6652
6653     Roo.data.Tree.superclass.constructor.call(this);
6654 };
6655
6656 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6657     pathSeparator: "/",
6658
6659     proxyNodeEvent : function(){
6660         return this.fireEvent.apply(this, arguments);
6661     },
6662
6663     /**
6664      * Returns the root node for this tree.
6665      * @return {Node}
6666      */
6667     getRootNode : function(){
6668         return this.root;
6669     },
6670
6671     /**
6672      * Sets the root node for this tree.
6673      * @param {Node} node
6674      * @return {Node}
6675      */
6676     setRootNode : function(node){
6677         this.root = node;
6678         node.ownerTree = this;
6679         node.isRoot = true;
6680         this.registerNode(node);
6681         return node;
6682     },
6683
6684     /**
6685      * Gets a node in this tree by its id.
6686      * @param {String} id
6687      * @return {Node}
6688      */
6689     getNodeById : function(id){
6690         return this.nodeHash[id];
6691     },
6692
6693     registerNode : function(node){
6694         this.nodeHash[node.id] = node;
6695     },
6696
6697     unregisterNode : function(node){
6698         delete this.nodeHash[node.id];
6699     },
6700
6701     toString : function(){
6702         return "[Tree"+(this.id?" "+this.id:"")+"]";
6703     }
6704 });
6705
6706 /**
6707  * @class Roo.data.Node
6708  * @extends Roo.util.Observable
6709  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6710  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6711  * @constructor
6712  * @param {Object} attributes The attributes/config for the node
6713  */
6714 Roo.data.Node = function(attributes){
6715     /**
6716      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6717      * @type {Object}
6718      */
6719     this.attributes = attributes || {};
6720     this.leaf = this.attributes.leaf;
6721     /**
6722      * The node id. @type String
6723      */
6724     this.id = this.attributes.id;
6725     if(!this.id){
6726         this.id = Roo.id(null, "ynode-");
6727         this.attributes.id = this.id;
6728     }
6729      
6730     
6731     /**
6732      * All child nodes of this node. @type Array
6733      */
6734     this.childNodes = [];
6735     if(!this.childNodes.indexOf){ // indexOf is a must
6736         this.childNodes.indexOf = function(o){
6737             for(var i = 0, len = this.length; i < len; i++){
6738                 if(this[i] == o) {
6739                     return i;
6740                 }
6741             }
6742             return -1;
6743         };
6744     }
6745     /**
6746      * The parent node for this node. @type Node
6747      */
6748     this.parentNode = null;
6749     /**
6750      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6751      */
6752     this.firstChild = null;
6753     /**
6754      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6755      */
6756     this.lastChild = null;
6757     /**
6758      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6759      */
6760     this.previousSibling = null;
6761     /**
6762      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6763      */
6764     this.nextSibling = null;
6765
6766     this.addEvents({
6767        /**
6768         * @event append
6769         * Fires when a new child node is appended
6770         * @param {Tree} tree The owner tree
6771         * @param {Node} this This node
6772         * @param {Node} node The newly appended node
6773         * @param {Number} index The index of the newly appended node
6774         */
6775        "append" : true,
6776        /**
6777         * @event remove
6778         * Fires when a child node is removed
6779         * @param {Tree} tree The owner tree
6780         * @param {Node} this This node
6781         * @param {Node} node The removed node
6782         */
6783        "remove" : true,
6784        /**
6785         * @event move
6786         * Fires when this node is moved to a new location in the tree
6787         * @param {Tree} tree The owner tree
6788         * @param {Node} this This node
6789         * @param {Node} oldParent The old parent of this node
6790         * @param {Node} newParent The new parent of this node
6791         * @param {Number} index The index it was moved to
6792         */
6793        "move" : true,
6794        /**
6795         * @event insert
6796         * Fires when a new child node is inserted.
6797         * @param {Tree} tree The owner tree
6798         * @param {Node} this This node
6799         * @param {Node} node The child node inserted
6800         * @param {Node} refNode The child node the node was inserted before
6801         */
6802        "insert" : true,
6803        /**
6804         * @event beforeappend
6805         * Fires before a new child is appended, return false to cancel the append.
6806         * @param {Tree} tree The owner tree
6807         * @param {Node} this This node
6808         * @param {Node} node The child node to be appended
6809         */
6810        "beforeappend" : true,
6811        /**
6812         * @event beforeremove
6813         * Fires before a child is removed, return false to cancel the remove.
6814         * @param {Tree} tree The owner tree
6815         * @param {Node} this This node
6816         * @param {Node} node The child node to be removed
6817         */
6818        "beforeremove" : true,
6819        /**
6820         * @event beforemove
6821         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6822         * @param {Tree} tree The owner tree
6823         * @param {Node} this This node
6824         * @param {Node} oldParent The parent of this node
6825         * @param {Node} newParent The new parent this node is moving to
6826         * @param {Number} index The index it is being moved to
6827         */
6828        "beforemove" : true,
6829        /**
6830         * @event beforeinsert
6831         * Fires before a new child is inserted, return false to cancel the insert.
6832         * @param {Tree} tree The owner tree
6833         * @param {Node} this This node
6834         * @param {Node} node The child node to be inserted
6835         * @param {Node} refNode The child node the node is being inserted before
6836         */
6837        "beforeinsert" : true
6838    });
6839     this.listeners = this.attributes.listeners;
6840     Roo.data.Node.superclass.constructor.call(this);
6841 };
6842
6843 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6844     fireEvent : function(evtName){
6845         // first do standard event for this node
6846         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6847             return false;
6848         }
6849         // then bubble it up to the tree if the event wasn't cancelled
6850         var ot = this.getOwnerTree();
6851         if(ot){
6852             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6853                 return false;
6854             }
6855         }
6856         return true;
6857     },
6858
6859     /**
6860      * Returns true if this node is a leaf
6861      * @return {Boolean}
6862      */
6863     isLeaf : function(){
6864         return this.leaf === true;
6865     },
6866
6867     // private
6868     setFirstChild : function(node){
6869         this.firstChild = node;
6870     },
6871
6872     //private
6873     setLastChild : function(node){
6874         this.lastChild = node;
6875     },
6876
6877
6878     /**
6879      * Returns true if this node is the last child of its parent
6880      * @return {Boolean}
6881      */
6882     isLast : function(){
6883        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6884     },
6885
6886     /**
6887      * Returns true if this node is the first child of its parent
6888      * @return {Boolean}
6889      */
6890     isFirst : function(){
6891        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6892     },
6893
6894     hasChildNodes : function(){
6895         return !this.isLeaf() && this.childNodes.length > 0;
6896     },
6897
6898     /**
6899      * Insert node(s) as the last child node of this node.
6900      * @param {Node/Array} node The node or Array of nodes to append
6901      * @return {Node} The appended node if single append, or null if an array was passed
6902      */
6903     appendChild : function(node){
6904         var multi = false;
6905         if(node instanceof Array){
6906             multi = node;
6907         }else if(arguments.length > 1){
6908             multi = arguments;
6909         }
6910         // if passed an array or multiple args do them one by one
6911         if(multi){
6912             for(var i = 0, len = multi.length; i < len; i++) {
6913                 this.appendChild(multi[i]);
6914             }
6915         }else{
6916             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6917                 return false;
6918             }
6919             var index = this.childNodes.length;
6920             var oldParent = node.parentNode;
6921             // it's a move, make sure we move it cleanly
6922             if(oldParent){
6923                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6924                     return false;
6925                 }
6926                 oldParent.removeChild(node);
6927             }
6928             index = this.childNodes.length;
6929             if(index == 0){
6930                 this.setFirstChild(node);
6931             }
6932             this.childNodes.push(node);
6933             node.parentNode = this;
6934             var ps = this.childNodes[index-1];
6935             if(ps){
6936                 node.previousSibling = ps;
6937                 ps.nextSibling = node;
6938             }else{
6939                 node.previousSibling = null;
6940             }
6941             node.nextSibling = null;
6942             this.setLastChild(node);
6943             node.setOwnerTree(this.getOwnerTree());
6944             this.fireEvent("append", this.ownerTree, this, node, index);
6945             if(oldParent){
6946                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6947             }
6948             return node;
6949         }
6950     },
6951
6952     /**
6953      * Removes a child node from this node.
6954      * @param {Node} node The node to remove
6955      * @return {Node} The removed node
6956      */
6957     removeChild : function(node){
6958         var index = this.childNodes.indexOf(node);
6959         if(index == -1){
6960             return false;
6961         }
6962         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6963             return false;
6964         }
6965
6966         // remove it from childNodes collection
6967         this.childNodes.splice(index, 1);
6968
6969         // update siblings
6970         if(node.previousSibling){
6971             node.previousSibling.nextSibling = node.nextSibling;
6972         }
6973         if(node.nextSibling){
6974             node.nextSibling.previousSibling = node.previousSibling;
6975         }
6976
6977         // update child refs
6978         if(this.firstChild == node){
6979             this.setFirstChild(node.nextSibling);
6980         }
6981         if(this.lastChild == node){
6982             this.setLastChild(node.previousSibling);
6983         }
6984
6985         node.setOwnerTree(null);
6986         // clear any references from the node
6987         node.parentNode = null;
6988         node.previousSibling = null;
6989         node.nextSibling = null;
6990         this.fireEvent("remove", this.ownerTree, this, node);
6991         return node;
6992     },
6993
6994     /**
6995      * Inserts the first node before the second node in this nodes childNodes collection.
6996      * @param {Node} node The node to insert
6997      * @param {Node} refNode The node to insert before (if null the node is appended)
6998      * @return {Node} The inserted node
6999      */
7000     insertBefore : function(node, refNode){
7001         if(!refNode){ // like standard Dom, refNode can be null for append
7002             return this.appendChild(node);
7003         }
7004         // nothing to do
7005         if(node == refNode){
7006             return false;
7007         }
7008
7009         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
7010             return false;
7011         }
7012         var index = this.childNodes.indexOf(refNode);
7013         var oldParent = node.parentNode;
7014         var refIndex = index;
7015
7016         // when moving internally, indexes will change after remove
7017         if(oldParent == this && this.childNodes.indexOf(node) < index){
7018             refIndex--;
7019         }
7020
7021         // it's a move, make sure we move it cleanly
7022         if(oldParent){
7023             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
7024                 return false;
7025             }
7026             oldParent.removeChild(node);
7027         }
7028         if(refIndex == 0){
7029             this.setFirstChild(node);
7030         }
7031         this.childNodes.splice(refIndex, 0, node);
7032         node.parentNode = this;
7033         var ps = this.childNodes[refIndex-1];
7034         if(ps){
7035             node.previousSibling = ps;
7036             ps.nextSibling = node;
7037         }else{
7038             node.previousSibling = null;
7039         }
7040         node.nextSibling = refNode;
7041         refNode.previousSibling = node;
7042         node.setOwnerTree(this.getOwnerTree());
7043         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7044         if(oldParent){
7045             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7046         }
7047         return node;
7048     },
7049
7050     /**
7051      * Returns the child node at the specified index.
7052      * @param {Number} index
7053      * @return {Node}
7054      */
7055     item : function(index){
7056         return this.childNodes[index];
7057     },
7058
7059     /**
7060      * Replaces one child node in this node with another.
7061      * @param {Node} newChild The replacement node
7062      * @param {Node} oldChild The node to replace
7063      * @return {Node} The replaced node
7064      */
7065     replaceChild : function(newChild, oldChild){
7066         this.insertBefore(newChild, oldChild);
7067         this.removeChild(oldChild);
7068         return oldChild;
7069     },
7070
7071     /**
7072      * Returns the index of a child node
7073      * @param {Node} node
7074      * @return {Number} The index of the node or -1 if it was not found
7075      */
7076     indexOf : function(child){
7077         return this.childNodes.indexOf(child);
7078     },
7079
7080     /**
7081      * Returns the tree this node is in.
7082      * @return {Tree}
7083      */
7084     getOwnerTree : function(){
7085         // if it doesn't have one, look for one
7086         if(!this.ownerTree){
7087             var p = this;
7088             while(p){
7089                 if(p.ownerTree){
7090                     this.ownerTree = p.ownerTree;
7091                     break;
7092                 }
7093                 p = p.parentNode;
7094             }
7095         }
7096         return this.ownerTree;
7097     },
7098
7099     /**
7100      * Returns depth of this node (the root node has a depth of 0)
7101      * @return {Number}
7102      */
7103     getDepth : function(){
7104         var depth = 0;
7105         var p = this;
7106         while(p.parentNode){
7107             ++depth;
7108             p = p.parentNode;
7109         }
7110         return depth;
7111     },
7112
7113     // private
7114     setOwnerTree : function(tree){
7115         // if it's move, we need to update everyone
7116         if(tree != this.ownerTree){
7117             if(this.ownerTree){
7118                 this.ownerTree.unregisterNode(this);
7119             }
7120             this.ownerTree = tree;
7121             var cs = this.childNodes;
7122             for(var i = 0, len = cs.length; i < len; i++) {
7123                 cs[i].setOwnerTree(tree);
7124             }
7125             if(tree){
7126                 tree.registerNode(this);
7127             }
7128         }
7129     },
7130
7131     /**
7132      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7133      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7134      * @return {String} The path
7135      */
7136     getPath : function(attr){
7137         attr = attr || "id";
7138         var p = this.parentNode;
7139         var b = [this.attributes[attr]];
7140         while(p){
7141             b.unshift(p.attributes[attr]);
7142             p = p.parentNode;
7143         }
7144         var sep = this.getOwnerTree().pathSeparator;
7145         return sep + b.join(sep);
7146     },
7147
7148     /**
7149      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7150      * function call will be the scope provided or the current node. The arguments to the function
7151      * will be the args provided or the current node. If the function returns false at any point,
7152      * the bubble is stopped.
7153      * @param {Function} fn The function to call
7154      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7155      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7156      */
7157     bubble : function(fn, scope, args){
7158         var p = this;
7159         while(p){
7160             if(fn.call(scope || p, args || p) === false){
7161                 break;
7162             }
7163             p = p.parentNode;
7164         }
7165     },
7166
7167     /**
7168      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7169      * function call will be the scope provided or the current node. The arguments to the function
7170      * will be the args provided or the current node. If the function returns false at any point,
7171      * the cascade is stopped on that branch.
7172      * @param {Function} fn The function to call
7173      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7174      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7175      */
7176     cascade : function(fn, scope, args){
7177         if(fn.call(scope || this, args || this) !== false){
7178             var cs = this.childNodes;
7179             for(var i = 0, len = cs.length; i < len; i++) {
7180                 cs[i].cascade(fn, scope, args);
7181             }
7182         }
7183     },
7184
7185     /**
7186      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7187      * function call will be the scope provided or the current node. The arguments to the function
7188      * will be the args provided or the current node. If the function returns false at any point,
7189      * the iteration stops.
7190      * @param {Function} fn The function to call
7191      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7192      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7193      */
7194     eachChild : function(fn, scope, args){
7195         var cs = this.childNodes;
7196         for(var i = 0, len = cs.length; i < len; i++) {
7197                 if(fn.call(scope || this, args || cs[i]) === false){
7198                     break;
7199                 }
7200         }
7201     },
7202
7203     /**
7204      * Finds the first child that has the attribute with the specified value.
7205      * @param {String} attribute The attribute name
7206      * @param {Mixed} value The value to search for
7207      * @return {Node} The found child or null if none was found
7208      */
7209     findChild : function(attribute, value){
7210         var cs = this.childNodes;
7211         for(var i = 0, len = cs.length; i < len; i++) {
7212                 if(cs[i].attributes[attribute] == value){
7213                     return cs[i];
7214                 }
7215         }
7216         return null;
7217     },
7218
7219     /**
7220      * Finds the first child by a custom function. The child matches if the function passed
7221      * returns true.
7222      * @param {Function} fn
7223      * @param {Object} scope (optional)
7224      * @return {Node} The found child or null if none was found
7225      */
7226     findChildBy : function(fn, scope){
7227         var cs = this.childNodes;
7228         for(var i = 0, len = cs.length; i < len; i++) {
7229                 if(fn.call(scope||cs[i], cs[i]) === true){
7230                     return cs[i];
7231                 }
7232         }
7233         return null;
7234     },
7235
7236     /**
7237      * Sorts this nodes children using the supplied sort function
7238      * @param {Function} fn
7239      * @param {Object} scope (optional)
7240      */
7241     sort : function(fn, scope){
7242         var cs = this.childNodes;
7243         var len = cs.length;
7244         if(len > 0){
7245             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7246             cs.sort(sortFn);
7247             for(var i = 0; i < len; i++){
7248                 var n = cs[i];
7249                 n.previousSibling = cs[i-1];
7250                 n.nextSibling = cs[i+1];
7251                 if(i == 0){
7252                     this.setFirstChild(n);
7253                 }
7254                 if(i == len-1){
7255                     this.setLastChild(n);
7256                 }
7257             }
7258         }
7259     },
7260
7261     /**
7262      * Returns true if this node is an ancestor (at any point) of the passed node.
7263      * @param {Node} node
7264      * @return {Boolean}
7265      */
7266     contains : function(node){
7267         return node.isAncestor(this);
7268     },
7269
7270     /**
7271      * Returns true if the passed node is an ancestor (at any point) of this node.
7272      * @param {Node} node
7273      * @return {Boolean}
7274      */
7275     isAncestor : function(node){
7276         var p = this.parentNode;
7277         while(p){
7278             if(p == node){
7279                 return true;
7280             }
7281             p = p.parentNode;
7282         }
7283         return false;
7284     },
7285
7286     toString : function(){
7287         return "[Node"+(this.id?" "+this.id:"")+"]";
7288     }
7289 });/*
7290  * Based on:
7291  * Ext JS Library 1.1.1
7292  * Copyright(c) 2006-2007, Ext JS, LLC.
7293  *
7294  * Originally Released Under LGPL - original licence link has changed is not relivant.
7295  *
7296  * Fork - LGPL
7297  * <script type="text/javascript">
7298  */
7299  (function(){ 
7300 /**
7301  * @class Roo.Layer
7302  * @extends Roo.Element
7303  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7304  * automatic maintaining of shadow/shim positions.
7305  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7306  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7307  * you can pass a string with a CSS class name. False turns off the shadow.
7308  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7309  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7310  * @cfg {String} cls CSS class to add to the element
7311  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7312  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7313  * @constructor
7314  * @param {Object} config An object with config options.
7315  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7316  */
7317
7318 Roo.Layer = function(config, existingEl){
7319     config = config || {};
7320     var dh = Roo.DomHelper;
7321     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7322     if(existingEl){
7323         this.dom = Roo.getDom(existingEl);
7324     }
7325     if(!this.dom){
7326         var o = config.dh || {tag: "div", cls: "x-layer"};
7327         this.dom = dh.append(pel, o);
7328     }
7329     if(config.cls){
7330         this.addClass(config.cls);
7331     }
7332     this.constrain = config.constrain !== false;
7333     this.visibilityMode = Roo.Element.VISIBILITY;
7334     if(config.id){
7335         this.id = this.dom.id = config.id;
7336     }else{
7337         this.id = Roo.id(this.dom);
7338     }
7339     this.zindex = config.zindex || this.getZIndex();
7340     this.position("absolute", this.zindex);
7341     if(config.shadow){
7342         this.shadowOffset = config.shadowOffset || 4;
7343         this.shadow = new Roo.Shadow({
7344             offset : this.shadowOffset,
7345             mode : config.shadow
7346         });
7347     }else{
7348         this.shadowOffset = 0;
7349     }
7350     this.useShim = config.shim !== false && Roo.useShims;
7351     this.useDisplay = config.useDisplay;
7352     this.hide();
7353 };
7354
7355 var supr = Roo.Element.prototype;
7356
7357 // shims are shared among layer to keep from having 100 iframes
7358 var shims = [];
7359
7360 Roo.extend(Roo.Layer, Roo.Element, {
7361
7362     getZIndex : function(){
7363         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7364     },
7365
7366     getShim : function(){
7367         if(!this.useShim){
7368             return null;
7369         }
7370         if(this.shim){
7371             return this.shim;
7372         }
7373         var shim = shims.shift();
7374         if(!shim){
7375             shim = this.createShim();
7376             shim.enableDisplayMode('block');
7377             shim.dom.style.display = 'none';
7378             shim.dom.style.visibility = 'visible';
7379         }
7380         var pn = this.dom.parentNode;
7381         if(shim.dom.parentNode != pn){
7382             pn.insertBefore(shim.dom, this.dom);
7383         }
7384         shim.setStyle('z-index', this.getZIndex()-2);
7385         this.shim = shim;
7386         return shim;
7387     },
7388
7389     hideShim : function(){
7390         if(this.shim){
7391             this.shim.setDisplayed(false);
7392             shims.push(this.shim);
7393             delete this.shim;
7394         }
7395     },
7396
7397     disableShadow : function(){
7398         if(this.shadow){
7399             this.shadowDisabled = true;
7400             this.shadow.hide();
7401             this.lastShadowOffset = this.shadowOffset;
7402             this.shadowOffset = 0;
7403         }
7404     },
7405
7406     enableShadow : function(show){
7407         if(this.shadow){
7408             this.shadowDisabled = false;
7409             this.shadowOffset = this.lastShadowOffset;
7410             delete this.lastShadowOffset;
7411             if(show){
7412                 this.sync(true);
7413             }
7414         }
7415     },
7416
7417     // private
7418     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7419     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7420     sync : function(doShow){
7421         var sw = this.shadow;
7422         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7423             var sh = this.getShim();
7424
7425             var w = this.getWidth(),
7426                 h = this.getHeight();
7427
7428             var l = this.getLeft(true),
7429                 t = this.getTop(true);
7430
7431             if(sw && !this.shadowDisabled){
7432                 if(doShow && !sw.isVisible()){
7433                     sw.show(this);
7434                 }else{
7435                     sw.realign(l, t, w, h);
7436                 }
7437                 if(sh){
7438                     if(doShow){
7439                        sh.show();
7440                     }
7441                     // fit the shim behind the shadow, so it is shimmed too
7442                     var a = sw.adjusts, s = sh.dom.style;
7443                     s.left = (Math.min(l, l+a.l))+"px";
7444                     s.top = (Math.min(t, t+a.t))+"px";
7445                     s.width = (w+a.w)+"px";
7446                     s.height = (h+a.h)+"px";
7447                 }
7448             }else if(sh){
7449                 if(doShow){
7450                    sh.show();
7451                 }
7452                 sh.setSize(w, h);
7453                 sh.setLeftTop(l, t);
7454             }
7455             
7456         }
7457     },
7458
7459     // private
7460     destroy : function(){
7461         this.hideShim();
7462         if(this.shadow){
7463             this.shadow.hide();
7464         }
7465         this.removeAllListeners();
7466         var pn = this.dom.parentNode;
7467         if(pn){
7468             pn.removeChild(this.dom);
7469         }
7470         Roo.Element.uncache(this.id);
7471     },
7472
7473     remove : function(){
7474         this.destroy();
7475     },
7476
7477     // private
7478     beginUpdate : function(){
7479         this.updating = true;
7480     },
7481
7482     // private
7483     endUpdate : function(){
7484         this.updating = false;
7485         this.sync(true);
7486     },
7487
7488     // private
7489     hideUnders : function(negOffset){
7490         if(this.shadow){
7491             this.shadow.hide();
7492         }
7493         this.hideShim();
7494     },
7495
7496     // private
7497     constrainXY : function(){
7498         if(this.constrain){
7499             var vw = Roo.lib.Dom.getViewWidth(),
7500                 vh = Roo.lib.Dom.getViewHeight();
7501             var s = Roo.get(document).getScroll();
7502
7503             var xy = this.getXY();
7504             var x = xy[0], y = xy[1];   
7505             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7506             // only move it if it needs it
7507             var moved = false;
7508             // first validate right/bottom
7509             if((x + w) > vw+s.left){
7510                 x = vw - w - this.shadowOffset;
7511                 moved = true;
7512             }
7513             if((y + h) > vh+s.top){
7514                 y = vh - h - this.shadowOffset;
7515                 moved = true;
7516             }
7517             // then make sure top/left isn't negative
7518             if(x < s.left){
7519                 x = s.left;
7520                 moved = true;
7521             }
7522             if(y < s.top){
7523                 y = s.top;
7524                 moved = true;
7525             }
7526             if(moved){
7527                 if(this.avoidY){
7528                     var ay = this.avoidY;
7529                     if(y <= ay && (y+h) >= ay){
7530                         y = ay-h-5;   
7531                     }
7532                 }
7533                 xy = [x, y];
7534                 this.storeXY(xy);
7535                 supr.setXY.call(this, xy);
7536                 this.sync();
7537             }
7538         }
7539     },
7540
7541     isVisible : function(){
7542         return this.visible;    
7543     },
7544
7545     // private
7546     showAction : function(){
7547         this.visible = true; // track visibility to prevent getStyle calls
7548         if(this.useDisplay === true){
7549             this.setDisplayed("");
7550         }else if(this.lastXY){
7551             supr.setXY.call(this, this.lastXY);
7552         }else if(this.lastLT){
7553             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7554         }
7555     },
7556
7557     // private
7558     hideAction : function(){
7559         this.visible = false;
7560         if(this.useDisplay === true){
7561             this.setDisplayed(false);
7562         }else{
7563             this.setLeftTop(-10000,-10000);
7564         }
7565     },
7566
7567     // overridden Element method
7568     setVisible : function(v, a, d, c, e){
7569         if(v){
7570             this.showAction();
7571         }
7572         if(a && v){
7573             var cb = function(){
7574                 this.sync(true);
7575                 if(c){
7576                     c();
7577                 }
7578             }.createDelegate(this);
7579             supr.setVisible.call(this, true, true, d, cb, e);
7580         }else{
7581             if(!v){
7582                 this.hideUnders(true);
7583             }
7584             var cb = c;
7585             if(a){
7586                 cb = function(){
7587                     this.hideAction();
7588                     if(c){
7589                         c();
7590                     }
7591                 }.createDelegate(this);
7592             }
7593             supr.setVisible.call(this, v, a, d, cb, e);
7594             if(v){
7595                 this.sync(true);
7596             }else if(!a){
7597                 this.hideAction();
7598             }
7599         }
7600     },
7601
7602     storeXY : function(xy){
7603         delete this.lastLT;
7604         this.lastXY = xy;
7605     },
7606
7607     storeLeftTop : function(left, top){
7608         delete this.lastXY;
7609         this.lastLT = [left, top];
7610     },
7611
7612     // private
7613     beforeFx : function(){
7614         this.beforeAction();
7615         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7616     },
7617
7618     // private
7619     afterFx : function(){
7620         Roo.Layer.superclass.afterFx.apply(this, arguments);
7621         this.sync(this.isVisible());
7622     },
7623
7624     // private
7625     beforeAction : function(){
7626         if(!this.updating && this.shadow){
7627             this.shadow.hide();
7628         }
7629     },
7630
7631     // overridden Element method
7632     setLeft : function(left){
7633         this.storeLeftTop(left, this.getTop(true));
7634         supr.setLeft.apply(this, arguments);
7635         this.sync();
7636     },
7637
7638     setTop : function(top){
7639         this.storeLeftTop(this.getLeft(true), top);
7640         supr.setTop.apply(this, arguments);
7641         this.sync();
7642     },
7643
7644     setLeftTop : function(left, top){
7645         this.storeLeftTop(left, top);
7646         supr.setLeftTop.apply(this, arguments);
7647         this.sync();
7648     },
7649
7650     setXY : function(xy, a, d, c, e){
7651         this.fixDisplay();
7652         this.beforeAction();
7653         this.storeXY(xy);
7654         var cb = this.createCB(c);
7655         supr.setXY.call(this, xy, a, d, cb, e);
7656         if(!a){
7657             cb();
7658         }
7659     },
7660
7661     // private
7662     createCB : function(c){
7663         var el = this;
7664         return function(){
7665             el.constrainXY();
7666             el.sync(true);
7667             if(c){
7668                 c();
7669             }
7670         };
7671     },
7672
7673     // overridden Element method
7674     setX : function(x, a, d, c, e){
7675         this.setXY([x, this.getY()], a, d, c, e);
7676     },
7677
7678     // overridden Element method
7679     setY : function(y, a, d, c, e){
7680         this.setXY([this.getX(), y], a, d, c, e);
7681     },
7682
7683     // overridden Element method
7684     setSize : function(w, h, a, d, c, e){
7685         this.beforeAction();
7686         var cb = this.createCB(c);
7687         supr.setSize.call(this, w, h, a, d, cb, e);
7688         if(!a){
7689             cb();
7690         }
7691     },
7692
7693     // overridden Element method
7694     setWidth : function(w, a, d, c, e){
7695         this.beforeAction();
7696         var cb = this.createCB(c);
7697         supr.setWidth.call(this, w, a, d, cb, e);
7698         if(!a){
7699             cb();
7700         }
7701     },
7702
7703     // overridden Element method
7704     setHeight : function(h, a, d, c, e){
7705         this.beforeAction();
7706         var cb = this.createCB(c);
7707         supr.setHeight.call(this, h, a, d, cb, e);
7708         if(!a){
7709             cb();
7710         }
7711     },
7712
7713     // overridden Element method
7714     setBounds : function(x, y, w, h, a, d, c, e){
7715         this.beforeAction();
7716         var cb = this.createCB(c);
7717         if(!a){
7718             this.storeXY([x, y]);
7719             supr.setXY.call(this, [x, y]);
7720             supr.setSize.call(this, w, h, a, d, cb, e);
7721             cb();
7722         }else{
7723             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
7724         }
7725         return this;
7726     },
7727     
7728     /**
7729      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
7730      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
7731      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
7732      * @param {Number} zindex The new z-index to set
7733      * @return {this} The Layer
7734      */
7735     setZIndex : function(zindex){
7736         this.zindex = zindex;
7737         this.setStyle("z-index", zindex + 2);
7738         if(this.shadow){
7739             this.shadow.setZIndex(zindex + 1);
7740         }
7741         if(this.shim){
7742             this.shim.setStyle("z-index", zindex);
7743         }
7744     }
7745 });
7746 })();/*
7747  * Based on:
7748  * Ext JS Library 1.1.1
7749  * Copyright(c) 2006-2007, Ext JS, LLC.
7750  *
7751  * Originally Released Under LGPL - original licence link has changed is not relivant.
7752  *
7753  * Fork - LGPL
7754  * <script type="text/javascript">
7755  */
7756
7757
7758 /**
7759  * @class Roo.Shadow
7760  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
7761  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
7762  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
7763  * @constructor
7764  * Create a new Shadow
7765  * @param {Object} config The config object
7766  */
7767 Roo.Shadow = function(config){
7768     Roo.apply(this, config);
7769     if(typeof this.mode != "string"){
7770         this.mode = this.defaultMode;
7771     }
7772     var o = this.offset, a = {h: 0};
7773     var rad = Math.floor(this.offset/2);
7774     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
7775         case "drop":
7776             a.w = 0;
7777             a.l = a.t = o;
7778             a.t -= 1;
7779             if(Roo.isIE){
7780                 a.l -= this.offset + rad;
7781                 a.t -= this.offset + rad;
7782                 a.w -= rad;
7783                 a.h -= rad;
7784                 a.t += 1;
7785             }
7786         break;
7787         case "sides":
7788             a.w = (o*2);
7789             a.l = -o;
7790             a.t = o-1;
7791             if(Roo.isIE){
7792                 a.l -= (this.offset - rad);
7793                 a.t -= this.offset + rad;
7794                 a.l += 1;
7795                 a.w -= (this.offset - rad)*2;
7796                 a.w -= rad + 1;
7797                 a.h -= 1;
7798             }
7799         break;
7800         case "frame":
7801             a.w = a.h = (o*2);
7802             a.l = a.t = -o;
7803             a.t += 1;
7804             a.h -= 2;
7805             if(Roo.isIE){
7806                 a.l -= (this.offset - rad);
7807                 a.t -= (this.offset - rad);
7808                 a.l += 1;
7809                 a.w -= (this.offset + rad + 1);
7810                 a.h -= (this.offset + rad);
7811                 a.h += 1;
7812             }
7813         break;
7814     };
7815
7816     this.adjusts = a;
7817 };
7818
7819 Roo.Shadow.prototype = {
7820     /**
7821      * @cfg {String} mode
7822      * The shadow display mode.  Supports the following options:<br />
7823      * sides: Shadow displays on both sides and bottom only<br />
7824      * frame: Shadow displays equally on all four sides<br />
7825      * drop: Traditional bottom-right drop shadow (default)
7826      */
7827     /**
7828      * @cfg {String} offset
7829      * The number of pixels to offset the shadow from the element (defaults to 4)
7830      */
7831     offset: 4,
7832
7833     // private
7834     defaultMode: "drop",
7835
7836     /**
7837      * Displays the shadow under the target element
7838      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
7839      */
7840     show : function(target){
7841         target = Roo.get(target);
7842         if(!this.el){
7843             this.el = Roo.Shadow.Pool.pull();
7844             if(this.el.dom.nextSibling != target.dom){
7845                 this.el.insertBefore(target);
7846             }
7847         }
7848         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
7849         if(Roo.isIE){
7850             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
7851         }
7852         this.realign(
7853             target.getLeft(true),
7854             target.getTop(true),
7855             target.getWidth(),
7856             target.getHeight()
7857         );
7858         this.el.dom.style.display = "block";
7859     },
7860
7861     /**
7862      * Returns true if the shadow is visible, else false
7863      */
7864     isVisible : function(){
7865         return this.el ? true : false;  
7866     },
7867
7868     /**
7869      * Direct alignment when values are already available. Show must be called at least once before
7870      * calling this method to ensure it is initialized.
7871      * @param {Number} left The target element left position
7872      * @param {Number} top The target element top position
7873      * @param {Number} width The target element width
7874      * @param {Number} height The target element height
7875      */
7876     realign : function(l, t, w, h){
7877         if(!this.el){
7878             return;
7879         }
7880         var a = this.adjusts, d = this.el.dom, s = d.style;
7881         var iea = 0;
7882         s.left = (l+a.l)+"px";
7883         s.top = (t+a.t)+"px";
7884         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
7885  
7886         if(s.width != sws || s.height != shs){
7887             s.width = sws;
7888             s.height = shs;
7889             if(!Roo.isIE){
7890                 var cn = d.childNodes;
7891                 var sww = Math.max(0, (sw-12))+"px";
7892                 cn[0].childNodes[1].style.width = sww;
7893                 cn[1].childNodes[1].style.width = sww;
7894                 cn[2].childNodes[1].style.width = sww;
7895                 cn[1].style.height = Math.max(0, (sh-12))+"px";
7896             }
7897         }
7898     },
7899
7900     /**
7901      * Hides this shadow
7902      */
7903     hide : function(){
7904         if(this.el){
7905             this.el.dom.style.display = "none";
7906             Roo.Shadow.Pool.push(this.el);
7907             delete this.el;
7908         }
7909     },
7910
7911     /**
7912      * Adjust the z-index of this shadow
7913      * @param {Number} zindex The new z-index
7914      */
7915     setZIndex : function(z){
7916         this.zIndex = z;
7917         if(this.el){
7918             this.el.setStyle("z-index", z);
7919         }
7920     }
7921 };
7922
7923 // Private utility class that manages the internal Shadow cache
7924 Roo.Shadow.Pool = function(){
7925     var p = [];
7926     var markup = Roo.isIE ?
7927                  '<div class="x-ie-shadow"></div>' :
7928                  '<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>';
7929     return {
7930         pull : function(){
7931             var sh = p.shift();
7932             if(!sh){
7933                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
7934                 sh.autoBoxAdjust = false;
7935             }
7936             return sh;
7937         },
7938
7939         push : function(sh){
7940             p.push(sh);
7941         }
7942     };
7943 }();/*
7944  * Based on:
7945  * Ext JS Library 1.1.1
7946  * Copyright(c) 2006-2007, Ext JS, LLC.
7947  *
7948  * Originally Released Under LGPL - original licence link has changed is not relivant.
7949  *
7950  * Fork - LGPL
7951  * <script type="text/javascript">
7952  */
7953
7954
7955 /**
7956  * @class Roo.SplitBar
7957  * @extends Roo.util.Observable
7958  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
7959  * <br><br>
7960  * Usage:
7961  * <pre><code>
7962 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
7963                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
7964 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
7965 split.minSize = 100;
7966 split.maxSize = 600;
7967 split.animate = true;
7968 split.on('moved', splitterMoved);
7969 </code></pre>
7970  * @constructor
7971  * Create a new SplitBar
7972  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
7973  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
7974  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7975  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
7976                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
7977                         position of the SplitBar).
7978  */
7979 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
7980     
7981     /** @private */
7982     this.el = Roo.get(dragElement, true);
7983     this.el.dom.unselectable = "on";
7984     /** @private */
7985     this.resizingEl = Roo.get(resizingElement, true);
7986
7987     /**
7988      * @private
7989      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
7990      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
7991      * @type Number
7992      */
7993     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
7994     
7995     /**
7996      * The minimum size of the resizing element. (Defaults to 0)
7997      * @type Number
7998      */
7999     this.minSize = 0;
8000     
8001     /**
8002      * The maximum size of the resizing element. (Defaults to 2000)
8003      * @type Number
8004      */
8005     this.maxSize = 2000;
8006     
8007     /**
8008      * Whether to animate the transition to the new size
8009      * @type Boolean
8010      */
8011     this.animate = false;
8012     
8013     /**
8014      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8015      * @type Boolean
8016      */
8017     this.useShim = false;
8018     
8019     /** @private */
8020     this.shim = null;
8021     
8022     if(!existingProxy){
8023         /** @private */
8024         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8025     }else{
8026         this.proxy = Roo.get(existingProxy).dom;
8027     }
8028     /** @private */
8029     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8030     
8031     /** @private */
8032     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8033     
8034     /** @private */
8035     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8036     
8037     /** @private */
8038     this.dragSpecs = {};
8039     
8040     /**
8041      * @private The adapter to use to positon and resize elements
8042      */
8043     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8044     this.adapter.init(this);
8045     
8046     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8047         /** @private */
8048         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8049         this.el.addClass("x-splitbar-h");
8050     }else{
8051         /** @private */
8052         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8053         this.el.addClass("x-splitbar-v");
8054     }
8055     
8056     this.addEvents({
8057         /**
8058          * @event resize
8059          * Fires when the splitter is moved (alias for {@link #event-moved})
8060          * @param {Roo.SplitBar} this
8061          * @param {Number} newSize the new width or height
8062          */
8063         "resize" : true,
8064         /**
8065          * @event moved
8066          * Fires when the splitter is moved
8067          * @param {Roo.SplitBar} this
8068          * @param {Number} newSize the new width or height
8069          */
8070         "moved" : true,
8071         /**
8072          * @event beforeresize
8073          * Fires before the splitter is dragged
8074          * @param {Roo.SplitBar} this
8075          */
8076         "beforeresize" : true,
8077
8078         "beforeapply" : true
8079     });
8080
8081     Roo.util.Observable.call(this);
8082 };
8083
8084 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8085     onStartProxyDrag : function(x, y){
8086         this.fireEvent("beforeresize", this);
8087         if(!this.overlay){
8088             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8089             o.unselectable();
8090             o.enableDisplayMode("block");
8091             // all splitbars share the same overlay
8092             Roo.SplitBar.prototype.overlay = o;
8093         }
8094         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8095         this.overlay.show();
8096         Roo.get(this.proxy).setDisplayed("block");
8097         var size = this.adapter.getElementSize(this);
8098         this.activeMinSize = this.getMinimumSize();;
8099         this.activeMaxSize = this.getMaximumSize();;
8100         var c1 = size - this.activeMinSize;
8101         var c2 = Math.max(this.activeMaxSize - size, 0);
8102         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8103             this.dd.resetConstraints();
8104             this.dd.setXConstraint(
8105                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8106                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8107             );
8108             this.dd.setYConstraint(0, 0);
8109         }else{
8110             this.dd.resetConstraints();
8111             this.dd.setXConstraint(0, 0);
8112             this.dd.setYConstraint(
8113                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8114                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8115             );
8116          }
8117         this.dragSpecs.startSize = size;
8118         this.dragSpecs.startPoint = [x, y];
8119         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8120     },
8121     
8122     /** 
8123      * @private Called after the drag operation by the DDProxy
8124      */
8125     onEndProxyDrag : function(e){
8126         Roo.get(this.proxy).setDisplayed(false);
8127         var endPoint = Roo.lib.Event.getXY(e);
8128         if(this.overlay){
8129             this.overlay.hide();
8130         }
8131         var newSize;
8132         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8133             newSize = this.dragSpecs.startSize + 
8134                 (this.placement == Roo.SplitBar.LEFT ?
8135                     endPoint[0] - this.dragSpecs.startPoint[0] :
8136                     this.dragSpecs.startPoint[0] - endPoint[0]
8137                 );
8138         }else{
8139             newSize = this.dragSpecs.startSize + 
8140                 (this.placement == Roo.SplitBar.TOP ?
8141                     endPoint[1] - this.dragSpecs.startPoint[1] :
8142                     this.dragSpecs.startPoint[1] - endPoint[1]
8143                 );
8144         }
8145         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8146         if(newSize != this.dragSpecs.startSize){
8147             if(this.fireEvent('beforeapply', this, newSize) !== false){
8148                 this.adapter.setElementSize(this, newSize);
8149                 this.fireEvent("moved", this, newSize);
8150                 this.fireEvent("resize", this, newSize);
8151             }
8152         }
8153     },
8154     
8155     /**
8156      * Get the adapter this SplitBar uses
8157      * @return The adapter object
8158      */
8159     getAdapter : function(){
8160         return this.adapter;
8161     },
8162     
8163     /**
8164      * Set the adapter this SplitBar uses
8165      * @param {Object} adapter A SplitBar adapter object
8166      */
8167     setAdapter : function(adapter){
8168         this.adapter = adapter;
8169         this.adapter.init(this);
8170     },
8171     
8172     /**
8173      * Gets the minimum size for the resizing element
8174      * @return {Number} The minimum size
8175      */
8176     getMinimumSize : function(){
8177         return this.minSize;
8178     },
8179     
8180     /**
8181      * Sets the minimum size for the resizing element
8182      * @param {Number} minSize The minimum size
8183      */
8184     setMinimumSize : function(minSize){
8185         this.minSize = minSize;
8186     },
8187     
8188     /**
8189      * Gets the maximum size for the resizing element
8190      * @return {Number} The maximum size
8191      */
8192     getMaximumSize : function(){
8193         return this.maxSize;
8194     },
8195     
8196     /**
8197      * Sets the maximum size for the resizing element
8198      * @param {Number} maxSize The maximum size
8199      */
8200     setMaximumSize : function(maxSize){
8201         this.maxSize = maxSize;
8202     },
8203     
8204     /**
8205      * Sets the initialize size for the resizing element
8206      * @param {Number} size The initial size
8207      */
8208     setCurrentSize : function(size){
8209         var oldAnimate = this.animate;
8210         this.animate = false;
8211         this.adapter.setElementSize(this, size);
8212         this.animate = oldAnimate;
8213     },
8214     
8215     /**
8216      * Destroy this splitbar. 
8217      * @param {Boolean} removeEl True to remove the element
8218      */
8219     destroy : function(removeEl){
8220         if(this.shim){
8221             this.shim.remove();
8222         }
8223         this.dd.unreg();
8224         this.proxy.parentNode.removeChild(this.proxy);
8225         if(removeEl){
8226             this.el.remove();
8227         }
8228     }
8229 });
8230
8231 /**
8232  * @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.
8233  */
8234 Roo.SplitBar.createProxy = function(dir){
8235     var proxy = new Roo.Element(document.createElement("div"));
8236     proxy.unselectable();
8237     var cls = 'x-splitbar-proxy';
8238     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8239     document.body.appendChild(proxy.dom);
8240     return proxy.dom;
8241 };
8242
8243 /** 
8244  * @class Roo.SplitBar.BasicLayoutAdapter
8245  * Default Adapter. It assumes the splitter and resizing element are not positioned
8246  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8247  */
8248 Roo.SplitBar.BasicLayoutAdapter = function(){
8249 };
8250
8251 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8252     // do nothing for now
8253     init : function(s){
8254     
8255     },
8256     /**
8257      * Called before drag operations to get the current size of the resizing element. 
8258      * @param {Roo.SplitBar} s The SplitBar using this adapter
8259      */
8260      getElementSize : function(s){
8261         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8262             return s.resizingEl.getWidth();
8263         }else{
8264             return s.resizingEl.getHeight();
8265         }
8266     },
8267     
8268     /**
8269      * Called after drag operations to set the size of the resizing element.
8270      * @param {Roo.SplitBar} s The SplitBar using this adapter
8271      * @param {Number} newSize The new size to set
8272      * @param {Function} onComplete A function to be invoked when resizing is complete
8273      */
8274     setElementSize : function(s, newSize, onComplete){
8275         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8276             if(!s.animate){
8277                 s.resizingEl.setWidth(newSize);
8278                 if(onComplete){
8279                     onComplete(s, newSize);
8280                 }
8281             }else{
8282                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8283             }
8284         }else{
8285             
8286             if(!s.animate){
8287                 s.resizingEl.setHeight(newSize);
8288                 if(onComplete){
8289                     onComplete(s, newSize);
8290                 }
8291             }else{
8292                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8293             }
8294         }
8295     }
8296 };
8297
8298 /** 
8299  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8300  * @extends Roo.SplitBar.BasicLayoutAdapter
8301  * Adapter that  moves the splitter element to align with the resized sizing element. 
8302  * Used with an absolute positioned SplitBar.
8303  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8304  * document.body, make sure you assign an id to the body element.
8305  */
8306 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8307     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8308     this.container = Roo.get(container);
8309 };
8310
8311 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8312     init : function(s){
8313         this.basic.init(s);
8314     },
8315     
8316     getElementSize : function(s){
8317         return this.basic.getElementSize(s);
8318     },
8319     
8320     setElementSize : function(s, newSize, onComplete){
8321         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8322     },
8323     
8324     moveSplitter : function(s){
8325         var yes = Roo.SplitBar;
8326         switch(s.placement){
8327             case yes.LEFT:
8328                 s.el.setX(s.resizingEl.getRight());
8329                 break;
8330             case yes.RIGHT:
8331                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8332                 break;
8333             case yes.TOP:
8334                 s.el.setY(s.resizingEl.getBottom());
8335                 break;
8336             case yes.BOTTOM:
8337                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8338                 break;
8339         }
8340     }
8341 };
8342
8343 /**
8344  * Orientation constant - Create a vertical SplitBar
8345  * @static
8346  * @type Number
8347  */
8348 Roo.SplitBar.VERTICAL = 1;
8349
8350 /**
8351  * Orientation constant - Create a horizontal SplitBar
8352  * @static
8353  * @type Number
8354  */
8355 Roo.SplitBar.HORIZONTAL = 2;
8356
8357 /**
8358  * Placement constant - The resizing element is to the left of the splitter element
8359  * @static
8360  * @type Number
8361  */
8362 Roo.SplitBar.LEFT = 1;
8363
8364 /**
8365  * Placement constant - The resizing element is to the right of the splitter element
8366  * @static
8367  * @type Number
8368  */
8369 Roo.SplitBar.RIGHT = 2;
8370
8371 /**
8372  * Placement constant - The resizing element is positioned above the splitter element
8373  * @static
8374  * @type Number
8375  */
8376 Roo.SplitBar.TOP = 3;
8377
8378 /**
8379  * Placement constant - The resizing element is positioned under splitter element
8380  * @static
8381  * @type Number
8382  */
8383 Roo.SplitBar.BOTTOM = 4;
8384 /*
8385  * Based on:
8386  * Ext JS Library 1.1.1
8387  * Copyright(c) 2006-2007, Ext JS, LLC.
8388  *
8389  * Originally Released Under LGPL - original licence link has changed is not relivant.
8390  *
8391  * Fork - LGPL
8392  * <script type="text/javascript">
8393  */
8394
8395 /**
8396  * @class Roo.View
8397  * @extends Roo.util.Observable
8398  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8399  * This class also supports single and multi selection modes. <br>
8400  * Create a data model bound view:
8401  <pre><code>
8402  var store = new Roo.data.Store(...);
8403
8404  var view = new Roo.View({
8405     el : "my-element",
8406     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8407  
8408     singleSelect: true,
8409     selectedClass: "ydataview-selected",
8410     store: store
8411  });
8412
8413  // listen for node click?
8414  view.on("click", function(vw, index, node, e){
8415  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8416  });
8417
8418  // load XML data
8419  dataModel.load("foobar.xml");
8420  </code></pre>
8421  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8422  * <br><br>
8423  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8424  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8425  * 
8426  * Note: old style constructor is still suported (container, template, config)
8427  * 
8428  * @constructor
8429  * Create a new View
8430  * @param {Object} config The config object
8431  * 
8432  */
8433 Roo.View = function(config, depreciated_tpl, depreciated_config){
8434     
8435     this.parent = false;
8436     
8437     if (typeof(depreciated_tpl) == 'undefined') {
8438         // new way.. - universal constructor.
8439         Roo.apply(this, config);
8440         this.el  = Roo.get(this.el);
8441     } else {
8442         // old format..
8443         this.el  = Roo.get(config);
8444         this.tpl = depreciated_tpl;
8445         Roo.apply(this, depreciated_config);
8446     }
8447     this.wrapEl  = this.el.wrap().wrap();
8448     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8449     
8450     
8451     if(typeof(this.tpl) == "string"){
8452         this.tpl = new Roo.Template(this.tpl);
8453     } else {
8454         // support xtype ctors..
8455         this.tpl = new Roo.factory(this.tpl, Roo);
8456     }
8457     
8458     
8459     this.tpl.compile();
8460     
8461     /** @private */
8462     this.addEvents({
8463         /**
8464          * @event beforeclick
8465          * Fires before a click is processed. Returns false to cancel the default action.
8466          * @param {Roo.View} this
8467          * @param {Number} index The index of the target node
8468          * @param {HTMLElement} node The target node
8469          * @param {Roo.EventObject} e The raw event object
8470          */
8471             "beforeclick" : true,
8472         /**
8473          * @event click
8474          * Fires when a template node is clicked.
8475          * @param {Roo.View} this
8476          * @param {Number} index The index of the target node
8477          * @param {HTMLElement} node The target node
8478          * @param {Roo.EventObject} e The raw event object
8479          */
8480             "click" : true,
8481         /**
8482          * @event dblclick
8483          * Fires when a template node is double clicked.
8484          * @param {Roo.View} this
8485          * @param {Number} index The index of the target node
8486          * @param {HTMLElement} node The target node
8487          * @param {Roo.EventObject} e The raw event object
8488          */
8489             "dblclick" : true,
8490         /**
8491          * @event contextmenu
8492          * Fires when a template node is right clicked.
8493          * @param {Roo.View} this
8494          * @param {Number} index The index of the target node
8495          * @param {HTMLElement} node The target node
8496          * @param {Roo.EventObject} e The raw event object
8497          */
8498             "contextmenu" : true,
8499         /**
8500          * @event selectionchange
8501          * Fires when the selected nodes change.
8502          * @param {Roo.View} this
8503          * @param {Array} selections Array of the selected nodes
8504          */
8505             "selectionchange" : true,
8506     
8507         /**
8508          * @event beforeselect
8509          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
8510          * @param {Roo.View} this
8511          * @param {HTMLElement} node The node to be selected
8512          * @param {Array} selections Array of currently selected nodes
8513          */
8514             "beforeselect" : true,
8515         /**
8516          * @event preparedata
8517          * Fires on every row to render, to allow you to change the data.
8518          * @param {Roo.View} this
8519          * @param {Object} data to be rendered (change this)
8520          */
8521           "preparedata" : true
8522           
8523           
8524         });
8525
8526
8527
8528     this.el.on({
8529         "click": this.onClick,
8530         "dblclick": this.onDblClick,
8531         "contextmenu": this.onContextMenu,
8532         scope:this
8533     });
8534
8535     this.selections = [];
8536     this.nodes = [];
8537     this.cmp = new Roo.CompositeElementLite([]);
8538     if(this.store){
8539         this.store = Roo.factory(this.store, Roo.data);
8540         this.setStore(this.store, true);
8541     }
8542     
8543     if ( this.footer && this.footer.xtype) {
8544            
8545          var fctr = this.wrapEl.appendChild(document.createElement("div"));
8546         
8547         this.footer.dataSource = this.store
8548         this.footer.container = fctr;
8549         this.footer = Roo.factory(this.footer, Roo);
8550         fctr.insertFirst(this.el);
8551         
8552         // this is a bit insane - as the paging toolbar seems to detach the el..
8553 //        dom.parentNode.parentNode.parentNode
8554          // they get detached?
8555     }
8556     
8557     
8558     Roo.View.superclass.constructor.call(this);
8559     
8560     
8561 };
8562
8563 Roo.extend(Roo.View, Roo.util.Observable, {
8564     
8565      /**
8566      * @cfg {Roo.data.Store} store Data store to load data from.
8567      */
8568     store : false,
8569     
8570     /**
8571      * @cfg {String|Roo.Element} el The container element.
8572      */
8573     el : '',
8574     
8575     /**
8576      * @cfg {String|Roo.Template} tpl The template used by this View 
8577      */
8578     tpl : false,
8579     /**
8580      * @cfg {String} dataName the named area of the template to use as the data area
8581      *                          Works with domtemplates roo-name="name"
8582      */
8583     dataName: false,
8584     /**
8585      * @cfg {String} selectedClass The css class to add to selected nodes
8586      */
8587     selectedClass : "x-view-selected",
8588      /**
8589      * @cfg {String} emptyText The empty text to show when nothing is loaded.
8590      */
8591     emptyText : "",
8592     
8593     /**
8594      * @cfg {String} text to display on mask (default Loading)
8595      */
8596     mask : false,
8597     /**
8598      * @cfg {Boolean} multiSelect Allow multiple selection
8599      */
8600     multiSelect : false,
8601     /**
8602      * @cfg {Boolean} singleSelect Allow single selection
8603      */
8604     singleSelect:  false,
8605     
8606     /**
8607      * @cfg {Boolean} toggleSelect - selecting 
8608      */
8609     toggleSelect : false,
8610     
8611     /**
8612      * @cfg {Boolean} tickable - selecting 
8613      */
8614     tickable : false,
8615     
8616     /**
8617      * Returns the element this view is bound to.
8618      * @return {Roo.Element}
8619      */
8620     getEl : function(){
8621         return this.wrapEl;
8622     },
8623     
8624     
8625
8626     /**
8627      * Refreshes the view. - called by datachanged on the store. - do not call directly.
8628      */
8629     refresh : function(){
8630         //Roo.log('refresh');
8631         var t = this.tpl;
8632         
8633         // if we are using something like 'domtemplate', then
8634         // the what gets used is:
8635         // t.applySubtemplate(NAME, data, wrapping data..)
8636         // the outer template then get' applied with
8637         //     the store 'extra data'
8638         // and the body get's added to the
8639         //      roo-name="data" node?
8640         //      <span class='roo-tpl-{name}'></span> ?????
8641         
8642         
8643         
8644         this.clearSelections();
8645         this.el.update("");
8646         var html = [];
8647         var records = this.store.getRange();
8648         if(records.length < 1) {
8649             
8650             // is this valid??  = should it render a template??
8651             
8652             this.el.update(this.emptyText);
8653             return;
8654         }
8655         var el = this.el;
8656         if (this.dataName) {
8657             this.el.update(t.apply(this.store.meta)); //????
8658             el = this.el.child('.roo-tpl-' + this.dataName);
8659         }
8660         
8661         for(var i = 0, len = records.length; i < len; i++){
8662             var data = this.prepareData(records[i].data, i, records[i]);
8663             this.fireEvent("preparedata", this, data, i, records[i]);
8664             
8665             var d = Roo.apply({}, data);
8666             
8667             if(this.tickable){
8668                 Roo.apply(d, {'roo-id' : Roo.id()});
8669                 
8670                 var _this = this;
8671             
8672                 Roo.each(this.parent.item, function(item){
8673                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
8674                         return;
8675                     }
8676                     Roo.apply(d, {'roo-data-checked' : 'checked'});
8677                 });
8678             }
8679             
8680             html[html.length] = Roo.util.Format.trim(
8681                 this.dataName ?
8682                     t.applySubtemplate(this.dataName, d, this.store.meta) :
8683                     t.apply(d)
8684             );
8685         }
8686         
8687         
8688         
8689         el.update(html.join(""));
8690         this.nodes = el.dom.childNodes;
8691         this.updateIndexes(0);
8692     },
8693     
8694
8695     /**
8696      * Function to override to reformat the data that is sent to
8697      * the template for each node.
8698      * DEPRICATED - use the preparedata event handler.
8699      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
8700      * a JSON object for an UpdateManager bound view).
8701      */
8702     prepareData : function(data, index, record)
8703     {
8704         this.fireEvent("preparedata", this, data, index, record);
8705         return data;
8706     },
8707
8708     onUpdate : function(ds, record){
8709         // Roo.log('on update');   
8710         this.clearSelections();
8711         var index = this.store.indexOf(record);
8712         var n = this.nodes[index];
8713         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
8714         n.parentNode.removeChild(n);
8715         this.updateIndexes(index, index);
8716     },
8717
8718     
8719     
8720 // --------- FIXME     
8721     onAdd : function(ds, records, index)
8722     {
8723         //Roo.log(['on Add', ds, records, index] );        
8724         this.clearSelections();
8725         if(this.nodes.length == 0){
8726             this.refresh();
8727             return;
8728         }
8729         var n = this.nodes[index];
8730         for(var i = 0, len = records.length; i < len; i++){
8731             var d = this.prepareData(records[i].data, i, records[i]);
8732             if(n){
8733                 this.tpl.insertBefore(n, d);
8734             }else{
8735                 
8736                 this.tpl.append(this.el, d);
8737             }
8738         }
8739         this.updateIndexes(index);
8740     },
8741
8742     onRemove : function(ds, record, index){
8743        // Roo.log('onRemove');
8744         this.clearSelections();
8745         var el = this.dataName  ?
8746             this.el.child('.roo-tpl-' + this.dataName) :
8747             this.el; 
8748         
8749         el.dom.removeChild(this.nodes[index]);
8750         this.updateIndexes(index);
8751     },
8752
8753     /**
8754      * Refresh an individual node.
8755      * @param {Number} index
8756      */
8757     refreshNode : function(index){
8758         this.onUpdate(this.store, this.store.getAt(index));
8759     },
8760
8761     updateIndexes : function(startIndex, endIndex){
8762         var ns = this.nodes;
8763         startIndex = startIndex || 0;
8764         endIndex = endIndex || ns.length - 1;
8765         for(var i = startIndex; i <= endIndex; i++){
8766             ns[i].nodeIndex = i;
8767         }
8768     },
8769
8770     /**
8771      * Changes the data store this view uses and refresh the view.
8772      * @param {Store} store
8773      */
8774     setStore : function(store, initial){
8775         if(!initial && this.store){
8776             this.store.un("datachanged", this.refresh);
8777             this.store.un("add", this.onAdd);
8778             this.store.un("remove", this.onRemove);
8779             this.store.un("update", this.onUpdate);
8780             this.store.un("clear", this.refresh);
8781             this.store.un("beforeload", this.onBeforeLoad);
8782             this.store.un("load", this.onLoad);
8783             this.store.un("loadexception", this.onLoad);
8784         }
8785         if(store){
8786           
8787             store.on("datachanged", this.refresh, this);
8788             store.on("add", this.onAdd, this);
8789             store.on("remove", this.onRemove, this);
8790             store.on("update", this.onUpdate, this);
8791             store.on("clear", this.refresh, this);
8792             store.on("beforeload", this.onBeforeLoad, this);
8793             store.on("load", this.onLoad, this);
8794             store.on("loadexception", this.onLoad, this);
8795         }
8796         
8797         if(store){
8798             this.refresh();
8799         }
8800     },
8801     /**
8802      * onbeforeLoad - masks the loading area.
8803      *
8804      */
8805     onBeforeLoad : function(store,opts)
8806     {
8807          //Roo.log('onBeforeLoad');   
8808         if (!opts.add) {
8809             this.el.update("");
8810         }
8811         this.el.mask(this.mask ? this.mask : "Loading" ); 
8812     },
8813     onLoad : function ()
8814     {
8815         this.el.unmask();
8816     },
8817     
8818
8819     /**
8820      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
8821      * @param {HTMLElement} node
8822      * @return {HTMLElement} The template node
8823      */
8824     findItemFromChild : function(node){
8825         var el = this.dataName  ?
8826             this.el.child('.roo-tpl-' + this.dataName,true) :
8827             this.el.dom; 
8828         
8829         if(!node || node.parentNode == el){
8830                     return node;
8831             }
8832             var p = node.parentNode;
8833             while(p && p != el){
8834             if(p.parentNode == el){
8835                 return p;
8836             }
8837             p = p.parentNode;
8838         }
8839             return null;
8840     },
8841
8842     /** @ignore */
8843     onClick : function(e){
8844         var item = this.findItemFromChild(e.getTarget());
8845         if(item){
8846             var index = this.indexOf(item);
8847             if(this.onItemClick(item, index, e) !== false){
8848                 this.fireEvent("click", this, index, item, e);
8849             }
8850         }else{
8851             this.clearSelections();
8852         }
8853     },
8854
8855     /** @ignore */
8856     onContextMenu : function(e){
8857         var item = this.findItemFromChild(e.getTarget());
8858         if(item){
8859             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
8860         }
8861     },
8862
8863     /** @ignore */
8864     onDblClick : function(e){
8865         var item = this.findItemFromChild(e.getTarget());
8866         if(item){
8867             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
8868         }
8869     },
8870
8871     onItemClick : function(item, index, e)
8872     {
8873         if(this.fireEvent("beforeclick", this, index, item, e) === false){
8874             return false;
8875         }
8876         if (this.toggleSelect) {
8877             var m = this.isSelected(item) ? 'unselect' : 'select';
8878             //Roo.log(m);
8879             var _t = this;
8880             _t[m](item, true, false);
8881             return true;
8882         }
8883         if(this.multiSelect || this.singleSelect){
8884             if(this.multiSelect && e.shiftKey && this.lastSelection){
8885                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
8886             }else{
8887                 this.select(item, this.multiSelect && e.ctrlKey);
8888                 this.lastSelection = item;
8889             }
8890             
8891             if(!this.tickable){
8892                 e.preventDefault();
8893             }
8894             
8895         }
8896         return true;
8897     },
8898
8899     /**
8900      * Get the number of selected nodes.
8901      * @return {Number}
8902      */
8903     getSelectionCount : function(){
8904         return this.selections.length;
8905     },
8906
8907     /**
8908      * Get the currently selected nodes.
8909      * @return {Array} An array of HTMLElements
8910      */
8911     getSelectedNodes : function(){
8912         return this.selections;
8913     },
8914
8915     /**
8916      * Get the indexes of the selected nodes.
8917      * @return {Array}
8918      */
8919     getSelectedIndexes : function(){
8920         var indexes = [], s = this.selections;
8921         for(var i = 0, len = s.length; i < len; i++){
8922             indexes.push(s[i].nodeIndex);
8923         }
8924         return indexes;
8925     },
8926
8927     /**
8928      * Clear all selections
8929      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
8930      */
8931     clearSelections : function(suppressEvent){
8932         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
8933             this.cmp.elements = this.selections;
8934             this.cmp.removeClass(this.selectedClass);
8935             this.selections = [];
8936             if(!suppressEvent){
8937                 this.fireEvent("selectionchange", this, this.selections);
8938             }
8939         }
8940     },
8941
8942     /**
8943      * Returns true if the passed node is selected
8944      * @param {HTMLElement/Number} node The node or node index
8945      * @return {Boolean}
8946      */
8947     isSelected : function(node){
8948         var s = this.selections;
8949         if(s.length < 1){
8950             return false;
8951         }
8952         node = this.getNode(node);
8953         return s.indexOf(node) !== -1;
8954     },
8955
8956     /**
8957      * Selects nodes.
8958      * @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
8959      * @param {Boolean} keepExisting (optional) true to keep existing selections
8960      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8961      */
8962     select : function(nodeInfo, keepExisting, suppressEvent){
8963         if(nodeInfo instanceof Array){
8964             if(!keepExisting){
8965                 this.clearSelections(true);
8966             }
8967             for(var i = 0, len = nodeInfo.length; i < len; i++){
8968                 this.select(nodeInfo[i], true, true);
8969             }
8970             return;
8971         } 
8972         var node = this.getNode(nodeInfo);
8973         if(!node || this.isSelected(node)){
8974             return; // already selected.
8975         }
8976         if(!keepExisting){
8977             this.clearSelections(true);
8978         }
8979         
8980         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
8981             Roo.fly(node).addClass(this.selectedClass);
8982             this.selections.push(node);
8983             if(!suppressEvent){
8984                 this.fireEvent("selectionchange", this, this.selections);
8985             }
8986         }
8987         
8988         
8989     },
8990       /**
8991      * Unselects nodes.
8992      * @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
8993      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
8994      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
8995      */
8996     unselect : function(nodeInfo, keepExisting, suppressEvent)
8997     {
8998         if(nodeInfo instanceof Array){
8999             Roo.each(this.selections, function(s) {
9000                 this.unselect(s, nodeInfo);
9001             }, this);
9002             return;
9003         }
9004         var node = this.getNode(nodeInfo);
9005         if(!node || !this.isSelected(node)){
9006             //Roo.log("not selected");
9007             return; // not selected.
9008         }
9009         // fireevent???
9010         var ns = [];
9011         Roo.each(this.selections, function(s) {
9012             if (s == node ) {
9013                 Roo.fly(node).removeClass(this.selectedClass);
9014
9015                 return;
9016             }
9017             ns.push(s);
9018         },this);
9019         
9020         this.selections= ns;
9021         this.fireEvent("selectionchange", this, this.selections);
9022     },
9023
9024     /**
9025      * Gets a template node.
9026      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9027      * @return {HTMLElement} The node or null if it wasn't found
9028      */
9029     getNode : function(nodeInfo){
9030         if(typeof nodeInfo == "string"){
9031             return document.getElementById(nodeInfo);
9032         }else if(typeof nodeInfo == "number"){
9033             return this.nodes[nodeInfo];
9034         }
9035         return nodeInfo;
9036     },
9037
9038     /**
9039      * Gets a range template nodes.
9040      * @param {Number} startIndex
9041      * @param {Number} endIndex
9042      * @return {Array} An array of nodes
9043      */
9044     getNodes : function(start, end){
9045         var ns = this.nodes;
9046         start = start || 0;
9047         end = typeof end == "undefined" ? ns.length - 1 : end;
9048         var nodes = [];
9049         if(start <= end){
9050             for(var i = start; i <= end; i++){
9051                 nodes.push(ns[i]);
9052             }
9053         } else{
9054             for(var i = start; i >= end; i--){
9055                 nodes.push(ns[i]);
9056             }
9057         }
9058         return nodes;
9059     },
9060
9061     /**
9062      * Finds the index of the passed node
9063      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9064      * @return {Number} The index of the node or -1
9065      */
9066     indexOf : function(node){
9067         node = this.getNode(node);
9068         if(typeof node.nodeIndex == "number"){
9069             return node.nodeIndex;
9070         }
9071         var ns = this.nodes;
9072         for(var i = 0, len = ns.length; i < len; i++){
9073             if(ns[i] == node){
9074                 return i;
9075             }
9076         }
9077         return -1;
9078     }
9079 });
9080 /*
9081  * Based on:
9082  * Ext JS Library 1.1.1
9083  * Copyright(c) 2006-2007, Ext JS, LLC.
9084  *
9085  * Originally Released Under LGPL - original licence link has changed is not relivant.
9086  *
9087  * Fork - LGPL
9088  * <script type="text/javascript">
9089  */
9090
9091 /**
9092  * @class Roo.JsonView
9093  * @extends Roo.View
9094  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9095 <pre><code>
9096 var view = new Roo.JsonView({
9097     container: "my-element",
9098     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9099     multiSelect: true, 
9100     jsonRoot: "data" 
9101 });
9102
9103 // listen for node click?
9104 view.on("click", function(vw, index, node, e){
9105     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9106 });
9107
9108 // direct load of JSON data
9109 view.load("foobar.php");
9110
9111 // Example from my blog list
9112 var tpl = new Roo.Template(
9113     '&lt;div class="entry"&gt;' +
9114     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9115     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9116     "&lt;/div&gt;&lt;hr /&gt;"
9117 );
9118
9119 var moreView = new Roo.JsonView({
9120     container :  "entry-list", 
9121     template : tpl,
9122     jsonRoot: "posts"
9123 });
9124 moreView.on("beforerender", this.sortEntries, this);
9125 moreView.load({
9126     url: "/blog/get-posts.php",
9127     params: "allposts=true",
9128     text: "Loading Blog Entries..."
9129 });
9130 </code></pre>
9131
9132 * Note: old code is supported with arguments : (container, template, config)
9133
9134
9135  * @constructor
9136  * Create a new JsonView
9137  * 
9138  * @param {Object} config The config object
9139  * 
9140  */
9141 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9142     
9143     
9144     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9145
9146     var um = this.el.getUpdateManager();
9147     um.setRenderer(this);
9148     um.on("update", this.onLoad, this);
9149     um.on("failure", this.onLoadException, this);
9150
9151     /**
9152      * @event beforerender
9153      * Fires before rendering of the downloaded JSON data.
9154      * @param {Roo.JsonView} this
9155      * @param {Object} data The JSON data loaded
9156      */
9157     /**
9158      * @event load
9159      * Fires when data is loaded.
9160      * @param {Roo.JsonView} this
9161      * @param {Object} data The JSON data loaded
9162      * @param {Object} response The raw Connect response object
9163      */
9164     /**
9165      * @event loadexception
9166      * Fires when loading fails.
9167      * @param {Roo.JsonView} this
9168      * @param {Object} response The raw Connect response object
9169      */
9170     this.addEvents({
9171         'beforerender' : true,
9172         'load' : true,
9173         'loadexception' : true
9174     });
9175 };
9176 Roo.extend(Roo.JsonView, Roo.View, {
9177     /**
9178      * @type {String} The root property in the loaded JSON object that contains the data
9179      */
9180     jsonRoot : "",
9181
9182     /**
9183      * Refreshes the view.
9184      */
9185     refresh : function(){
9186         this.clearSelections();
9187         this.el.update("");
9188         var html = [];
9189         var o = this.jsonData;
9190         if(o && o.length > 0){
9191             for(var i = 0, len = o.length; i < len; i++){
9192                 var data = this.prepareData(o[i], i, o);
9193                 html[html.length] = this.tpl.apply(data);
9194             }
9195         }else{
9196             html.push(this.emptyText);
9197         }
9198         this.el.update(html.join(""));
9199         this.nodes = this.el.dom.childNodes;
9200         this.updateIndexes(0);
9201     },
9202
9203     /**
9204      * 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.
9205      * @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:
9206      <pre><code>
9207      view.load({
9208          url: "your-url.php",
9209          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9210          callback: yourFunction,
9211          scope: yourObject, //(optional scope)
9212          discardUrl: false,
9213          nocache: false,
9214          text: "Loading...",
9215          timeout: 30,
9216          scripts: false
9217      });
9218      </code></pre>
9219      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9220      * 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.
9221      * @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}
9222      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9223      * @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.
9224      */
9225     load : function(){
9226         var um = this.el.getUpdateManager();
9227         um.update.apply(um, arguments);
9228     },
9229
9230     render : function(el, response){
9231         this.clearSelections();
9232         this.el.update("");
9233         var o;
9234         try{
9235             o = Roo.util.JSON.decode(response.responseText);
9236             if(this.jsonRoot){
9237                 
9238                 o = o[this.jsonRoot];
9239             }
9240         } catch(e){
9241         }
9242         /**
9243          * The current JSON data or null
9244          */
9245         this.jsonData = o;
9246         this.beforeRender();
9247         this.refresh();
9248     },
9249
9250 /**
9251  * Get the number of records in the current JSON dataset
9252  * @return {Number}
9253  */
9254     getCount : function(){
9255         return this.jsonData ? this.jsonData.length : 0;
9256     },
9257
9258 /**
9259  * Returns the JSON object for the specified node(s)
9260  * @param {HTMLElement/Array} node The node or an array of nodes
9261  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9262  * you get the JSON object for the node
9263  */
9264     getNodeData : function(node){
9265         if(node instanceof Array){
9266             var data = [];
9267             for(var i = 0, len = node.length; i < len; i++){
9268                 data.push(this.getNodeData(node[i]));
9269             }
9270             return data;
9271         }
9272         return this.jsonData[this.indexOf(node)] || null;
9273     },
9274
9275     beforeRender : function(){
9276         this.snapshot = this.jsonData;
9277         if(this.sortInfo){
9278             this.sort.apply(this, this.sortInfo);
9279         }
9280         this.fireEvent("beforerender", this, this.jsonData);
9281     },
9282
9283     onLoad : function(el, o){
9284         this.fireEvent("load", this, this.jsonData, o);
9285     },
9286
9287     onLoadException : function(el, o){
9288         this.fireEvent("loadexception", this, o);
9289     },
9290
9291 /**
9292  * Filter the data by a specific property.
9293  * @param {String} property A property on your JSON objects
9294  * @param {String/RegExp} value Either string that the property values
9295  * should start with, or a RegExp to test against the property
9296  */
9297     filter : function(property, value){
9298         if(this.jsonData){
9299             var data = [];
9300             var ss = this.snapshot;
9301             if(typeof value == "string"){
9302                 var vlen = value.length;
9303                 if(vlen == 0){
9304                     this.clearFilter();
9305                     return;
9306                 }
9307                 value = value.toLowerCase();
9308                 for(var i = 0, len = ss.length; i < len; i++){
9309                     var o = ss[i];
9310                     if(o[property].substr(0, vlen).toLowerCase() == value){
9311                         data.push(o);
9312                     }
9313                 }
9314             } else if(value.exec){ // regex?
9315                 for(var i = 0, len = ss.length; i < len; i++){
9316                     var o = ss[i];
9317                     if(value.test(o[property])){
9318                         data.push(o);
9319                     }
9320                 }
9321             } else{
9322                 return;
9323             }
9324             this.jsonData = data;
9325             this.refresh();
9326         }
9327     },
9328
9329 /**
9330  * Filter by a function. The passed function will be called with each
9331  * object in the current dataset. If the function returns true the value is kept,
9332  * otherwise it is filtered.
9333  * @param {Function} fn
9334  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9335  */
9336     filterBy : function(fn, scope){
9337         if(this.jsonData){
9338             var data = [];
9339             var ss = this.snapshot;
9340             for(var i = 0, len = ss.length; i < len; i++){
9341                 var o = ss[i];
9342                 if(fn.call(scope || this, o)){
9343                     data.push(o);
9344                 }
9345             }
9346             this.jsonData = data;
9347             this.refresh();
9348         }
9349     },
9350
9351 /**
9352  * Clears the current filter.
9353  */
9354     clearFilter : function(){
9355         if(this.snapshot && this.jsonData != this.snapshot){
9356             this.jsonData = this.snapshot;
9357             this.refresh();
9358         }
9359     },
9360
9361
9362 /**
9363  * Sorts the data for this view and refreshes it.
9364  * @param {String} property A property on your JSON objects to sort on
9365  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9366  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9367  */
9368     sort : function(property, dir, sortType){
9369         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9370         if(this.jsonData){
9371             var p = property;
9372             var dsc = dir && dir.toLowerCase() == "desc";
9373             var f = function(o1, o2){
9374                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9375                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9376                 ;
9377                 if(v1 < v2){
9378                     return dsc ? +1 : -1;
9379                 } else if(v1 > v2){
9380                     return dsc ? -1 : +1;
9381                 } else{
9382                     return 0;
9383                 }
9384             };
9385             this.jsonData.sort(f);
9386             this.refresh();
9387             if(this.jsonData != this.snapshot){
9388                 this.snapshot.sort(f);
9389             }
9390         }
9391     }
9392 });/*
9393  * Based on:
9394  * Ext JS Library 1.1.1
9395  * Copyright(c) 2006-2007, Ext JS, LLC.
9396  *
9397  * Originally Released Under LGPL - original licence link has changed is not relivant.
9398  *
9399  * Fork - LGPL
9400  * <script type="text/javascript">
9401  */
9402  
9403
9404 /**
9405  * @class Roo.ColorPalette
9406  * @extends Roo.Component
9407  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9408  * Here's an example of typical usage:
9409  * <pre><code>
9410 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9411 cp.render('my-div');
9412
9413 cp.on('select', function(palette, selColor){
9414     // do something with selColor
9415 });
9416 </code></pre>
9417  * @constructor
9418  * Create a new ColorPalette
9419  * @param {Object} config The config object
9420  */
9421 Roo.ColorPalette = function(config){
9422     Roo.ColorPalette.superclass.constructor.call(this, config);
9423     this.addEvents({
9424         /**
9425              * @event select
9426              * Fires when a color is selected
9427              * @param {ColorPalette} this
9428              * @param {String} color The 6-digit color hex code (without the # symbol)
9429              */
9430         select: true
9431     });
9432
9433     if(this.handler){
9434         this.on("select", this.handler, this.scope, true);
9435     }
9436 };
9437 Roo.extend(Roo.ColorPalette, Roo.Component, {
9438     /**
9439      * @cfg {String} itemCls
9440      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9441      */
9442     itemCls : "x-color-palette",
9443     /**
9444      * @cfg {String} value
9445      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9446      * the hex codes are case-sensitive.
9447      */
9448     value : null,
9449     clickEvent:'click',
9450     // private
9451     ctype: "Roo.ColorPalette",
9452
9453     /**
9454      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9455      */
9456     allowReselect : false,
9457
9458     /**
9459      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9460      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9461      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9462      * of colors with the width setting until the box is symmetrical.</p>
9463      * <p>You can override individual colors if needed:</p>
9464      * <pre><code>
9465 var cp = new Roo.ColorPalette();
9466 cp.colors[0] = "FF0000";  // change the first box to red
9467 </code></pre>
9468
9469 Or you can provide a custom array of your own for complete control:
9470 <pre><code>
9471 var cp = new Roo.ColorPalette();
9472 cp.colors = ["000000", "993300", "333300"];
9473 </code></pre>
9474      * @type Array
9475      */
9476     colors : [
9477         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9478         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9479         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9480         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9481         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9482     ],
9483
9484     // private
9485     onRender : function(container, position){
9486         var t = new Roo.MasterTemplate(
9487             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9488         );
9489         var c = this.colors;
9490         for(var i = 0, len = c.length; i < len; i++){
9491             t.add([c[i]]);
9492         }
9493         var el = document.createElement("div");
9494         el.className = this.itemCls;
9495         t.overwrite(el);
9496         container.dom.insertBefore(el, position);
9497         this.el = Roo.get(el);
9498         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9499         if(this.clickEvent != 'click'){
9500             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9501         }
9502     },
9503
9504     // private
9505     afterRender : function(){
9506         Roo.ColorPalette.superclass.afterRender.call(this);
9507         if(this.value){
9508             var s = this.value;
9509             this.value = null;
9510             this.select(s);
9511         }
9512     },
9513
9514     // private
9515     handleClick : function(e, t){
9516         e.preventDefault();
9517         if(!this.disabled){
9518             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9519             this.select(c.toUpperCase());
9520         }
9521     },
9522
9523     /**
9524      * Selects the specified color in the palette (fires the select event)
9525      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9526      */
9527     select : function(color){
9528         color = color.replace("#", "");
9529         if(color != this.value || this.allowReselect){
9530             var el = this.el;
9531             if(this.value){
9532                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9533             }
9534             el.child("a.color-"+color).addClass("x-color-palette-sel");
9535             this.value = color;
9536             this.fireEvent("select", this, color);
9537         }
9538     }
9539 });/*
9540  * Based on:
9541  * Ext JS Library 1.1.1
9542  * Copyright(c) 2006-2007, Ext JS, LLC.
9543  *
9544  * Originally Released Under LGPL - original licence link has changed is not relivant.
9545  *
9546  * Fork - LGPL
9547  * <script type="text/javascript">
9548  */
9549  
9550 /**
9551  * @class Roo.DatePicker
9552  * @extends Roo.Component
9553  * Simple date picker class.
9554  * @constructor
9555  * Create a new DatePicker
9556  * @param {Object} config The config object
9557  */
9558 Roo.DatePicker = function(config){
9559     Roo.DatePicker.superclass.constructor.call(this, config);
9560
9561     this.value = config && config.value ?
9562                  config.value.clearTime() : new Date().clearTime();
9563
9564     this.addEvents({
9565         /**
9566              * @event select
9567              * Fires when a date is selected
9568              * @param {DatePicker} this
9569              * @param {Date} date The selected date
9570              */
9571         'select': true,
9572         /**
9573              * @event monthchange
9574              * Fires when the displayed month changes 
9575              * @param {DatePicker} this
9576              * @param {Date} date The selected month
9577              */
9578         'monthchange': true
9579     });
9580
9581     if(this.handler){
9582         this.on("select", this.handler,  this.scope || this);
9583     }
9584     // build the disabledDatesRE
9585     if(!this.disabledDatesRE && this.disabledDates){
9586         var dd = this.disabledDates;
9587         var re = "(?:";
9588         for(var i = 0; i < dd.length; i++){
9589             re += dd[i];
9590             if(i != dd.length-1) re += "|";
9591         }
9592         this.disabledDatesRE = new RegExp(re + ")");
9593     }
9594 };
9595
9596 Roo.extend(Roo.DatePicker, Roo.Component, {
9597     /**
9598      * @cfg {String} todayText
9599      * The text to display on the button that selects the current date (defaults to "Today")
9600      */
9601     todayText : "Today",
9602     /**
9603      * @cfg {String} okText
9604      * The text to display on the ok button
9605      */
9606     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9607     /**
9608      * @cfg {String} cancelText
9609      * The text to display on the cancel button
9610      */
9611     cancelText : "Cancel",
9612     /**
9613      * @cfg {String} todayTip
9614      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9615      */
9616     todayTip : "{0} (Spacebar)",
9617     /**
9618      * @cfg {Date} minDate
9619      * Minimum allowable date (JavaScript date object, defaults to null)
9620      */
9621     minDate : null,
9622     /**
9623      * @cfg {Date} maxDate
9624      * Maximum allowable date (JavaScript date object, defaults to null)
9625      */
9626     maxDate : null,
9627     /**
9628      * @cfg {String} minText
9629      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
9630      */
9631     minText : "This date is before the minimum date",
9632     /**
9633      * @cfg {String} maxText
9634      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
9635      */
9636     maxText : "This date is after the maximum date",
9637     /**
9638      * @cfg {String} format
9639      * The default date format string which can be overriden for localization support.  The format must be
9640      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
9641      */
9642     format : "m/d/y",
9643     /**
9644      * @cfg {Array} disabledDays
9645      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
9646      */
9647     disabledDays : null,
9648     /**
9649      * @cfg {String} disabledDaysText
9650      * The tooltip to display when the date falls on a disabled day (defaults to "")
9651      */
9652     disabledDaysText : "",
9653     /**
9654      * @cfg {RegExp} disabledDatesRE
9655      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
9656      */
9657     disabledDatesRE : null,
9658     /**
9659      * @cfg {String} disabledDatesText
9660      * The tooltip text to display when the date falls on a disabled date (defaults to "")
9661      */
9662     disabledDatesText : "",
9663     /**
9664      * @cfg {Boolean} constrainToViewport
9665      * True to constrain the date picker to the viewport (defaults to true)
9666      */
9667     constrainToViewport : true,
9668     /**
9669      * @cfg {Array} monthNames
9670      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
9671      */
9672     monthNames : Date.monthNames,
9673     /**
9674      * @cfg {Array} dayNames
9675      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
9676      */
9677     dayNames : Date.dayNames,
9678     /**
9679      * @cfg {String} nextText
9680      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
9681      */
9682     nextText: 'Next Month (Control+Right)',
9683     /**
9684      * @cfg {String} prevText
9685      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
9686      */
9687     prevText: 'Previous Month (Control+Left)',
9688     /**
9689      * @cfg {String} monthYearText
9690      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
9691      */
9692     monthYearText: 'Choose a month (Control+Up/Down to move years)',
9693     /**
9694      * @cfg {Number} startDay
9695      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9696      */
9697     startDay : 0,
9698     /**
9699      * @cfg {Bool} showClear
9700      * Show a clear button (usefull for date form elements that can be blank.)
9701      */
9702     
9703     showClear: false,
9704     
9705     /**
9706      * Sets the value of the date field
9707      * @param {Date} value The date to set
9708      */
9709     setValue : function(value){
9710         var old = this.value;
9711         
9712         if (typeof(value) == 'string') {
9713          
9714             value = Date.parseDate(value, this.format);
9715         }
9716         if (!value) {
9717             value = new Date();
9718         }
9719         
9720         this.value = value.clearTime(true);
9721         if(this.el){
9722             this.update(this.value);
9723         }
9724     },
9725
9726     /**
9727      * Gets the current selected value of the date field
9728      * @return {Date} The selected date
9729      */
9730     getValue : function(){
9731         return this.value;
9732     },
9733
9734     // private
9735     focus : function(){
9736         if(this.el){
9737             this.update(this.activeDate);
9738         }
9739     },
9740
9741     // privateval
9742     onRender : function(container, position){
9743         
9744         var m = [
9745              '<table cellspacing="0">',
9746                 '<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>',
9747                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
9748         var dn = this.dayNames;
9749         for(var i = 0; i < 7; i++){
9750             var d = this.startDay+i;
9751             if(d > 6){
9752                 d = d-7;
9753             }
9754             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
9755         }
9756         m[m.length] = "</tr></thead><tbody><tr>";
9757         for(var i = 0; i < 42; i++) {
9758             if(i % 7 == 0 && i != 0){
9759                 m[m.length] = "</tr><tr>";
9760             }
9761             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
9762         }
9763         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
9764             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
9765
9766         var el = document.createElement("div");
9767         el.className = "x-date-picker";
9768         el.innerHTML = m.join("");
9769
9770         container.dom.insertBefore(el, position);
9771
9772         this.el = Roo.get(el);
9773         this.eventEl = Roo.get(el.firstChild);
9774
9775         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
9776             handler: this.showPrevMonth,
9777             scope: this,
9778             preventDefault:true,
9779             stopDefault:true
9780         });
9781
9782         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
9783             handler: this.showNextMonth,
9784             scope: this,
9785             preventDefault:true,
9786             stopDefault:true
9787         });
9788
9789         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
9790
9791         this.monthPicker = this.el.down('div.x-date-mp');
9792         this.monthPicker.enableDisplayMode('block');
9793         
9794         var kn = new Roo.KeyNav(this.eventEl, {
9795             "left" : function(e){
9796                 e.ctrlKey ?
9797                     this.showPrevMonth() :
9798                     this.update(this.activeDate.add("d", -1));
9799             },
9800
9801             "right" : function(e){
9802                 e.ctrlKey ?
9803                     this.showNextMonth() :
9804                     this.update(this.activeDate.add("d", 1));
9805             },
9806
9807             "up" : function(e){
9808                 e.ctrlKey ?
9809                     this.showNextYear() :
9810                     this.update(this.activeDate.add("d", -7));
9811             },
9812
9813             "down" : function(e){
9814                 e.ctrlKey ?
9815                     this.showPrevYear() :
9816                     this.update(this.activeDate.add("d", 7));
9817             },
9818
9819             "pageUp" : function(e){
9820                 this.showNextMonth();
9821             },
9822
9823             "pageDown" : function(e){
9824                 this.showPrevMonth();
9825             },
9826
9827             "enter" : function(e){
9828                 e.stopPropagation();
9829                 return true;
9830             },
9831
9832             scope : this
9833         });
9834
9835         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
9836
9837         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
9838
9839         this.el.unselectable();
9840         
9841         this.cells = this.el.select("table.x-date-inner tbody td");
9842         this.textNodes = this.el.query("table.x-date-inner tbody span");
9843
9844         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
9845             text: "&#160;",
9846             tooltip: this.monthYearText
9847         });
9848
9849         this.mbtn.on('click', this.showMonthPicker, this);
9850         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
9851
9852
9853         var today = (new Date()).dateFormat(this.format);
9854         
9855         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
9856         if (this.showClear) {
9857             baseTb.add( new Roo.Toolbar.Fill());
9858         }
9859         baseTb.add({
9860             text: String.format(this.todayText, today),
9861             tooltip: String.format(this.todayTip, today),
9862             handler: this.selectToday,
9863             scope: this
9864         });
9865         
9866         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
9867             
9868         //});
9869         if (this.showClear) {
9870             
9871             baseTb.add( new Roo.Toolbar.Fill());
9872             baseTb.add({
9873                 text: '&#160;',
9874                 cls: 'x-btn-icon x-btn-clear',
9875                 handler: function() {
9876                     //this.value = '';
9877                     this.fireEvent("select", this, '');
9878                 },
9879                 scope: this
9880             });
9881         }
9882         
9883         
9884         if(Roo.isIE){
9885             this.el.repaint();
9886         }
9887         this.update(this.value);
9888     },
9889
9890     createMonthPicker : function(){
9891         if(!this.monthPicker.dom.firstChild){
9892             var buf = ['<table border="0" cellspacing="0">'];
9893             for(var i = 0; i < 6; i++){
9894                 buf.push(
9895                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
9896                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
9897                     i == 0 ?
9898                     '<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>' :
9899                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
9900                 );
9901             }
9902             buf.push(
9903                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
9904                     this.okText,
9905                     '</button><button type="button" class="x-date-mp-cancel">',
9906                     this.cancelText,
9907                     '</button></td></tr>',
9908                 '</table>'
9909             );
9910             this.monthPicker.update(buf.join(''));
9911             this.monthPicker.on('click', this.onMonthClick, this);
9912             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
9913
9914             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
9915             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
9916
9917             this.mpMonths.each(function(m, a, i){
9918                 i += 1;
9919                 if((i%2) == 0){
9920                     m.dom.xmonth = 5 + Math.round(i * .5);
9921                 }else{
9922                     m.dom.xmonth = Math.round((i-1) * .5);
9923                 }
9924             });
9925         }
9926     },
9927
9928     showMonthPicker : function(){
9929         this.createMonthPicker();
9930         var size = this.el.getSize();
9931         this.monthPicker.setSize(size);
9932         this.monthPicker.child('table').setSize(size);
9933
9934         this.mpSelMonth = (this.activeDate || this.value).getMonth();
9935         this.updateMPMonth(this.mpSelMonth);
9936         this.mpSelYear = (this.activeDate || this.value).getFullYear();
9937         this.updateMPYear(this.mpSelYear);
9938
9939         this.monthPicker.slideIn('t', {duration:.2});
9940     },
9941
9942     updateMPYear : function(y){
9943         this.mpyear = y;
9944         var ys = this.mpYears.elements;
9945         for(var i = 1; i <= 10; i++){
9946             var td = ys[i-1], y2;
9947             if((i%2) == 0){
9948                 y2 = y + Math.round(i * .5);
9949                 td.firstChild.innerHTML = y2;
9950                 td.xyear = y2;
9951             }else{
9952                 y2 = y - (5-Math.round(i * .5));
9953                 td.firstChild.innerHTML = y2;
9954                 td.xyear = y2;
9955             }
9956             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
9957         }
9958     },
9959
9960     updateMPMonth : function(sm){
9961         this.mpMonths.each(function(m, a, i){
9962             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
9963         });
9964     },
9965
9966     selectMPMonth: function(m){
9967         
9968     },
9969
9970     onMonthClick : function(e, t){
9971         e.stopEvent();
9972         var el = new Roo.Element(t), pn;
9973         if(el.is('button.x-date-mp-cancel')){
9974             this.hideMonthPicker();
9975         }
9976         else if(el.is('button.x-date-mp-ok')){
9977             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
9978             this.hideMonthPicker();
9979         }
9980         else if(pn = el.up('td.x-date-mp-month', 2)){
9981             this.mpMonths.removeClass('x-date-mp-sel');
9982             pn.addClass('x-date-mp-sel');
9983             this.mpSelMonth = pn.dom.xmonth;
9984         }
9985         else if(pn = el.up('td.x-date-mp-year', 2)){
9986             this.mpYears.removeClass('x-date-mp-sel');
9987             pn.addClass('x-date-mp-sel');
9988             this.mpSelYear = pn.dom.xyear;
9989         }
9990         else if(el.is('a.x-date-mp-prev')){
9991             this.updateMPYear(this.mpyear-10);
9992         }
9993         else if(el.is('a.x-date-mp-next')){
9994             this.updateMPYear(this.mpyear+10);
9995         }
9996     },
9997
9998     onMonthDblClick : function(e, t){
9999         e.stopEvent();
10000         var el = new Roo.Element(t), pn;
10001         if(pn = el.up('td.x-date-mp-month', 2)){
10002             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10003             this.hideMonthPicker();
10004         }
10005         else if(pn = el.up('td.x-date-mp-year', 2)){
10006             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10007             this.hideMonthPicker();
10008         }
10009     },
10010
10011     hideMonthPicker : function(disableAnim){
10012         if(this.monthPicker){
10013             if(disableAnim === true){
10014                 this.monthPicker.hide();
10015             }else{
10016                 this.monthPicker.slideOut('t', {duration:.2});
10017             }
10018         }
10019     },
10020
10021     // private
10022     showPrevMonth : function(e){
10023         this.update(this.activeDate.add("mo", -1));
10024     },
10025
10026     // private
10027     showNextMonth : function(e){
10028         this.update(this.activeDate.add("mo", 1));
10029     },
10030
10031     // private
10032     showPrevYear : function(){
10033         this.update(this.activeDate.add("y", -1));
10034     },
10035
10036     // private
10037     showNextYear : function(){
10038         this.update(this.activeDate.add("y", 1));
10039     },
10040
10041     // private
10042     handleMouseWheel : function(e){
10043         var delta = e.getWheelDelta();
10044         if(delta > 0){
10045             this.showPrevMonth();
10046             e.stopEvent();
10047         } else if(delta < 0){
10048             this.showNextMonth();
10049             e.stopEvent();
10050         }
10051     },
10052
10053     // private
10054     handleDateClick : function(e, t){
10055         e.stopEvent();
10056         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10057             this.setValue(new Date(t.dateValue));
10058             this.fireEvent("select", this, this.value);
10059         }
10060     },
10061
10062     // private
10063     selectToday : function(){
10064         this.setValue(new Date().clearTime());
10065         this.fireEvent("select", this, this.value);
10066     },
10067
10068     // private
10069     update : function(date)
10070     {
10071         var vd = this.activeDate;
10072         this.activeDate = date;
10073         if(vd && this.el){
10074             var t = date.getTime();
10075             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10076                 this.cells.removeClass("x-date-selected");
10077                 this.cells.each(function(c){
10078                    if(c.dom.firstChild.dateValue == t){
10079                        c.addClass("x-date-selected");
10080                        setTimeout(function(){
10081                             try{c.dom.firstChild.focus();}catch(e){}
10082                        }, 50);
10083                        return false;
10084                    }
10085                 });
10086                 return;
10087             }
10088         }
10089         
10090         var days = date.getDaysInMonth();
10091         var firstOfMonth = date.getFirstDateOfMonth();
10092         var startingPos = firstOfMonth.getDay()-this.startDay;
10093
10094         if(startingPos <= this.startDay){
10095             startingPos += 7;
10096         }
10097
10098         var pm = date.add("mo", -1);
10099         var prevStart = pm.getDaysInMonth()-startingPos;
10100
10101         var cells = this.cells.elements;
10102         var textEls = this.textNodes;
10103         days += startingPos;
10104
10105         // convert everything to numbers so it's fast
10106         var day = 86400000;
10107         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10108         var today = new Date().clearTime().getTime();
10109         var sel = date.clearTime().getTime();
10110         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10111         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10112         var ddMatch = this.disabledDatesRE;
10113         var ddText = this.disabledDatesText;
10114         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10115         var ddaysText = this.disabledDaysText;
10116         var format = this.format;
10117
10118         var setCellClass = function(cal, cell){
10119             cell.title = "";
10120             var t = d.getTime();
10121             cell.firstChild.dateValue = t;
10122             if(t == today){
10123                 cell.className += " x-date-today";
10124                 cell.title = cal.todayText;
10125             }
10126             if(t == sel){
10127                 cell.className += " x-date-selected";
10128                 setTimeout(function(){
10129                     try{cell.firstChild.focus();}catch(e){}
10130                 }, 50);
10131             }
10132             // disabling
10133             if(t < min) {
10134                 cell.className = " x-date-disabled";
10135                 cell.title = cal.minText;
10136                 return;
10137             }
10138             if(t > max) {
10139                 cell.className = " x-date-disabled";
10140                 cell.title = cal.maxText;
10141                 return;
10142             }
10143             if(ddays){
10144                 if(ddays.indexOf(d.getDay()) != -1){
10145                     cell.title = ddaysText;
10146                     cell.className = " x-date-disabled";
10147                 }
10148             }
10149             if(ddMatch && format){
10150                 var fvalue = d.dateFormat(format);
10151                 if(ddMatch.test(fvalue)){
10152                     cell.title = ddText.replace("%0", fvalue);
10153                     cell.className = " x-date-disabled";
10154                 }
10155             }
10156         };
10157
10158         var i = 0;
10159         for(; i < startingPos; i++) {
10160             textEls[i].innerHTML = (++prevStart);
10161             d.setDate(d.getDate()+1);
10162             cells[i].className = "x-date-prevday";
10163             setCellClass(this, cells[i]);
10164         }
10165         for(; i < days; i++){
10166             intDay = i - startingPos + 1;
10167             textEls[i].innerHTML = (intDay);
10168             d.setDate(d.getDate()+1);
10169             cells[i].className = "x-date-active";
10170             setCellClass(this, cells[i]);
10171         }
10172         var extraDays = 0;
10173         for(; i < 42; i++) {
10174              textEls[i].innerHTML = (++extraDays);
10175              d.setDate(d.getDate()+1);
10176              cells[i].className = "x-date-nextday";
10177              setCellClass(this, cells[i]);
10178         }
10179
10180         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10181         this.fireEvent('monthchange', this, date);
10182         
10183         if(!this.internalRender){
10184             var main = this.el.dom.firstChild;
10185             var w = main.offsetWidth;
10186             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10187             Roo.fly(main).setWidth(w);
10188             this.internalRender = true;
10189             // opera does not respect the auto grow header center column
10190             // then, after it gets a width opera refuses to recalculate
10191             // without a second pass
10192             if(Roo.isOpera && !this.secondPass){
10193                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10194                 this.secondPass = true;
10195                 this.update.defer(10, this, [date]);
10196             }
10197         }
10198         
10199         
10200     }
10201 });        /*
10202  * Based on:
10203  * Ext JS Library 1.1.1
10204  * Copyright(c) 2006-2007, Ext JS, LLC.
10205  *
10206  * Originally Released Under LGPL - original licence link has changed is not relivant.
10207  *
10208  * Fork - LGPL
10209  * <script type="text/javascript">
10210  */
10211 /**
10212  * @class Roo.TabPanel
10213  * @extends Roo.util.Observable
10214  * A lightweight tab container.
10215  * <br><br>
10216  * Usage:
10217  * <pre><code>
10218 // basic tabs 1, built from existing content
10219 var tabs = new Roo.TabPanel("tabs1");
10220 tabs.addTab("script", "View Script");
10221 tabs.addTab("markup", "View Markup");
10222 tabs.activate("script");
10223
10224 // more advanced tabs, built from javascript
10225 var jtabs = new Roo.TabPanel("jtabs");
10226 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10227
10228 // set up the UpdateManager
10229 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10230 var updater = tab2.getUpdateManager();
10231 updater.setDefaultUrl("ajax1.htm");
10232 tab2.on('activate', updater.refresh, updater, true);
10233
10234 // Use setUrl for Ajax loading
10235 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10236 tab3.setUrl("ajax2.htm", null, true);
10237
10238 // Disabled tab
10239 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10240 tab4.disable();
10241
10242 jtabs.activate("jtabs-1");
10243  * </code></pre>
10244  * @constructor
10245  * Create a new TabPanel.
10246  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10247  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10248  */
10249 Roo.TabPanel = function(container, config){
10250     /**
10251     * The container element for this TabPanel.
10252     * @type Roo.Element
10253     */
10254     this.el = Roo.get(container, true);
10255     if(config){
10256         if(typeof config == "boolean"){
10257             this.tabPosition = config ? "bottom" : "top";
10258         }else{
10259             Roo.apply(this, config);
10260         }
10261     }
10262     if(this.tabPosition == "bottom"){
10263         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10264         this.el.addClass("x-tabs-bottom");
10265     }
10266     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10267     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10268     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10269     if(Roo.isIE){
10270         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10271     }
10272     if(this.tabPosition != "bottom"){
10273         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10274          * @type Roo.Element
10275          */
10276         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10277         this.el.addClass("x-tabs-top");
10278     }
10279     this.items = [];
10280
10281     this.bodyEl.setStyle("position", "relative");
10282
10283     this.active = null;
10284     this.activateDelegate = this.activate.createDelegate(this);
10285
10286     this.addEvents({
10287         /**
10288          * @event tabchange
10289          * Fires when the active tab changes
10290          * @param {Roo.TabPanel} this
10291          * @param {Roo.TabPanelItem} activePanel The new active tab
10292          */
10293         "tabchange": true,
10294         /**
10295          * @event beforetabchange
10296          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10297          * @param {Roo.TabPanel} this
10298          * @param {Object} e Set cancel to true on this object to cancel the tab change
10299          * @param {Roo.TabPanelItem} tab The tab being changed to
10300          */
10301         "beforetabchange" : true
10302     });
10303
10304     Roo.EventManager.onWindowResize(this.onResize, this);
10305     this.cpad = this.el.getPadding("lr");
10306     this.hiddenCount = 0;
10307
10308
10309     // toolbar on the tabbar support...
10310     if (this.toolbar) {
10311         var tcfg = this.toolbar;
10312         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10313         this.toolbar = new Roo.Toolbar(tcfg);
10314         if (Roo.isSafari) {
10315             var tbl = tcfg.container.child('table', true);
10316             tbl.setAttribute('width', '100%');
10317         }
10318         
10319     }
10320    
10321
10322
10323     Roo.TabPanel.superclass.constructor.call(this);
10324 };
10325
10326 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10327     /*
10328      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10329      */
10330     tabPosition : "top",
10331     /*
10332      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10333      */
10334     currentTabWidth : 0,
10335     /*
10336      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10337      */
10338     minTabWidth : 40,
10339     /*
10340      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10341      */
10342     maxTabWidth : 250,
10343     /*
10344      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10345      */
10346     preferredTabWidth : 175,
10347     /*
10348      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10349      */
10350     resizeTabs : false,
10351     /*
10352      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10353      */
10354     monitorResize : true,
10355     /*
10356      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10357      */
10358     toolbar : false,
10359
10360     /**
10361      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10362      * @param {String} id The id of the div to use <b>or create</b>
10363      * @param {String} text The text for the tab
10364      * @param {String} content (optional) Content to put in the TabPanelItem body
10365      * @param {Boolean} closable (optional) True to create a close icon on the tab
10366      * @return {Roo.TabPanelItem} The created TabPanelItem
10367      */
10368     addTab : function(id, text, content, closable){
10369         var item = new Roo.TabPanelItem(this, id, text, closable);
10370         this.addTabItem(item);
10371         if(content){
10372             item.setContent(content);
10373         }
10374         return item;
10375     },
10376
10377     /**
10378      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10379      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10380      * @return {Roo.TabPanelItem}
10381      */
10382     getTab : function(id){
10383         return this.items[id];
10384     },
10385
10386     /**
10387      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10388      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10389      */
10390     hideTab : function(id){
10391         var t = this.items[id];
10392         if(!t.isHidden()){
10393            t.setHidden(true);
10394            this.hiddenCount++;
10395            this.autoSizeTabs();
10396         }
10397     },
10398
10399     /**
10400      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10401      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10402      */
10403     unhideTab : function(id){
10404         var t = this.items[id];
10405         if(t.isHidden()){
10406            t.setHidden(false);
10407            this.hiddenCount--;
10408            this.autoSizeTabs();
10409         }
10410     },
10411
10412     /**
10413      * Adds an existing {@link Roo.TabPanelItem}.
10414      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10415      */
10416     addTabItem : function(item){
10417         this.items[item.id] = item;
10418         this.items.push(item);
10419         if(this.resizeTabs){
10420            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10421            this.autoSizeTabs();
10422         }else{
10423             item.autoSize();
10424         }
10425     },
10426
10427     /**
10428      * Removes a {@link Roo.TabPanelItem}.
10429      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10430      */
10431     removeTab : function(id){
10432         var items = this.items;
10433         var tab = items[id];
10434         if(!tab) { return; }
10435         var index = items.indexOf(tab);
10436         if(this.active == tab && items.length > 1){
10437             var newTab = this.getNextAvailable(index);
10438             if(newTab) {
10439                 newTab.activate();
10440             }
10441         }
10442         this.stripEl.dom.removeChild(tab.pnode.dom);
10443         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10444             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10445         }
10446         items.splice(index, 1);
10447         delete this.items[tab.id];
10448         tab.fireEvent("close", tab);
10449         tab.purgeListeners();
10450         this.autoSizeTabs();
10451     },
10452
10453     getNextAvailable : function(start){
10454         var items = this.items;
10455         var index = start;
10456         // look for a next tab that will slide over to
10457         // replace the one being removed
10458         while(index < items.length){
10459             var item = items[++index];
10460             if(item && !item.isHidden()){
10461                 return item;
10462             }
10463         }
10464         // if one isn't found select the previous tab (on the left)
10465         index = start;
10466         while(index >= 0){
10467             var item = items[--index];
10468             if(item && !item.isHidden()){
10469                 return item;
10470             }
10471         }
10472         return null;
10473     },
10474
10475     /**
10476      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10477      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10478      */
10479     disableTab : function(id){
10480         var tab = this.items[id];
10481         if(tab && this.active != tab){
10482             tab.disable();
10483         }
10484     },
10485
10486     /**
10487      * Enables a {@link Roo.TabPanelItem} that is disabled.
10488      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10489      */
10490     enableTab : function(id){
10491         var tab = this.items[id];
10492         tab.enable();
10493     },
10494
10495     /**
10496      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10497      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10498      * @return {Roo.TabPanelItem} The TabPanelItem.
10499      */
10500     activate : function(id){
10501         var tab = this.items[id];
10502         if(!tab){
10503             return null;
10504         }
10505         if(tab == this.active || tab.disabled){
10506             return tab;
10507         }
10508         var e = {};
10509         this.fireEvent("beforetabchange", this, e, tab);
10510         if(e.cancel !== true && !tab.disabled){
10511             if(this.active){
10512                 this.active.hide();
10513             }
10514             this.active = this.items[id];
10515             this.active.show();
10516             this.fireEvent("tabchange", this, this.active);
10517         }
10518         return tab;
10519     },
10520
10521     /**
10522      * Gets the active {@link Roo.TabPanelItem}.
10523      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10524      */
10525     getActiveTab : function(){
10526         return this.active;
10527     },
10528
10529     /**
10530      * Updates the tab body element to fit the height of the container element
10531      * for overflow scrolling
10532      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10533      */
10534     syncHeight : function(targetHeight){
10535         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10536         var bm = this.bodyEl.getMargins();
10537         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10538         this.bodyEl.setHeight(newHeight);
10539         return newHeight;
10540     },
10541
10542     onResize : function(){
10543         if(this.monitorResize){
10544             this.autoSizeTabs();
10545         }
10546     },
10547
10548     /**
10549      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10550      */
10551     beginUpdate : function(){
10552         this.updating = true;
10553     },
10554
10555     /**
10556      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10557      */
10558     endUpdate : function(){
10559         this.updating = false;
10560         this.autoSizeTabs();
10561     },
10562
10563     /**
10564      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10565      */
10566     autoSizeTabs : function(){
10567         var count = this.items.length;
10568         var vcount = count - this.hiddenCount;
10569         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10570         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10571         var availWidth = Math.floor(w / vcount);
10572         var b = this.stripBody;
10573         if(b.getWidth() > w){
10574             var tabs = this.items;
10575             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10576             if(availWidth < this.minTabWidth){
10577                 /*if(!this.sleft){    // incomplete scrolling code
10578                     this.createScrollButtons();
10579                 }
10580                 this.showScroll();
10581                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10582             }
10583         }else{
10584             if(this.currentTabWidth < this.preferredTabWidth){
10585                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10586             }
10587         }
10588     },
10589
10590     /**
10591      * Returns the number of tabs in this TabPanel.
10592      * @return {Number}
10593      */
10594      getCount : function(){
10595          return this.items.length;
10596      },
10597
10598     /**
10599      * Resizes all the tabs to the passed width
10600      * @param {Number} The new width
10601      */
10602     setTabWidth : function(width){
10603         this.currentTabWidth = width;
10604         for(var i = 0, len = this.items.length; i < len; i++) {
10605                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10606         }
10607     },
10608
10609     /**
10610      * Destroys this TabPanel
10611      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10612      */
10613     destroy : function(removeEl){
10614         Roo.EventManager.removeResizeListener(this.onResize, this);
10615         for(var i = 0, len = this.items.length; i < len; i++){
10616             this.items[i].purgeListeners();
10617         }
10618         if(removeEl === true){
10619             this.el.update("");
10620             this.el.remove();
10621         }
10622     }
10623 });
10624
10625 /**
10626  * @class Roo.TabPanelItem
10627  * @extends Roo.util.Observable
10628  * Represents an individual item (tab plus body) in a TabPanel.
10629  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10630  * @param {String} id The id of this TabPanelItem
10631  * @param {String} text The text for the tab of this TabPanelItem
10632  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10633  */
10634 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10635     /**
10636      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10637      * @type Roo.TabPanel
10638      */
10639     this.tabPanel = tabPanel;
10640     /**
10641      * The id for this TabPanelItem
10642      * @type String
10643      */
10644     this.id = id;
10645     /** @private */
10646     this.disabled = false;
10647     /** @private */
10648     this.text = text;
10649     /** @private */
10650     this.loaded = false;
10651     this.closable = closable;
10652
10653     /**
10654      * The body element for this TabPanelItem.
10655      * @type Roo.Element
10656      */
10657     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
10658     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
10659     this.bodyEl.setStyle("display", "block");
10660     this.bodyEl.setStyle("zoom", "1");
10661     this.hideAction();
10662
10663     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
10664     /** @private */
10665     this.el = Roo.get(els.el, true);
10666     this.inner = Roo.get(els.inner, true);
10667     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
10668     this.pnode = Roo.get(els.el.parentNode, true);
10669     this.el.on("mousedown", this.onTabMouseDown, this);
10670     this.el.on("click", this.onTabClick, this);
10671     /** @private */
10672     if(closable){
10673         var c = Roo.get(els.close, true);
10674         c.dom.title = this.closeText;
10675         c.addClassOnOver("close-over");
10676         c.on("click", this.closeClick, this);
10677      }
10678
10679     this.addEvents({
10680          /**
10681          * @event activate
10682          * Fires when this tab becomes the active tab.
10683          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10684          * @param {Roo.TabPanelItem} this
10685          */
10686         "activate": true,
10687         /**
10688          * @event beforeclose
10689          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
10690          * @param {Roo.TabPanelItem} this
10691          * @param {Object} e Set cancel to true on this object to cancel the close.
10692          */
10693         "beforeclose": true,
10694         /**
10695          * @event close
10696          * Fires when this tab is closed.
10697          * @param {Roo.TabPanelItem} this
10698          */
10699          "close": true,
10700         /**
10701          * @event deactivate
10702          * Fires when this tab is no longer the active tab.
10703          * @param {Roo.TabPanel} tabPanel The parent TabPanel
10704          * @param {Roo.TabPanelItem} this
10705          */
10706          "deactivate" : true
10707     });
10708     this.hidden = false;
10709
10710     Roo.TabPanelItem.superclass.constructor.call(this);
10711 };
10712
10713 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
10714     purgeListeners : function(){
10715        Roo.util.Observable.prototype.purgeListeners.call(this);
10716        this.el.removeAllListeners();
10717     },
10718     /**
10719      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
10720      */
10721     show : function(){
10722         this.pnode.addClass("on");
10723         this.showAction();
10724         if(Roo.isOpera){
10725             this.tabPanel.stripWrap.repaint();
10726         }
10727         this.fireEvent("activate", this.tabPanel, this);
10728     },
10729
10730     /**
10731      * Returns true if this tab is the active tab.
10732      * @return {Boolean}
10733      */
10734     isActive : function(){
10735         return this.tabPanel.getActiveTab() == this;
10736     },
10737
10738     /**
10739      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
10740      */
10741     hide : function(){
10742         this.pnode.removeClass("on");
10743         this.hideAction();
10744         this.fireEvent("deactivate", this.tabPanel, this);
10745     },
10746
10747     hideAction : function(){
10748         this.bodyEl.hide();
10749         this.bodyEl.setStyle("position", "absolute");
10750         this.bodyEl.setLeft("-20000px");
10751         this.bodyEl.setTop("-20000px");
10752     },
10753
10754     showAction : function(){
10755         this.bodyEl.setStyle("position", "relative");
10756         this.bodyEl.setTop("");
10757         this.bodyEl.setLeft("");
10758         this.bodyEl.show();
10759     },
10760
10761     /**
10762      * Set the tooltip for the tab.
10763      * @param {String} tooltip The tab's tooltip
10764      */
10765     setTooltip : function(text){
10766         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
10767             this.textEl.dom.qtip = text;
10768             this.textEl.dom.removeAttribute('title');
10769         }else{
10770             this.textEl.dom.title = text;
10771         }
10772     },
10773
10774     onTabClick : function(e){
10775         e.preventDefault();
10776         this.tabPanel.activate(this.id);
10777     },
10778
10779     onTabMouseDown : function(e){
10780         e.preventDefault();
10781         this.tabPanel.activate(this.id);
10782     },
10783
10784     getWidth : function(){
10785         return this.inner.getWidth();
10786     },
10787
10788     setWidth : function(width){
10789         var iwidth = width - this.pnode.getPadding("lr");
10790         this.inner.setWidth(iwidth);
10791         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
10792         this.pnode.setWidth(width);
10793     },
10794
10795     /**
10796      * Show or hide the tab
10797      * @param {Boolean} hidden True to hide or false to show.
10798      */
10799     setHidden : function(hidden){
10800         this.hidden = hidden;
10801         this.pnode.setStyle("display", hidden ? "none" : "");
10802     },
10803
10804     /**
10805      * Returns true if this tab is "hidden"
10806      * @return {Boolean}
10807      */
10808     isHidden : function(){
10809         return this.hidden;
10810     },
10811
10812     /**
10813      * Returns the text for this tab
10814      * @return {String}
10815      */
10816     getText : function(){
10817         return this.text;
10818     },
10819
10820     autoSize : function(){
10821         //this.el.beginMeasure();
10822         this.textEl.setWidth(1);
10823         /*
10824          *  #2804 [new] Tabs in Roojs
10825          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
10826          */
10827         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
10828         //this.el.endMeasure();
10829     },
10830
10831     /**
10832      * Sets the text for the tab (Note: this also sets the tooltip text)
10833      * @param {String} text The tab's text and tooltip
10834      */
10835     setText : function(text){
10836         this.text = text;
10837         this.textEl.update(text);
10838         this.setTooltip(text);
10839         if(!this.tabPanel.resizeTabs){
10840             this.autoSize();
10841         }
10842     },
10843     /**
10844      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
10845      */
10846     activate : function(){
10847         this.tabPanel.activate(this.id);
10848     },
10849
10850     /**
10851      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
10852      */
10853     disable : function(){
10854         if(this.tabPanel.active != this){
10855             this.disabled = true;
10856             this.pnode.addClass("disabled");
10857         }
10858     },
10859
10860     /**
10861      * Enables this TabPanelItem if it was previously disabled.
10862      */
10863     enable : function(){
10864         this.disabled = false;
10865         this.pnode.removeClass("disabled");
10866     },
10867
10868     /**
10869      * Sets the content for this TabPanelItem.
10870      * @param {String} content The content
10871      * @param {Boolean} loadScripts true to look for and load scripts
10872      */
10873     setContent : function(content, loadScripts){
10874         this.bodyEl.update(content, loadScripts);
10875     },
10876
10877     /**
10878      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
10879      * @return {Roo.UpdateManager} The UpdateManager
10880      */
10881     getUpdateManager : function(){
10882         return this.bodyEl.getUpdateManager();
10883     },
10884
10885     /**
10886      * Set a URL to be used to load the content for this TabPanelItem.
10887      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
10888      * @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)
10889      * @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)
10890      * @return {Roo.UpdateManager} The UpdateManager
10891      */
10892     setUrl : function(url, params, loadOnce){
10893         if(this.refreshDelegate){
10894             this.un('activate', this.refreshDelegate);
10895         }
10896         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
10897         this.on("activate", this.refreshDelegate);
10898         return this.bodyEl.getUpdateManager();
10899     },
10900
10901     /** @private */
10902     _handleRefresh : function(url, params, loadOnce){
10903         if(!loadOnce || !this.loaded){
10904             var updater = this.bodyEl.getUpdateManager();
10905             updater.update(url, params, this._setLoaded.createDelegate(this));
10906         }
10907     },
10908
10909     /**
10910      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
10911      *   Will fail silently if the setUrl method has not been called.
10912      *   This does not activate the panel, just updates its content.
10913      */
10914     refresh : function(){
10915         if(this.refreshDelegate){
10916            this.loaded = false;
10917            this.refreshDelegate();
10918         }
10919     },
10920
10921     /** @private */
10922     _setLoaded : function(){
10923         this.loaded = true;
10924     },
10925
10926     /** @private */
10927     closeClick : function(e){
10928         var o = {};
10929         e.stopEvent();
10930         this.fireEvent("beforeclose", this, o);
10931         if(o.cancel !== true){
10932             this.tabPanel.removeTab(this.id);
10933         }
10934     },
10935     /**
10936      * The text displayed in the tooltip for the close icon.
10937      * @type String
10938      */
10939     closeText : "Close this tab"
10940 });
10941
10942 /** @private */
10943 Roo.TabPanel.prototype.createStrip = function(container){
10944     var strip = document.createElement("div");
10945     strip.className = "x-tabs-wrap";
10946     container.appendChild(strip);
10947     return strip;
10948 };
10949 /** @private */
10950 Roo.TabPanel.prototype.createStripList = function(strip){
10951     // div wrapper for retard IE
10952     // returns the "tr" element.
10953     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
10954         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
10955         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
10956     return strip.firstChild.firstChild.firstChild.firstChild;
10957 };
10958 /** @private */
10959 Roo.TabPanel.prototype.createBody = function(container){
10960     var body = document.createElement("div");
10961     Roo.id(body, "tab-body");
10962     Roo.fly(body).addClass("x-tabs-body");
10963     container.appendChild(body);
10964     return body;
10965 };
10966 /** @private */
10967 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
10968     var body = Roo.getDom(id);
10969     if(!body){
10970         body = document.createElement("div");
10971         body.id = id;
10972     }
10973     Roo.fly(body).addClass("x-tabs-item-body");
10974     bodyEl.insertBefore(body, bodyEl.firstChild);
10975     return body;
10976 };
10977 /** @private */
10978 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
10979     var td = document.createElement("td");
10980     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
10981     //stripEl.appendChild(td);
10982     if(closable){
10983         td.className = "x-tabs-closable";
10984         if(!this.closeTpl){
10985             this.closeTpl = new Roo.Template(
10986                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10987                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
10988                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
10989             );
10990         }
10991         var el = this.closeTpl.overwrite(td, {"text": text});
10992         var close = el.getElementsByTagName("div")[0];
10993         var inner = el.getElementsByTagName("em")[0];
10994         return {"el": el, "close": close, "inner": inner};
10995     } else {
10996         if(!this.tabTpl){
10997             this.tabTpl = new Roo.Template(
10998                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
10999                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11000             );
11001         }
11002         var el = this.tabTpl.overwrite(td, {"text": text});
11003         var inner = el.getElementsByTagName("em")[0];
11004         return {"el": el, "inner": inner};
11005     }
11006 };/*
11007  * Based on:
11008  * Ext JS Library 1.1.1
11009  * Copyright(c) 2006-2007, Ext JS, LLC.
11010  *
11011  * Originally Released Under LGPL - original licence link has changed is not relivant.
11012  *
11013  * Fork - LGPL
11014  * <script type="text/javascript">
11015  */
11016
11017 /**
11018  * @class Roo.Button
11019  * @extends Roo.util.Observable
11020  * Simple Button class
11021  * @cfg {String} text The button text
11022  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11023  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11024  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11025  * @cfg {Object} scope The scope of the handler
11026  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11027  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11028  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11029  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11030  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11031  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11032    applies if enableToggle = true)
11033  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11034  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11035   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11036  * @constructor
11037  * Create a new button
11038  * @param {Object} config The config object
11039  */
11040 Roo.Button = function(renderTo, config)
11041 {
11042     if (!config) {
11043         config = renderTo;
11044         renderTo = config.renderTo || false;
11045     }
11046     
11047     Roo.apply(this, config);
11048     this.addEvents({
11049         /**
11050              * @event click
11051              * Fires when this button is clicked
11052              * @param {Button} this
11053              * @param {EventObject} e The click event
11054              */
11055             "click" : true,
11056         /**
11057              * @event toggle
11058              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11059              * @param {Button} this
11060              * @param {Boolean} pressed
11061              */
11062             "toggle" : true,
11063         /**
11064              * @event mouseover
11065              * Fires when the mouse hovers over the button
11066              * @param {Button} this
11067              * @param {Event} e The event object
11068              */
11069         'mouseover' : true,
11070         /**
11071              * @event mouseout
11072              * Fires when the mouse exits the button
11073              * @param {Button} this
11074              * @param {Event} e The event object
11075              */
11076         'mouseout': true,
11077          /**
11078              * @event render
11079              * Fires when the button is rendered
11080              * @param {Button} this
11081              */
11082         'render': true
11083     });
11084     if(this.menu){
11085         this.menu = Roo.menu.MenuMgr.get(this.menu);
11086     }
11087     // register listeners first!!  - so render can be captured..
11088     Roo.util.Observable.call(this);
11089     if(renderTo){
11090         this.render(renderTo);
11091     }
11092     
11093   
11094 };
11095
11096 Roo.extend(Roo.Button, Roo.util.Observable, {
11097     /**
11098      * 
11099      */
11100     
11101     /**
11102      * Read-only. True if this button is hidden
11103      * @type Boolean
11104      */
11105     hidden : false,
11106     /**
11107      * Read-only. True if this button is disabled
11108      * @type Boolean
11109      */
11110     disabled : false,
11111     /**
11112      * Read-only. True if this button is pressed (only if enableToggle = true)
11113      * @type Boolean
11114      */
11115     pressed : false,
11116
11117     /**
11118      * @cfg {Number} tabIndex 
11119      * The DOM tabIndex for this button (defaults to undefined)
11120      */
11121     tabIndex : undefined,
11122
11123     /**
11124      * @cfg {Boolean} enableToggle
11125      * True to enable pressed/not pressed toggling (defaults to false)
11126      */
11127     enableToggle: false,
11128     /**
11129      * @cfg {Mixed} menu
11130      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11131      */
11132     menu : undefined,
11133     /**
11134      * @cfg {String} menuAlign
11135      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11136      */
11137     menuAlign : "tl-bl?",
11138
11139     /**
11140      * @cfg {String} iconCls
11141      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11142      */
11143     iconCls : undefined,
11144     /**
11145      * @cfg {String} type
11146      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11147      */
11148     type : 'button',
11149
11150     // private
11151     menuClassTarget: 'tr',
11152
11153     /**
11154      * @cfg {String} clickEvent
11155      * The type of event to map to the button's event handler (defaults to 'click')
11156      */
11157     clickEvent : 'click',
11158
11159     /**
11160      * @cfg {Boolean} handleMouseEvents
11161      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11162      */
11163     handleMouseEvents : true,
11164
11165     /**
11166      * @cfg {String} tooltipType
11167      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11168      */
11169     tooltipType : 'qtip',
11170
11171     /**
11172      * @cfg {String} cls
11173      * A CSS class to apply to the button's main element.
11174      */
11175     
11176     /**
11177      * @cfg {Roo.Template} template (Optional)
11178      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11179      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11180      * require code modifications if required elements (e.g. a button) aren't present.
11181      */
11182
11183     // private
11184     render : function(renderTo){
11185         var btn;
11186         if(this.hideParent){
11187             this.parentEl = Roo.get(renderTo);
11188         }
11189         if(!this.dhconfig){
11190             if(!this.template){
11191                 if(!Roo.Button.buttonTemplate){
11192                     // hideous table template
11193                     Roo.Button.buttonTemplate = new Roo.Template(
11194                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11195                         '<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>',
11196                         "</tr></tbody></table>");
11197                 }
11198                 this.template = Roo.Button.buttonTemplate;
11199             }
11200             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11201             var btnEl = btn.child("button:first");
11202             btnEl.on('focus', this.onFocus, this);
11203             btnEl.on('blur', this.onBlur, this);
11204             if(this.cls){
11205                 btn.addClass(this.cls);
11206             }
11207             if(this.icon){
11208                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11209             }
11210             if(this.iconCls){
11211                 btnEl.addClass(this.iconCls);
11212                 if(!this.cls){
11213                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11214                 }
11215             }
11216             if(this.tabIndex !== undefined){
11217                 btnEl.dom.tabIndex = this.tabIndex;
11218             }
11219             if(this.tooltip){
11220                 if(typeof this.tooltip == 'object'){
11221                     Roo.QuickTips.tips(Roo.apply({
11222                           target: btnEl.id
11223                     }, this.tooltip));
11224                 } else {
11225                     btnEl.dom[this.tooltipType] = this.tooltip;
11226                 }
11227             }
11228         }else{
11229             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11230         }
11231         this.el = btn;
11232         if(this.id){
11233             this.el.dom.id = this.el.id = this.id;
11234         }
11235         if(this.menu){
11236             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11237             this.menu.on("show", this.onMenuShow, this);
11238             this.menu.on("hide", this.onMenuHide, this);
11239         }
11240         btn.addClass("x-btn");
11241         if(Roo.isIE && !Roo.isIE7){
11242             this.autoWidth.defer(1, this);
11243         }else{
11244             this.autoWidth();
11245         }
11246         if(this.handleMouseEvents){
11247             btn.on("mouseover", this.onMouseOver, this);
11248             btn.on("mouseout", this.onMouseOut, this);
11249             btn.on("mousedown", this.onMouseDown, this);
11250         }
11251         btn.on(this.clickEvent, this.onClick, this);
11252         //btn.on("mouseup", this.onMouseUp, this);
11253         if(this.hidden){
11254             this.hide();
11255         }
11256         if(this.disabled){
11257             this.disable();
11258         }
11259         Roo.ButtonToggleMgr.register(this);
11260         if(this.pressed){
11261             this.el.addClass("x-btn-pressed");
11262         }
11263         if(this.repeat){
11264             var repeater = new Roo.util.ClickRepeater(btn,
11265                 typeof this.repeat == "object" ? this.repeat : {}
11266             );
11267             repeater.on("click", this.onClick,  this);
11268         }
11269         
11270         this.fireEvent('render', this);
11271         
11272     },
11273     /**
11274      * Returns the button's underlying element
11275      * @return {Roo.Element} The element
11276      */
11277     getEl : function(){
11278         return this.el;  
11279     },
11280     
11281     /**
11282      * Destroys this Button and removes any listeners.
11283      */
11284     destroy : function(){
11285         Roo.ButtonToggleMgr.unregister(this);
11286         this.el.removeAllListeners();
11287         this.purgeListeners();
11288         this.el.remove();
11289     },
11290
11291     // private
11292     autoWidth : function(){
11293         if(this.el){
11294             this.el.setWidth("auto");
11295             if(Roo.isIE7 && Roo.isStrict){
11296                 var ib = this.el.child('button');
11297                 if(ib && ib.getWidth() > 20){
11298                     ib.clip();
11299                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11300                 }
11301             }
11302             if(this.minWidth){
11303                 if(this.hidden){
11304                     this.el.beginMeasure();
11305                 }
11306                 if(this.el.getWidth() < this.minWidth){
11307                     this.el.setWidth(this.minWidth);
11308                 }
11309                 if(this.hidden){
11310                     this.el.endMeasure();
11311                 }
11312             }
11313         }
11314     },
11315
11316     /**
11317      * Assigns this button's click handler
11318      * @param {Function} handler The function to call when the button is clicked
11319      * @param {Object} scope (optional) Scope for the function passed in
11320      */
11321     setHandler : function(handler, scope){
11322         this.handler = handler;
11323         this.scope = scope;  
11324     },
11325     
11326     /**
11327      * Sets this button's text
11328      * @param {String} text The button text
11329      */
11330     setText : function(text){
11331         this.text = text;
11332         if(this.el){
11333             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11334         }
11335         this.autoWidth();
11336     },
11337     
11338     /**
11339      * Gets the text for this button
11340      * @return {String} The button text
11341      */
11342     getText : function(){
11343         return this.text;  
11344     },
11345     
11346     /**
11347      * Show this button
11348      */
11349     show: function(){
11350         this.hidden = false;
11351         if(this.el){
11352             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11353         }
11354     },
11355     
11356     /**
11357      * Hide this button
11358      */
11359     hide: function(){
11360         this.hidden = true;
11361         if(this.el){
11362             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11363         }
11364     },
11365     
11366     /**
11367      * Convenience function for boolean show/hide
11368      * @param {Boolean} visible True to show, false to hide
11369      */
11370     setVisible: function(visible){
11371         if(visible) {
11372             this.show();
11373         }else{
11374             this.hide();
11375         }
11376     },
11377     
11378     /**
11379      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11380      * @param {Boolean} state (optional) Force a particular state
11381      */
11382     toggle : function(state){
11383         state = state === undefined ? !this.pressed : state;
11384         if(state != this.pressed){
11385             if(state){
11386                 this.el.addClass("x-btn-pressed");
11387                 this.pressed = true;
11388                 this.fireEvent("toggle", this, true);
11389             }else{
11390                 this.el.removeClass("x-btn-pressed");
11391                 this.pressed = false;
11392                 this.fireEvent("toggle", this, false);
11393             }
11394             if(this.toggleHandler){
11395                 this.toggleHandler.call(this.scope || this, this, state);
11396             }
11397         }
11398     },
11399     
11400     /**
11401      * Focus the button
11402      */
11403     focus : function(){
11404         this.el.child('button:first').focus();
11405     },
11406     
11407     /**
11408      * Disable this button
11409      */
11410     disable : function(){
11411         if(this.el){
11412             this.el.addClass("x-btn-disabled");
11413         }
11414         this.disabled = true;
11415     },
11416     
11417     /**
11418      * Enable this button
11419      */
11420     enable : function(){
11421         if(this.el){
11422             this.el.removeClass("x-btn-disabled");
11423         }
11424         this.disabled = false;
11425     },
11426
11427     /**
11428      * Convenience function for boolean enable/disable
11429      * @param {Boolean} enabled True to enable, false to disable
11430      */
11431     setDisabled : function(v){
11432         this[v !== true ? "enable" : "disable"]();
11433     },
11434
11435     // private
11436     onClick : function(e)
11437     {
11438         if(e){
11439             e.preventDefault();
11440         }
11441         if(e.button != 0){
11442             return;
11443         }
11444         if(!this.disabled){
11445             if(this.enableToggle){
11446                 this.toggle();
11447             }
11448             if(this.menu && !this.menu.isVisible()){
11449                 this.menu.show(this.el, this.menuAlign);
11450             }
11451             this.fireEvent("click", this, e);
11452             if(this.handler){
11453                 this.el.removeClass("x-btn-over");
11454                 this.handler.call(this.scope || this, this, e);
11455             }
11456         }
11457     },
11458     // private
11459     onMouseOver : function(e){
11460         if(!this.disabled){
11461             this.el.addClass("x-btn-over");
11462             this.fireEvent('mouseover', this, e);
11463         }
11464     },
11465     // private
11466     onMouseOut : function(e){
11467         if(!e.within(this.el,  true)){
11468             this.el.removeClass("x-btn-over");
11469             this.fireEvent('mouseout', this, e);
11470         }
11471     },
11472     // private
11473     onFocus : function(e){
11474         if(!this.disabled){
11475             this.el.addClass("x-btn-focus");
11476         }
11477     },
11478     // private
11479     onBlur : function(e){
11480         this.el.removeClass("x-btn-focus");
11481     },
11482     // private
11483     onMouseDown : function(e){
11484         if(!this.disabled && e.button == 0){
11485             this.el.addClass("x-btn-click");
11486             Roo.get(document).on('mouseup', this.onMouseUp, this);
11487         }
11488     },
11489     // private
11490     onMouseUp : function(e){
11491         if(e.button == 0){
11492             this.el.removeClass("x-btn-click");
11493             Roo.get(document).un('mouseup', this.onMouseUp, this);
11494         }
11495     },
11496     // private
11497     onMenuShow : function(e){
11498         this.el.addClass("x-btn-menu-active");
11499     },
11500     // private
11501     onMenuHide : function(e){
11502         this.el.removeClass("x-btn-menu-active");
11503     }   
11504 });
11505
11506 // Private utility class used by Button
11507 Roo.ButtonToggleMgr = function(){
11508    var groups = {};
11509    
11510    function toggleGroup(btn, state){
11511        if(state){
11512            var g = groups[btn.toggleGroup];
11513            for(var i = 0, l = g.length; i < l; i++){
11514                if(g[i] != btn){
11515                    g[i].toggle(false);
11516                }
11517            }
11518        }
11519    }
11520    
11521    return {
11522        register : function(btn){
11523            if(!btn.toggleGroup){
11524                return;
11525            }
11526            var g = groups[btn.toggleGroup];
11527            if(!g){
11528                g = groups[btn.toggleGroup] = [];
11529            }
11530            g.push(btn);
11531            btn.on("toggle", toggleGroup);
11532        },
11533        
11534        unregister : function(btn){
11535            if(!btn.toggleGroup){
11536                return;
11537            }
11538            var g = groups[btn.toggleGroup];
11539            if(g){
11540                g.remove(btn);
11541                btn.un("toggle", toggleGroup);
11542            }
11543        }
11544    };
11545 }();/*
11546  * Based on:
11547  * Ext JS Library 1.1.1
11548  * Copyright(c) 2006-2007, Ext JS, LLC.
11549  *
11550  * Originally Released Under LGPL - original licence link has changed is not relivant.
11551  *
11552  * Fork - LGPL
11553  * <script type="text/javascript">
11554  */
11555  
11556 /**
11557  * @class Roo.SplitButton
11558  * @extends Roo.Button
11559  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11560  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11561  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11562  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11563  * @cfg {String} arrowTooltip The title attribute of the arrow
11564  * @constructor
11565  * Create a new menu button
11566  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11567  * @param {Object} config The config object
11568  */
11569 Roo.SplitButton = function(renderTo, config){
11570     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11571     /**
11572      * @event arrowclick
11573      * Fires when this button's arrow is clicked
11574      * @param {SplitButton} this
11575      * @param {EventObject} e The click event
11576      */
11577     this.addEvents({"arrowclick":true});
11578 };
11579
11580 Roo.extend(Roo.SplitButton, Roo.Button, {
11581     render : function(renderTo){
11582         // this is one sweet looking template!
11583         var tpl = new Roo.Template(
11584             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11585             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11586             '<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>',
11587             "</tbody></table></td><td>",
11588             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11589             '<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>',
11590             "</tbody></table></td></tr></table>"
11591         );
11592         var btn = tpl.append(renderTo, [this.text, this.type], true);
11593         var btnEl = btn.child("button");
11594         if(this.cls){
11595             btn.addClass(this.cls);
11596         }
11597         if(this.icon){
11598             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11599         }
11600         if(this.iconCls){
11601             btnEl.addClass(this.iconCls);
11602             if(!this.cls){
11603                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11604             }
11605         }
11606         this.el = btn;
11607         if(this.handleMouseEvents){
11608             btn.on("mouseover", this.onMouseOver, this);
11609             btn.on("mouseout", this.onMouseOut, this);
11610             btn.on("mousedown", this.onMouseDown, this);
11611             btn.on("mouseup", this.onMouseUp, this);
11612         }
11613         btn.on(this.clickEvent, this.onClick, this);
11614         if(this.tooltip){
11615             if(typeof this.tooltip == 'object'){
11616                 Roo.QuickTips.tips(Roo.apply({
11617                       target: btnEl.id
11618                 }, this.tooltip));
11619             } else {
11620                 btnEl.dom[this.tooltipType] = this.tooltip;
11621             }
11622         }
11623         if(this.arrowTooltip){
11624             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11625         }
11626         if(this.hidden){
11627             this.hide();
11628         }
11629         if(this.disabled){
11630             this.disable();
11631         }
11632         if(this.pressed){
11633             this.el.addClass("x-btn-pressed");
11634         }
11635         if(Roo.isIE && !Roo.isIE7){
11636             this.autoWidth.defer(1, this);
11637         }else{
11638             this.autoWidth();
11639         }
11640         if(this.menu){
11641             this.menu.on("show", this.onMenuShow, this);
11642             this.menu.on("hide", this.onMenuHide, this);
11643         }
11644         this.fireEvent('render', this);
11645     },
11646
11647     // private
11648     autoWidth : function(){
11649         if(this.el){
11650             var tbl = this.el.child("table:first");
11651             var tbl2 = this.el.child("table:last");
11652             this.el.setWidth("auto");
11653             tbl.setWidth("auto");
11654             if(Roo.isIE7 && Roo.isStrict){
11655                 var ib = this.el.child('button:first');
11656                 if(ib && ib.getWidth() > 20){
11657                     ib.clip();
11658                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11659                 }
11660             }
11661             if(this.minWidth){
11662                 if(this.hidden){
11663                     this.el.beginMeasure();
11664                 }
11665                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11666                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11667                 }
11668                 if(this.hidden){
11669                     this.el.endMeasure();
11670                 }
11671             }
11672             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
11673         } 
11674     },
11675     /**
11676      * Sets this button's click handler
11677      * @param {Function} handler The function to call when the button is clicked
11678      * @param {Object} scope (optional) Scope for the function passed above
11679      */
11680     setHandler : function(handler, scope){
11681         this.handler = handler;
11682         this.scope = scope;  
11683     },
11684     
11685     /**
11686      * Sets this button's arrow click handler
11687      * @param {Function} handler The function to call when the arrow is clicked
11688      * @param {Object} scope (optional) Scope for the function passed above
11689      */
11690     setArrowHandler : function(handler, scope){
11691         this.arrowHandler = handler;
11692         this.scope = scope;  
11693     },
11694     
11695     /**
11696      * Focus the button
11697      */
11698     focus : function(){
11699         if(this.el){
11700             this.el.child("button:first").focus();
11701         }
11702     },
11703
11704     // private
11705     onClick : function(e){
11706         e.preventDefault();
11707         if(!this.disabled){
11708             if(e.getTarget(".x-btn-menu-arrow-wrap")){
11709                 if(this.menu && !this.menu.isVisible()){
11710                     this.menu.show(this.el, this.menuAlign);
11711                 }
11712                 this.fireEvent("arrowclick", this, e);
11713                 if(this.arrowHandler){
11714                     this.arrowHandler.call(this.scope || this, this, e);
11715                 }
11716             }else{
11717                 this.fireEvent("click", this, e);
11718                 if(this.handler){
11719                     this.handler.call(this.scope || this, this, e);
11720                 }
11721             }
11722         }
11723     },
11724     // private
11725     onMouseDown : function(e){
11726         if(!this.disabled){
11727             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
11728         }
11729     },
11730     // private
11731     onMouseUp : function(e){
11732         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
11733     }   
11734 });
11735
11736
11737 // backwards compat
11738 Roo.MenuButton = Roo.SplitButton;/*
11739  * Based on:
11740  * Ext JS Library 1.1.1
11741  * Copyright(c) 2006-2007, Ext JS, LLC.
11742  *
11743  * Originally Released Under LGPL - original licence link has changed is not relivant.
11744  *
11745  * Fork - LGPL
11746  * <script type="text/javascript">
11747  */
11748
11749 /**
11750  * @class Roo.Toolbar
11751  * Basic Toolbar class.
11752  * @constructor
11753  * Creates a new Toolbar
11754  * @param {Object} container The config object
11755  */ 
11756 Roo.Toolbar = function(container, buttons, config)
11757 {
11758     /// old consturctor format still supported..
11759     if(container instanceof Array){ // omit the container for later rendering
11760         buttons = container;
11761         config = buttons;
11762         container = null;
11763     }
11764     if (typeof(container) == 'object' && container.xtype) {
11765         config = container;
11766         container = config.container;
11767         buttons = config.buttons || []; // not really - use items!!
11768     }
11769     var xitems = [];
11770     if (config && config.items) {
11771         xitems = config.items;
11772         delete config.items;
11773     }
11774     Roo.apply(this, config);
11775     this.buttons = buttons;
11776     
11777     if(container){
11778         this.render(container);
11779     }
11780     this.xitems = xitems;
11781     Roo.each(xitems, function(b) {
11782         this.add(b);
11783     }, this);
11784     
11785 };
11786
11787 Roo.Toolbar.prototype = {
11788     /**
11789      * @cfg {Array} items
11790      * array of button configs or elements to add (will be converted to a MixedCollection)
11791      */
11792     
11793     /**
11794      * @cfg {String/HTMLElement/Element} container
11795      * The id or element that will contain the toolbar
11796      */
11797     // private
11798     render : function(ct){
11799         this.el = Roo.get(ct);
11800         if(this.cls){
11801             this.el.addClass(this.cls);
11802         }
11803         // using a table allows for vertical alignment
11804         // 100% width is needed by Safari...
11805         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
11806         this.tr = this.el.child("tr", true);
11807         var autoId = 0;
11808         this.items = new Roo.util.MixedCollection(false, function(o){
11809             return o.id || ("item" + (++autoId));
11810         });
11811         if(this.buttons){
11812             this.add.apply(this, this.buttons);
11813             delete this.buttons;
11814         }
11815     },
11816
11817     /**
11818      * Adds element(s) to the toolbar -- this function takes a variable number of 
11819      * arguments of mixed type and adds them to the toolbar.
11820      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
11821      * <ul>
11822      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
11823      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
11824      * <li>Field: Any form field (equivalent to {@link #addField})</li>
11825      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
11826      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
11827      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
11828      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
11829      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
11830      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
11831      * </ul>
11832      * @param {Mixed} arg2
11833      * @param {Mixed} etc.
11834      */
11835     add : function(){
11836         var a = arguments, l = a.length;
11837         for(var i = 0; i < l; i++){
11838             this._add(a[i]);
11839         }
11840     },
11841     // private..
11842     _add : function(el) {
11843         
11844         if (el.xtype) {
11845             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
11846         }
11847         
11848         if (el.applyTo){ // some kind of form field
11849             return this.addField(el);
11850         } 
11851         if (el.render){ // some kind of Toolbar.Item
11852             return this.addItem(el);
11853         }
11854         if (typeof el == "string"){ // string
11855             if(el == "separator" || el == "-"){
11856                 return this.addSeparator();
11857             }
11858             if (el == " "){
11859                 return this.addSpacer();
11860             }
11861             if(el == "->"){
11862                 return this.addFill();
11863             }
11864             return this.addText(el);
11865             
11866         }
11867         if(el.tagName){ // element
11868             return this.addElement(el);
11869         }
11870         if(typeof el == "object"){ // must be button config?
11871             return this.addButton(el);
11872         }
11873         // and now what?!?!
11874         return false;
11875         
11876     },
11877     
11878     /**
11879      * Add an Xtype element
11880      * @param {Object} xtype Xtype Object
11881      * @return {Object} created Object
11882      */
11883     addxtype : function(e){
11884         return this.add(e);  
11885     },
11886     
11887     /**
11888      * Returns the Element for this toolbar.
11889      * @return {Roo.Element}
11890      */
11891     getEl : function(){
11892         return this.el;  
11893     },
11894     
11895     /**
11896      * Adds a separator
11897      * @return {Roo.Toolbar.Item} The separator item
11898      */
11899     addSeparator : function(){
11900         return this.addItem(new Roo.Toolbar.Separator());
11901     },
11902
11903     /**
11904      * Adds a spacer element
11905      * @return {Roo.Toolbar.Spacer} The spacer item
11906      */
11907     addSpacer : function(){
11908         return this.addItem(new Roo.Toolbar.Spacer());
11909     },
11910
11911     /**
11912      * Adds a fill element that forces subsequent additions to the right side of the toolbar
11913      * @return {Roo.Toolbar.Fill} The fill item
11914      */
11915     addFill : function(){
11916         return this.addItem(new Roo.Toolbar.Fill());
11917     },
11918
11919     /**
11920      * Adds any standard HTML element to the toolbar
11921      * @param {String/HTMLElement/Element} el The element or id of the element to add
11922      * @return {Roo.Toolbar.Item} The element's item
11923      */
11924     addElement : function(el){
11925         return this.addItem(new Roo.Toolbar.Item(el));
11926     },
11927     /**
11928      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
11929      * @type Roo.util.MixedCollection  
11930      */
11931     items : false,
11932      
11933     /**
11934      * Adds any Toolbar.Item or subclass
11935      * @param {Roo.Toolbar.Item} item
11936      * @return {Roo.Toolbar.Item} The item
11937      */
11938     addItem : function(item){
11939         var td = this.nextBlock();
11940         item.render(td);
11941         this.items.add(item);
11942         return item;
11943     },
11944     
11945     /**
11946      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
11947      * @param {Object/Array} config A button config or array of configs
11948      * @return {Roo.Toolbar.Button/Array}
11949      */
11950     addButton : function(config){
11951         if(config instanceof Array){
11952             var buttons = [];
11953             for(var i = 0, len = config.length; i < len; i++) {
11954                 buttons.push(this.addButton(config[i]));
11955             }
11956             return buttons;
11957         }
11958         var b = config;
11959         if(!(config instanceof Roo.Toolbar.Button)){
11960             b = config.split ?
11961                 new Roo.Toolbar.SplitButton(config) :
11962                 new Roo.Toolbar.Button(config);
11963         }
11964         var td = this.nextBlock();
11965         b.render(td);
11966         this.items.add(b);
11967         return b;
11968     },
11969     
11970     /**
11971      * Adds text to the toolbar
11972      * @param {String} text The text to add
11973      * @return {Roo.Toolbar.Item} The element's item
11974      */
11975     addText : function(text){
11976         return this.addItem(new Roo.Toolbar.TextItem(text));
11977     },
11978     
11979     /**
11980      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
11981      * @param {Number} index The index where the item is to be inserted
11982      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
11983      * @return {Roo.Toolbar.Button/Item}
11984      */
11985     insertButton : function(index, item){
11986         if(item instanceof Array){
11987             var buttons = [];
11988             for(var i = 0, len = item.length; i < len; i++) {
11989                buttons.push(this.insertButton(index + i, item[i]));
11990             }
11991             return buttons;
11992         }
11993         if (!(item instanceof Roo.Toolbar.Button)){
11994            item = new Roo.Toolbar.Button(item);
11995         }
11996         var td = document.createElement("td");
11997         this.tr.insertBefore(td, this.tr.childNodes[index]);
11998         item.render(td);
11999         this.items.insert(index, item);
12000         return item;
12001     },
12002     
12003     /**
12004      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12005      * @param {Object} config
12006      * @return {Roo.Toolbar.Item} The element's item
12007      */
12008     addDom : function(config, returnEl){
12009         var td = this.nextBlock();
12010         Roo.DomHelper.overwrite(td, config);
12011         var ti = new Roo.Toolbar.Item(td.firstChild);
12012         ti.render(td);
12013         this.items.add(ti);
12014         return ti;
12015     },
12016
12017     /**
12018      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12019      * @type Roo.util.MixedCollection  
12020      */
12021     fields : false,
12022     
12023     /**
12024      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12025      * Note: the field should not have been rendered yet. For a field that has already been
12026      * rendered, use {@link #addElement}.
12027      * @param {Roo.form.Field} field
12028      * @return {Roo.ToolbarItem}
12029      */
12030      
12031       
12032     addField : function(field) {
12033         if (!this.fields) {
12034             var autoId = 0;
12035             this.fields = new Roo.util.MixedCollection(false, function(o){
12036                 return o.id || ("item" + (++autoId));
12037             });
12038
12039         }
12040         
12041         var td = this.nextBlock();
12042         field.render(td);
12043         var ti = new Roo.Toolbar.Item(td.firstChild);
12044         ti.render(td);
12045         this.items.add(ti);
12046         this.fields.add(field);
12047         return ti;
12048     },
12049     /**
12050      * Hide the toolbar
12051      * @method hide
12052      */
12053      
12054       
12055     hide : function()
12056     {
12057         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12058         this.el.child('div').hide();
12059     },
12060     /**
12061      * Show the toolbar
12062      * @method show
12063      */
12064     show : function()
12065     {
12066         this.el.child('div').show();
12067     },
12068       
12069     // private
12070     nextBlock : function(){
12071         var td = document.createElement("td");
12072         this.tr.appendChild(td);
12073         return td;
12074     },
12075
12076     // private
12077     destroy : function(){
12078         if(this.items){ // rendered?
12079             Roo.destroy.apply(Roo, this.items.items);
12080         }
12081         if(this.fields){ // rendered?
12082             Roo.destroy.apply(Roo, this.fields.items);
12083         }
12084         Roo.Element.uncache(this.el, this.tr);
12085     }
12086 };
12087
12088 /**
12089  * @class Roo.Toolbar.Item
12090  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12091  * @constructor
12092  * Creates a new Item
12093  * @param {HTMLElement} el 
12094  */
12095 Roo.Toolbar.Item = function(el){
12096     var cfg = {};
12097     if (typeof (el.xtype) != 'undefined') {
12098         cfg = el;
12099         el = cfg.el;
12100     }
12101     
12102     this.el = Roo.getDom(el);
12103     this.id = Roo.id(this.el);
12104     this.hidden = false;
12105     
12106     this.addEvents({
12107          /**
12108              * @event render
12109              * Fires when the button is rendered
12110              * @param {Button} this
12111              */
12112         'render': true
12113     });
12114     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
12115 };
12116 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
12117 //Roo.Toolbar.Item.prototype = {
12118     
12119     /**
12120      * Get this item's HTML Element
12121      * @return {HTMLElement}
12122      */
12123     getEl : function(){
12124        return this.el;  
12125     },
12126
12127     // private
12128     render : function(td){
12129         
12130          this.td = td;
12131         td.appendChild(this.el);
12132         
12133         this.fireEvent('render', this);
12134     },
12135     
12136     /**
12137      * Removes and destroys this item.
12138      */
12139     destroy : function(){
12140         this.td.parentNode.removeChild(this.td);
12141     },
12142     
12143     /**
12144      * Shows this item.
12145      */
12146     show: function(){
12147         this.hidden = false;
12148         this.td.style.display = "";
12149     },
12150     
12151     /**
12152      * Hides this item.
12153      */
12154     hide: function(){
12155         this.hidden = true;
12156         this.td.style.display = "none";
12157     },
12158     
12159     /**
12160      * Convenience function for boolean show/hide.
12161      * @param {Boolean} visible true to show/false to hide
12162      */
12163     setVisible: function(visible){
12164         if(visible) {
12165             this.show();
12166         }else{
12167             this.hide();
12168         }
12169     },
12170     
12171     /**
12172      * Try to focus this item.
12173      */
12174     focus : function(){
12175         Roo.fly(this.el).focus();
12176     },
12177     
12178     /**
12179      * Disables this item.
12180      */
12181     disable : function(){
12182         Roo.fly(this.td).addClass("x-item-disabled");
12183         this.disabled = true;
12184         this.el.disabled = true;
12185     },
12186     
12187     /**
12188      * Enables this item.
12189      */
12190     enable : function(){
12191         Roo.fly(this.td).removeClass("x-item-disabled");
12192         this.disabled = false;
12193         this.el.disabled = false;
12194     }
12195 });
12196
12197
12198 /**
12199  * @class Roo.Toolbar.Separator
12200  * @extends Roo.Toolbar.Item
12201  * A simple toolbar separator class
12202  * @constructor
12203  * Creates a new Separator
12204  */
12205 Roo.Toolbar.Separator = function(cfg){
12206     
12207     var s = document.createElement("span");
12208     s.className = "ytb-sep";
12209     if (cfg) {
12210         cfg.el = s;
12211     }
12212     
12213     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
12214 };
12215 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12216     enable:Roo.emptyFn,
12217     disable:Roo.emptyFn,
12218     focus:Roo.emptyFn
12219 });
12220
12221 /**
12222  * @class Roo.Toolbar.Spacer
12223  * @extends Roo.Toolbar.Item
12224  * A simple element that adds extra horizontal space to a toolbar.
12225  * @constructor
12226  * Creates a new Spacer
12227  */
12228 Roo.Toolbar.Spacer = function(cfg){
12229     var s = document.createElement("div");
12230     s.className = "ytb-spacer";
12231     if (cfg) {
12232         cfg.el = s;
12233     }
12234     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
12235 };
12236 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12237     enable:Roo.emptyFn,
12238     disable:Roo.emptyFn,
12239     focus:Roo.emptyFn
12240 });
12241
12242 /**
12243  * @class Roo.Toolbar.Fill
12244  * @extends Roo.Toolbar.Spacer
12245  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12246  * @constructor
12247  * Creates a new Spacer
12248  */
12249 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12250     // private
12251     render : function(td){
12252         td.style.width = '100%';
12253         Roo.Toolbar.Fill.superclass.render.call(this, td);
12254     }
12255 });
12256
12257 /**
12258  * @class Roo.Toolbar.TextItem
12259  * @extends Roo.Toolbar.Item
12260  * A simple class that renders text directly into a toolbar.
12261  * @constructor
12262  * Creates a new TextItem
12263  * @param {String} text
12264  */
12265 Roo.Toolbar.TextItem = function(cfg){
12266     var  text = cfg || "";
12267     if (typeof(cfg) == 'object') {
12268         text = cfg.text || "";
12269     }  else {
12270         cfg = null;
12271     }
12272     var s = document.createElement("span");
12273     s.className = "ytb-text";
12274     s.innerHTML = text;
12275     if (cfg) {
12276         cfg.el  = s;
12277     }
12278     
12279     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
12280 };
12281 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12282     
12283      
12284     enable:Roo.emptyFn,
12285     disable:Roo.emptyFn,
12286     focus:Roo.emptyFn
12287 });
12288
12289 /**
12290  * @class Roo.Toolbar.Button
12291  * @extends Roo.Button
12292  * A button that renders into a toolbar.
12293  * @constructor
12294  * Creates a new Button
12295  * @param {Object} config A standard {@link Roo.Button} config object
12296  */
12297 Roo.Toolbar.Button = function(config){
12298     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12299 };
12300 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12301     render : function(td){
12302         this.td = td;
12303         Roo.Toolbar.Button.superclass.render.call(this, td);
12304     },
12305     
12306     /**
12307      * Removes and destroys this button
12308      */
12309     destroy : function(){
12310         Roo.Toolbar.Button.superclass.destroy.call(this);
12311         this.td.parentNode.removeChild(this.td);
12312     },
12313     
12314     /**
12315      * Shows this button
12316      */
12317     show: function(){
12318         this.hidden = false;
12319         this.td.style.display = "";
12320     },
12321     
12322     /**
12323      * Hides this button
12324      */
12325     hide: function(){
12326         this.hidden = true;
12327         this.td.style.display = "none";
12328     },
12329
12330     /**
12331      * Disables this item
12332      */
12333     disable : function(){
12334         Roo.fly(this.td).addClass("x-item-disabled");
12335         this.disabled = true;
12336     },
12337
12338     /**
12339      * Enables this item
12340      */
12341     enable : function(){
12342         Roo.fly(this.td).removeClass("x-item-disabled");
12343         this.disabled = false;
12344     }
12345 });
12346 // backwards compat
12347 Roo.ToolbarButton = Roo.Toolbar.Button;
12348
12349 /**
12350  * @class Roo.Toolbar.SplitButton
12351  * @extends Roo.SplitButton
12352  * A menu button that renders into a toolbar.
12353  * @constructor
12354  * Creates a new SplitButton
12355  * @param {Object} config A standard {@link Roo.SplitButton} config object
12356  */
12357 Roo.Toolbar.SplitButton = function(config){
12358     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12359 };
12360 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12361     render : function(td){
12362         this.td = td;
12363         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12364     },
12365     
12366     /**
12367      * Removes and destroys this button
12368      */
12369     destroy : function(){
12370         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12371         this.td.parentNode.removeChild(this.td);
12372     },
12373     
12374     /**
12375      * Shows this button
12376      */
12377     show: function(){
12378         this.hidden = false;
12379         this.td.style.display = "";
12380     },
12381     
12382     /**
12383      * Hides this button
12384      */
12385     hide: function(){
12386         this.hidden = true;
12387         this.td.style.display = "none";
12388     }
12389 });
12390
12391 // backwards compat
12392 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12393  * Based on:
12394  * Ext JS Library 1.1.1
12395  * Copyright(c) 2006-2007, Ext JS, LLC.
12396  *
12397  * Originally Released Under LGPL - original licence link has changed is not relivant.
12398  *
12399  * Fork - LGPL
12400  * <script type="text/javascript">
12401  */
12402  
12403 /**
12404  * @class Roo.PagingToolbar
12405  * @extends Roo.Toolbar
12406  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12407  * @constructor
12408  * Create a new PagingToolbar
12409  * @param {Object} config The config object
12410  */
12411 Roo.PagingToolbar = function(el, ds, config)
12412 {
12413     // old args format still supported... - xtype is prefered..
12414     if (typeof(el) == 'object' && el.xtype) {
12415         // created from xtype...
12416         config = el;
12417         ds = el.dataSource;
12418         el = config.container;
12419     }
12420     var items = [];
12421     if (config.items) {
12422         items = config.items;
12423         config.items = [];
12424     }
12425     
12426     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12427     this.ds = ds;
12428     this.cursor = 0;
12429     this.renderButtons(this.el);
12430     this.bind(ds);
12431     
12432     // supprot items array.
12433    
12434     Roo.each(items, function(e) {
12435         this.add(Roo.factory(e));
12436     },this);
12437     
12438 };
12439
12440 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12441     /**
12442      * @cfg {Roo.data.Store} dataSource
12443      * The underlying data store providing the paged data
12444      */
12445     /**
12446      * @cfg {String/HTMLElement/Element} container
12447      * container The id or element that will contain the toolbar
12448      */
12449     /**
12450      * @cfg {Boolean} displayInfo
12451      * True to display the displayMsg (defaults to false)
12452      */
12453     /**
12454      * @cfg {Number} pageSize
12455      * The number of records to display per page (defaults to 20)
12456      */
12457     pageSize: 20,
12458     /**
12459      * @cfg {String} displayMsg
12460      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12461      */
12462     displayMsg : 'Displaying {0} - {1} of {2}',
12463     /**
12464      * @cfg {String} emptyMsg
12465      * The message to display when no records are found (defaults to "No data to display")
12466      */
12467     emptyMsg : 'No data to display',
12468     /**
12469      * Customizable piece of the default paging text (defaults to "Page")
12470      * @type String
12471      */
12472     beforePageText : "Page",
12473     /**
12474      * Customizable piece of the default paging text (defaults to "of %0")
12475      * @type String
12476      */
12477     afterPageText : "of {0}",
12478     /**
12479      * Customizable piece of the default paging text (defaults to "First Page")
12480      * @type String
12481      */
12482     firstText : "First Page",
12483     /**
12484      * Customizable piece of the default paging text (defaults to "Previous Page")
12485      * @type String
12486      */
12487     prevText : "Previous Page",
12488     /**
12489      * Customizable piece of the default paging text (defaults to "Next Page")
12490      * @type String
12491      */
12492     nextText : "Next Page",
12493     /**
12494      * Customizable piece of the default paging text (defaults to "Last Page")
12495      * @type String
12496      */
12497     lastText : "Last Page",
12498     /**
12499      * Customizable piece of the default paging text (defaults to "Refresh")
12500      * @type String
12501      */
12502     refreshText : "Refresh",
12503
12504     // private
12505     renderButtons : function(el){
12506         Roo.PagingToolbar.superclass.render.call(this, el);
12507         this.first = this.addButton({
12508             tooltip: this.firstText,
12509             cls: "x-btn-icon x-grid-page-first",
12510             disabled: true,
12511             handler: this.onClick.createDelegate(this, ["first"])
12512         });
12513         this.prev = this.addButton({
12514             tooltip: this.prevText,
12515             cls: "x-btn-icon x-grid-page-prev",
12516             disabled: true,
12517             handler: this.onClick.createDelegate(this, ["prev"])
12518         });
12519         //this.addSeparator();
12520         this.add(this.beforePageText);
12521         this.field = Roo.get(this.addDom({
12522            tag: "input",
12523            type: "text",
12524            size: "3",
12525            value: "1",
12526            cls: "x-grid-page-number"
12527         }).el);
12528         this.field.on("keydown", this.onPagingKeydown, this);
12529         this.field.on("focus", function(){this.dom.select();});
12530         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12531         this.field.setHeight(18);
12532         //this.addSeparator();
12533         this.next = this.addButton({
12534             tooltip: this.nextText,
12535             cls: "x-btn-icon x-grid-page-next",
12536             disabled: true,
12537             handler: this.onClick.createDelegate(this, ["next"])
12538         });
12539         this.last = this.addButton({
12540             tooltip: this.lastText,
12541             cls: "x-btn-icon x-grid-page-last",
12542             disabled: true,
12543             handler: this.onClick.createDelegate(this, ["last"])
12544         });
12545         //this.addSeparator();
12546         this.loading = this.addButton({
12547             tooltip: this.refreshText,
12548             cls: "x-btn-icon x-grid-loading",
12549             handler: this.onClick.createDelegate(this, ["refresh"])
12550         });
12551
12552         if(this.displayInfo){
12553             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12554         }
12555     },
12556
12557     // private
12558     updateInfo : function(){
12559         if(this.displayEl){
12560             var count = this.ds.getCount();
12561             var msg = count == 0 ?
12562                 this.emptyMsg :
12563                 String.format(
12564                     this.displayMsg,
12565                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12566                 );
12567             this.displayEl.update(msg);
12568         }
12569     },
12570
12571     // private
12572     onLoad : function(ds, r, o){
12573        this.cursor = o.params ? o.params.start : 0;
12574        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12575
12576        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12577        this.field.dom.value = ap;
12578        this.first.setDisabled(ap == 1);
12579        this.prev.setDisabled(ap == 1);
12580        this.next.setDisabled(ap == ps);
12581        this.last.setDisabled(ap == ps);
12582        this.loading.enable();
12583        this.updateInfo();
12584     },
12585
12586     // private
12587     getPageData : function(){
12588         var total = this.ds.getTotalCount();
12589         return {
12590             total : total,
12591             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12592             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12593         };
12594     },
12595
12596     // private
12597     onLoadError : function(){
12598         this.loading.enable();
12599     },
12600
12601     // private
12602     onPagingKeydown : function(e){
12603         var k = e.getKey();
12604         var d = this.getPageData();
12605         if(k == e.RETURN){
12606             var v = this.field.dom.value, pageNum;
12607             if(!v || isNaN(pageNum = parseInt(v, 10))){
12608                 this.field.dom.value = d.activePage;
12609                 return;
12610             }
12611             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12612             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12613             e.stopEvent();
12614         }
12615         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))
12616         {
12617           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12618           this.field.dom.value = pageNum;
12619           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12620           e.stopEvent();
12621         }
12622         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12623         {
12624           var v = this.field.dom.value, pageNum; 
12625           var increment = (e.shiftKey) ? 10 : 1;
12626           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12627             increment *= -1;
12628           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12629             this.field.dom.value = d.activePage;
12630             return;
12631           }
12632           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12633           {
12634             this.field.dom.value = parseInt(v, 10) + increment;
12635             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12636             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12637           }
12638           e.stopEvent();
12639         }
12640     },
12641
12642     // private
12643     beforeLoad : function(){
12644         if(this.loading){
12645             this.loading.disable();
12646         }
12647     },
12648
12649     // private
12650     onClick : function(which){
12651         var ds = this.ds;
12652         switch(which){
12653             case "first":
12654                 ds.load({params:{start: 0, limit: this.pageSize}});
12655             break;
12656             case "prev":
12657                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12658             break;
12659             case "next":
12660                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12661             break;
12662             case "last":
12663                 var total = ds.getTotalCount();
12664                 var extra = total % this.pageSize;
12665                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12666                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12667             break;
12668             case "refresh":
12669                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12670             break;
12671         }
12672     },
12673
12674     /**
12675      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12676      * @param {Roo.data.Store} store The data store to unbind
12677      */
12678     unbind : function(ds){
12679         ds.un("beforeload", this.beforeLoad, this);
12680         ds.un("load", this.onLoad, this);
12681         ds.un("loadexception", this.onLoadError, this);
12682         ds.un("remove", this.updateInfo, this);
12683         ds.un("add", this.updateInfo, this);
12684         this.ds = undefined;
12685     },
12686
12687     /**
12688      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12689      * @param {Roo.data.Store} store The data store to bind
12690      */
12691     bind : function(ds){
12692         ds.on("beforeload", this.beforeLoad, this);
12693         ds.on("load", this.onLoad, this);
12694         ds.on("loadexception", this.onLoadError, this);
12695         ds.on("remove", this.updateInfo, this);
12696         ds.on("add", this.updateInfo, this);
12697         this.ds = ds;
12698     }
12699 });/*
12700  * Based on:
12701  * Ext JS Library 1.1.1
12702  * Copyright(c) 2006-2007, Ext JS, LLC.
12703  *
12704  * Originally Released Under LGPL - original licence link has changed is not relivant.
12705  *
12706  * Fork - LGPL
12707  * <script type="text/javascript">
12708  */
12709
12710 /**
12711  * @class Roo.Resizable
12712  * @extends Roo.util.Observable
12713  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12714  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12715  * 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
12716  * the element will be wrapped for you automatically.</p>
12717  * <p>Here is the list of valid resize handles:</p>
12718  * <pre>
12719 Value   Description
12720 ------  -------------------
12721  'n'     north
12722  's'     south
12723  'e'     east
12724  'w'     west
12725  'nw'    northwest
12726  'sw'    southwest
12727  'se'    southeast
12728  'ne'    northeast
12729  'hd'    horizontal drag
12730  'all'   all
12731 </pre>
12732  * <p>Here's an example showing the creation of a typical Resizable:</p>
12733  * <pre><code>
12734 var resizer = new Roo.Resizable("element-id", {
12735     handles: 'all',
12736     minWidth: 200,
12737     minHeight: 100,
12738     maxWidth: 500,
12739     maxHeight: 400,
12740     pinned: true
12741 });
12742 resizer.on("resize", myHandler);
12743 </code></pre>
12744  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
12745  * resizer.east.setDisplayed(false);</p>
12746  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
12747  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
12748  * resize operation's new size (defaults to [0, 0])
12749  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
12750  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
12751  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
12752  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
12753  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
12754  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
12755  * @cfg {Number} width The width of the element in pixels (defaults to null)
12756  * @cfg {Number} height The height of the element in pixels (defaults to null)
12757  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
12758  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
12759  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
12760  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
12761  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
12762  * in favor of the handles config option (defaults to false)
12763  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
12764  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
12765  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
12766  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
12767  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
12768  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
12769  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
12770  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
12771  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
12772  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
12773  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
12774  * @constructor
12775  * Create a new resizable component
12776  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
12777  * @param {Object} config configuration options
12778   */
12779 Roo.Resizable = function(el, config)
12780 {
12781     this.el = Roo.get(el);
12782
12783     if(config && config.wrap){
12784         config.resizeChild = this.el;
12785         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
12786         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
12787         this.el.setStyle("overflow", "hidden");
12788         this.el.setPositioning(config.resizeChild.getPositioning());
12789         config.resizeChild.clearPositioning();
12790         if(!config.width || !config.height){
12791             var csize = config.resizeChild.getSize();
12792             this.el.setSize(csize.width, csize.height);
12793         }
12794         if(config.pinned && !config.adjustments){
12795             config.adjustments = "auto";
12796         }
12797     }
12798
12799     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
12800     this.proxy.unselectable();
12801     this.proxy.enableDisplayMode('block');
12802
12803     Roo.apply(this, config);
12804
12805     if(this.pinned){
12806         this.disableTrackOver = true;
12807         this.el.addClass("x-resizable-pinned");
12808     }
12809     // if the element isn't positioned, make it relative
12810     var position = this.el.getStyle("position");
12811     if(position != "absolute" && position != "fixed"){
12812         this.el.setStyle("position", "relative");
12813     }
12814     if(!this.handles){ // no handles passed, must be legacy style
12815         this.handles = 's,e,se';
12816         if(this.multiDirectional){
12817             this.handles += ',n,w';
12818         }
12819     }
12820     if(this.handles == "all"){
12821         this.handles = "n s e w ne nw se sw";
12822     }
12823     var hs = this.handles.split(/\s*?[,;]\s*?| /);
12824     var ps = Roo.Resizable.positions;
12825     for(var i = 0, len = hs.length; i < len; i++){
12826         if(hs[i] && ps[hs[i]]){
12827             var pos = ps[hs[i]];
12828             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
12829         }
12830     }
12831     // legacy
12832     this.corner = this.southeast;
12833     
12834     // updateBox = the box can move..
12835     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
12836         this.updateBox = true;
12837     }
12838
12839     this.activeHandle = null;
12840
12841     if(this.resizeChild){
12842         if(typeof this.resizeChild == "boolean"){
12843             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
12844         }else{
12845             this.resizeChild = Roo.get(this.resizeChild, true);
12846         }
12847     }
12848     
12849     if(this.adjustments == "auto"){
12850         var rc = this.resizeChild;
12851         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
12852         if(rc && (hw || hn)){
12853             rc.position("relative");
12854             rc.setLeft(hw ? hw.el.getWidth() : 0);
12855             rc.setTop(hn ? hn.el.getHeight() : 0);
12856         }
12857         this.adjustments = [
12858             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
12859             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
12860         ];
12861     }
12862
12863     if(this.draggable){
12864         this.dd = this.dynamic ?
12865             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
12866         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
12867     }
12868
12869     // public events
12870     this.addEvents({
12871         /**
12872          * @event beforeresize
12873          * Fired before resize is allowed. Set enabled to false to cancel resize.
12874          * @param {Roo.Resizable} this
12875          * @param {Roo.EventObject} e The mousedown event
12876          */
12877         "beforeresize" : true,
12878         /**
12879          * @event resizing
12880          * Fired a resizing.
12881          * @param {Roo.Resizable} this
12882          * @param {Number} x The new x position
12883          * @param {Number} y The new y position
12884          * @param {Number} w The new w width
12885          * @param {Number} h The new h hight
12886          * @param {Roo.EventObject} e The mouseup event
12887          */
12888         "resizing" : true,
12889         /**
12890          * @event resize
12891          * Fired after a resize.
12892          * @param {Roo.Resizable} this
12893          * @param {Number} width The new width
12894          * @param {Number} height The new height
12895          * @param {Roo.EventObject} e The mouseup event
12896          */
12897         "resize" : true
12898     });
12899
12900     if(this.width !== null && this.height !== null){
12901         this.resizeTo(this.width, this.height);
12902     }else{
12903         this.updateChildSize();
12904     }
12905     if(Roo.isIE){
12906         this.el.dom.style.zoom = 1;
12907     }
12908     Roo.Resizable.superclass.constructor.call(this);
12909 };
12910
12911 Roo.extend(Roo.Resizable, Roo.util.Observable, {
12912         resizeChild : false,
12913         adjustments : [0, 0],
12914         minWidth : 5,
12915         minHeight : 5,
12916         maxWidth : 10000,
12917         maxHeight : 10000,
12918         enabled : true,
12919         animate : false,
12920         duration : .35,
12921         dynamic : false,
12922         handles : false,
12923         multiDirectional : false,
12924         disableTrackOver : false,
12925         easing : 'easeOutStrong',
12926         widthIncrement : 0,
12927         heightIncrement : 0,
12928         pinned : false,
12929         width : null,
12930         height : null,
12931         preserveRatio : false,
12932         transparent: false,
12933         minX: 0,
12934         minY: 0,
12935         draggable: false,
12936
12937         /**
12938          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
12939          */
12940         constrainTo: undefined,
12941         /**
12942          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
12943          */
12944         resizeRegion: undefined,
12945
12946
12947     /**
12948      * Perform a manual resize
12949      * @param {Number} width
12950      * @param {Number} height
12951      */
12952     resizeTo : function(width, height){
12953         this.el.setSize(width, height);
12954         this.updateChildSize();
12955         this.fireEvent("resize", this, width, height, null);
12956     },
12957
12958     // private
12959     startSizing : function(e, handle){
12960         this.fireEvent("beforeresize", this, e);
12961         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
12962
12963             if(!this.overlay){
12964                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
12965                 this.overlay.unselectable();
12966                 this.overlay.enableDisplayMode("block");
12967                 this.overlay.on("mousemove", this.onMouseMove, this);
12968                 this.overlay.on("mouseup", this.onMouseUp, this);
12969             }
12970             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
12971
12972             this.resizing = true;
12973             this.startBox = this.el.getBox();
12974             this.startPoint = e.getXY();
12975             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
12976                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
12977
12978             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
12979             this.overlay.show();
12980
12981             if(this.constrainTo) {
12982                 var ct = Roo.get(this.constrainTo);
12983                 this.resizeRegion = ct.getRegion().adjust(
12984                     ct.getFrameWidth('t'),
12985                     ct.getFrameWidth('l'),
12986                     -ct.getFrameWidth('b'),
12987                     -ct.getFrameWidth('r')
12988                 );
12989             }
12990
12991             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
12992             this.proxy.show();
12993             this.proxy.setBox(this.startBox);
12994             if(!this.dynamic){
12995                 this.proxy.setStyle('visibility', 'visible');
12996             }
12997         }
12998     },
12999
13000     // private
13001     onMouseDown : function(handle, e){
13002         if(this.enabled){
13003             e.stopEvent();
13004             this.activeHandle = handle;
13005             this.startSizing(e, handle);
13006         }
13007     },
13008
13009     // private
13010     onMouseUp : function(e){
13011         var size = this.resizeElement();
13012         this.resizing = false;
13013         this.handleOut();
13014         this.overlay.hide();
13015         this.proxy.hide();
13016         this.fireEvent("resize", this, size.width, size.height, e);
13017     },
13018
13019     // private
13020     updateChildSize : function(){
13021         
13022         if(this.resizeChild){
13023             var el = this.el;
13024             var child = this.resizeChild;
13025             var adj = this.adjustments;
13026             if(el.dom.offsetWidth){
13027                 var b = el.getSize(true);
13028                 child.setSize(b.width+adj[0], b.height+adj[1]);
13029             }
13030             // Second call here for IE
13031             // The first call enables instant resizing and
13032             // the second call corrects scroll bars if they
13033             // exist
13034             if(Roo.isIE){
13035                 setTimeout(function(){
13036                     if(el.dom.offsetWidth){
13037                         var b = el.getSize(true);
13038                         child.setSize(b.width+adj[0], b.height+adj[1]);
13039                     }
13040                 }, 10);
13041             }
13042         }
13043     },
13044
13045     // private
13046     snap : function(value, inc, min){
13047         if(!inc || !value) return value;
13048         var newValue = value;
13049         var m = value % inc;
13050         if(m > 0){
13051             if(m > (inc/2)){
13052                 newValue = value + (inc-m);
13053             }else{
13054                 newValue = value - m;
13055             }
13056         }
13057         return Math.max(min, newValue);
13058     },
13059
13060     // private
13061     resizeElement : function(){
13062         var box = this.proxy.getBox();
13063         if(this.updateBox){
13064             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13065         }else{
13066             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13067         }
13068         this.updateChildSize();
13069         if(!this.dynamic){
13070             this.proxy.hide();
13071         }
13072         return box;
13073     },
13074
13075     // private
13076     constrain : function(v, diff, m, mx){
13077         if(v - diff < m){
13078             diff = v - m;
13079         }else if(v - diff > mx){
13080             diff = mx - v;
13081         }
13082         return diff;
13083     },
13084
13085     // private
13086     onMouseMove : function(e){
13087         
13088         if(this.enabled){
13089             try{// try catch so if something goes wrong the user doesn't get hung
13090
13091             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13092                 return;
13093             }
13094
13095             //var curXY = this.startPoint;
13096             var curSize = this.curSize || this.startBox;
13097             var x = this.startBox.x, y = this.startBox.y;
13098             var ox = x, oy = y;
13099             var w = curSize.width, h = curSize.height;
13100             var ow = w, oh = h;
13101             var mw = this.minWidth, mh = this.minHeight;
13102             var mxw = this.maxWidth, mxh = this.maxHeight;
13103             var wi = this.widthIncrement;
13104             var hi = this.heightIncrement;
13105
13106             var eventXY = e.getXY();
13107             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13108             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13109
13110             var pos = this.activeHandle.position;
13111
13112             switch(pos){
13113                 case "east":
13114                     w += diffX;
13115                     w = Math.min(Math.max(mw, w), mxw);
13116                     break;
13117              
13118                 case "south":
13119                     h += diffY;
13120                     h = Math.min(Math.max(mh, h), mxh);
13121                     break;
13122                 case "southeast":
13123                     w += diffX;
13124                     h += diffY;
13125                     w = Math.min(Math.max(mw, w), mxw);
13126                     h = Math.min(Math.max(mh, h), mxh);
13127                     break;
13128                 case "north":
13129                     diffY = this.constrain(h, diffY, mh, mxh);
13130                     y += diffY;
13131                     h -= diffY;
13132                     break;
13133                 case "hdrag":
13134                     
13135                     if (wi) {
13136                         var adiffX = Math.abs(diffX);
13137                         var sub = (adiffX % wi); // how much 
13138                         if (sub > (wi/2)) { // far enough to snap
13139                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13140                         } else {
13141                             // remove difference.. 
13142                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13143                         }
13144                     }
13145                     x += diffX;
13146                     x = Math.max(this.minX, x);
13147                     break;
13148                 case "west":
13149                     diffX = this.constrain(w, diffX, mw, mxw);
13150                     x += diffX;
13151                     w -= diffX;
13152                     break;
13153                 case "northeast":
13154                     w += diffX;
13155                     w = Math.min(Math.max(mw, w), mxw);
13156                     diffY = this.constrain(h, diffY, mh, mxh);
13157                     y += diffY;
13158                     h -= diffY;
13159                     break;
13160                 case "northwest":
13161                     diffX = this.constrain(w, diffX, mw, mxw);
13162                     diffY = this.constrain(h, diffY, mh, mxh);
13163                     y += diffY;
13164                     h -= diffY;
13165                     x += diffX;
13166                     w -= diffX;
13167                     break;
13168                case "southwest":
13169                     diffX = this.constrain(w, diffX, mw, mxw);
13170                     h += diffY;
13171                     h = Math.min(Math.max(mh, h), mxh);
13172                     x += diffX;
13173                     w -= diffX;
13174                     break;
13175             }
13176
13177             var sw = this.snap(w, wi, mw);
13178             var sh = this.snap(h, hi, mh);
13179             if(sw != w || sh != h){
13180                 switch(pos){
13181                     case "northeast":
13182                         y -= sh - h;
13183                     break;
13184                     case "north":
13185                         y -= sh - h;
13186                         break;
13187                     case "southwest":
13188                         x -= sw - w;
13189                     break;
13190                     case "west":
13191                         x -= sw - w;
13192                         break;
13193                     case "northwest":
13194                         x -= sw - w;
13195                         y -= sh - h;
13196                     break;
13197                 }
13198                 w = sw;
13199                 h = sh;
13200             }
13201
13202             if(this.preserveRatio){
13203                 switch(pos){
13204                     case "southeast":
13205                     case "east":
13206                         h = oh * (w/ow);
13207                         h = Math.min(Math.max(mh, h), mxh);
13208                         w = ow * (h/oh);
13209                        break;
13210                     case "south":
13211                         w = ow * (h/oh);
13212                         w = Math.min(Math.max(mw, w), mxw);
13213                         h = oh * (w/ow);
13214                         break;
13215                     case "northeast":
13216                         w = ow * (h/oh);
13217                         w = Math.min(Math.max(mw, w), mxw);
13218                         h = oh * (w/ow);
13219                     break;
13220                     case "north":
13221                         var tw = w;
13222                         w = ow * (h/oh);
13223                         w = Math.min(Math.max(mw, w), mxw);
13224                         h = oh * (w/ow);
13225                         x += (tw - w) / 2;
13226                         break;
13227                     case "southwest":
13228                         h = oh * (w/ow);
13229                         h = Math.min(Math.max(mh, h), mxh);
13230                         var tw = w;
13231                         w = ow * (h/oh);
13232                         x += tw - w;
13233                         break;
13234                     case "west":
13235                         var th = h;
13236                         h = oh * (w/ow);
13237                         h = Math.min(Math.max(mh, h), mxh);
13238                         y += (th - h) / 2;
13239                         var tw = w;
13240                         w = ow * (h/oh);
13241                         x += tw - w;
13242                        break;
13243                     case "northwest":
13244                         var tw = w;
13245                         var th = h;
13246                         h = oh * (w/ow);
13247                         h = Math.min(Math.max(mh, h), mxh);
13248                         w = ow * (h/oh);
13249                         y += th - h;
13250                         x += tw - w;
13251                        break;
13252
13253                 }
13254             }
13255             if (pos == 'hdrag') {
13256                 w = ow;
13257             }
13258             this.proxy.setBounds(x, y, w, h);
13259             if(this.dynamic){
13260                 this.resizeElement();
13261             }
13262             }catch(e){}
13263         }
13264         this.fireEvent("resizing", this, x, y, w, h, e);
13265     },
13266
13267     // private
13268     handleOver : function(){
13269         if(this.enabled){
13270             this.el.addClass("x-resizable-over");
13271         }
13272     },
13273
13274     // private
13275     handleOut : function(){
13276         if(!this.resizing){
13277             this.el.removeClass("x-resizable-over");
13278         }
13279     },
13280
13281     /**
13282      * Returns the element this component is bound to.
13283      * @return {Roo.Element}
13284      */
13285     getEl : function(){
13286         return this.el;
13287     },
13288
13289     /**
13290      * Returns the resizeChild element (or null).
13291      * @return {Roo.Element}
13292      */
13293     getResizeChild : function(){
13294         return this.resizeChild;
13295     },
13296     groupHandler : function()
13297     {
13298         
13299     },
13300     /**
13301      * Destroys this resizable. If the element was wrapped and
13302      * removeEl is not true then the element remains.
13303      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13304      */
13305     destroy : function(removeEl){
13306         this.proxy.remove();
13307         if(this.overlay){
13308             this.overlay.removeAllListeners();
13309             this.overlay.remove();
13310         }
13311         var ps = Roo.Resizable.positions;
13312         for(var k in ps){
13313             if(typeof ps[k] != "function" && this[ps[k]]){
13314                 var h = this[ps[k]];
13315                 h.el.removeAllListeners();
13316                 h.el.remove();
13317             }
13318         }
13319         if(removeEl){
13320             this.el.update("");
13321             this.el.remove();
13322         }
13323     }
13324 });
13325
13326 // private
13327 // hash to map config positions to true positions
13328 Roo.Resizable.positions = {
13329     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13330     hd: "hdrag"
13331 };
13332
13333 // private
13334 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13335     if(!this.tpl){
13336         // only initialize the template if resizable is used
13337         var tpl = Roo.DomHelper.createTemplate(
13338             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13339         );
13340         tpl.compile();
13341         Roo.Resizable.Handle.prototype.tpl = tpl;
13342     }
13343     this.position = pos;
13344     this.rz = rz;
13345     // show north drag fro topdra
13346     var handlepos = pos == 'hdrag' ? 'north' : pos;
13347     
13348     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13349     if (pos == 'hdrag') {
13350         this.el.setStyle('cursor', 'pointer');
13351     }
13352     this.el.unselectable();
13353     if(transparent){
13354         this.el.setOpacity(0);
13355     }
13356     this.el.on("mousedown", this.onMouseDown, this);
13357     if(!disableTrackOver){
13358         this.el.on("mouseover", this.onMouseOver, this);
13359         this.el.on("mouseout", this.onMouseOut, this);
13360     }
13361 };
13362
13363 // private
13364 Roo.Resizable.Handle.prototype = {
13365     afterResize : function(rz){
13366         Roo.log('after?');
13367         // do nothing
13368     },
13369     // private
13370     onMouseDown : function(e){
13371         this.rz.onMouseDown(this, e);
13372     },
13373     // private
13374     onMouseOver : function(e){
13375         this.rz.handleOver(this, e);
13376     },
13377     // private
13378     onMouseOut : function(e){
13379         this.rz.handleOut(this, e);
13380     }
13381 };/*
13382  * Based on:
13383  * Ext JS Library 1.1.1
13384  * Copyright(c) 2006-2007, Ext JS, LLC.
13385  *
13386  * Originally Released Under LGPL - original licence link has changed is not relivant.
13387  *
13388  * Fork - LGPL
13389  * <script type="text/javascript">
13390  */
13391
13392 /**
13393  * @class Roo.Editor
13394  * @extends Roo.Component
13395  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13396  * @constructor
13397  * Create a new Editor
13398  * @param {Roo.form.Field} field The Field object (or descendant)
13399  * @param {Object} config The config object
13400  */
13401 Roo.Editor = function(field, config){
13402     Roo.Editor.superclass.constructor.call(this, config);
13403     this.field = field;
13404     this.addEvents({
13405         /**
13406              * @event beforestartedit
13407              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13408              * false from the handler of this event.
13409              * @param {Editor} this
13410              * @param {Roo.Element} boundEl The underlying element bound to this editor
13411              * @param {Mixed} value The field value being set
13412              */
13413         "beforestartedit" : true,
13414         /**
13415              * @event startedit
13416              * Fires when this editor is displayed
13417              * @param {Roo.Element} boundEl The underlying element bound to this editor
13418              * @param {Mixed} value The starting field value
13419              */
13420         "startedit" : true,
13421         /**
13422              * @event beforecomplete
13423              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13424              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13425              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13426              * event will not fire since no edit actually occurred.
13427              * @param {Editor} this
13428              * @param {Mixed} value The current field value
13429              * @param {Mixed} startValue The original field value
13430              */
13431         "beforecomplete" : true,
13432         /**
13433              * @event complete
13434              * Fires after editing is complete and any changed value has been written to the underlying field.
13435              * @param {Editor} this
13436              * @param {Mixed} value The current field value
13437              * @param {Mixed} startValue The original field value
13438              */
13439         "complete" : true,
13440         /**
13441          * @event specialkey
13442          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13443          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13444          * @param {Roo.form.Field} this
13445          * @param {Roo.EventObject} e The event object
13446          */
13447         "specialkey" : true
13448     });
13449 };
13450
13451 Roo.extend(Roo.Editor, Roo.Component, {
13452     /**
13453      * @cfg {Boolean/String} autosize
13454      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13455      * or "height" to adopt the height only (defaults to false)
13456      */
13457     /**
13458      * @cfg {Boolean} revertInvalid
13459      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13460      * validation fails (defaults to true)
13461      */
13462     /**
13463      * @cfg {Boolean} ignoreNoChange
13464      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13465      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13466      * will never be ignored.
13467      */
13468     /**
13469      * @cfg {Boolean} hideEl
13470      * False to keep the bound element visible while the editor is displayed (defaults to true)
13471      */
13472     /**
13473      * @cfg {Mixed} value
13474      * The data value of the underlying field (defaults to "")
13475      */
13476     value : "",
13477     /**
13478      * @cfg {String} alignment
13479      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13480      */
13481     alignment: "c-c?",
13482     /**
13483      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13484      * for bottom-right shadow (defaults to "frame")
13485      */
13486     shadow : "frame",
13487     /**
13488      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13489      */
13490     constrain : false,
13491     /**
13492      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13493      */
13494     completeOnEnter : false,
13495     /**
13496      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13497      */
13498     cancelOnEsc : false,
13499     /**
13500      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13501      */
13502     updateEl : false,
13503
13504     // private
13505     onRender : function(ct, position){
13506         this.el = new Roo.Layer({
13507             shadow: this.shadow,
13508             cls: "x-editor",
13509             parentEl : ct,
13510             shim : this.shim,
13511             shadowOffset:4,
13512             id: this.id,
13513             constrain: this.constrain
13514         });
13515         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13516         if(this.field.msgTarget != 'title'){
13517             this.field.msgTarget = 'qtip';
13518         }
13519         this.field.render(this.el);
13520         if(Roo.isGecko){
13521             this.field.el.dom.setAttribute('autocomplete', 'off');
13522         }
13523         this.field.on("specialkey", this.onSpecialKey, this);
13524         if(this.swallowKeys){
13525             this.field.el.swallowEvent(['keydown','keypress']);
13526         }
13527         this.field.show();
13528         this.field.on("blur", this.onBlur, this);
13529         if(this.field.grow){
13530             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13531         }
13532     },
13533
13534     onSpecialKey : function(field, e)
13535     {
13536         //Roo.log('editor onSpecialKey');
13537         if(this.completeOnEnter && e.getKey() == e.ENTER){
13538             e.stopEvent();
13539             this.completeEdit();
13540             return;
13541         }
13542         // do not fire special key otherwise it might hide close the editor...
13543         if(e.getKey() == e.ENTER){    
13544             return;
13545         }
13546         if(this.cancelOnEsc && e.getKey() == e.ESC){
13547             this.cancelEdit();
13548             return;
13549         } 
13550         this.fireEvent('specialkey', field, e);
13551     
13552     },
13553
13554     /**
13555      * Starts the editing process and shows the editor.
13556      * @param {String/HTMLElement/Element} el The element to edit
13557      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13558       * to the innerHTML of el.
13559      */
13560     startEdit : function(el, value){
13561         if(this.editing){
13562             this.completeEdit();
13563         }
13564         this.boundEl = Roo.get(el);
13565         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13566         if(!this.rendered){
13567             this.render(this.parentEl || document.body);
13568         }
13569         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13570             return;
13571         }
13572         this.startValue = v;
13573         this.field.setValue(v);
13574         if(this.autoSize){
13575             var sz = this.boundEl.getSize();
13576             switch(this.autoSize){
13577                 case "width":
13578                 this.setSize(sz.width,  "");
13579                 break;
13580                 case "height":
13581                 this.setSize("",  sz.height);
13582                 break;
13583                 default:
13584                 this.setSize(sz.width,  sz.height);
13585             }
13586         }
13587         this.el.alignTo(this.boundEl, this.alignment);
13588         this.editing = true;
13589         if(Roo.QuickTips){
13590             Roo.QuickTips.disable();
13591         }
13592         this.show();
13593     },
13594
13595     /**
13596      * Sets the height and width of this editor.
13597      * @param {Number} width The new width
13598      * @param {Number} height The new height
13599      */
13600     setSize : function(w, h){
13601         this.field.setSize(w, h);
13602         if(this.el){
13603             this.el.sync();
13604         }
13605     },
13606
13607     /**
13608      * Realigns the editor to the bound field based on the current alignment config value.
13609      */
13610     realign : function(){
13611         this.el.alignTo(this.boundEl, this.alignment);
13612     },
13613
13614     /**
13615      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13616      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13617      */
13618     completeEdit : function(remainVisible){
13619         if(!this.editing){
13620             return;
13621         }
13622         var v = this.getValue();
13623         if(this.revertInvalid !== false && !this.field.isValid()){
13624             v = this.startValue;
13625             this.cancelEdit(true);
13626         }
13627         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13628             this.editing = false;
13629             this.hide();
13630             return;
13631         }
13632         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13633             this.editing = false;
13634             if(this.updateEl && this.boundEl){
13635                 this.boundEl.update(v);
13636             }
13637             if(remainVisible !== true){
13638                 this.hide();
13639             }
13640             this.fireEvent("complete", this, v, this.startValue);
13641         }
13642     },
13643
13644     // private
13645     onShow : function(){
13646         this.el.show();
13647         if(this.hideEl !== false){
13648             this.boundEl.hide();
13649         }
13650         this.field.show();
13651         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13652             this.fixIEFocus = true;
13653             this.deferredFocus.defer(50, this);
13654         }else{
13655             this.field.focus();
13656         }
13657         this.fireEvent("startedit", this.boundEl, this.startValue);
13658     },
13659
13660     deferredFocus : function(){
13661         if(this.editing){
13662             this.field.focus();
13663         }
13664     },
13665
13666     /**
13667      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13668      * reverted to the original starting value.
13669      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13670      * cancel (defaults to false)
13671      */
13672     cancelEdit : function(remainVisible){
13673         if(this.editing){
13674             this.setValue(this.startValue);
13675             if(remainVisible !== true){
13676                 this.hide();
13677             }
13678         }
13679     },
13680
13681     // private
13682     onBlur : function(){
13683         if(this.allowBlur !== true && this.editing){
13684             this.completeEdit();
13685         }
13686     },
13687
13688     // private
13689     onHide : function(){
13690         if(this.editing){
13691             this.completeEdit();
13692             return;
13693         }
13694         this.field.blur();
13695         if(this.field.collapse){
13696             this.field.collapse();
13697         }
13698         this.el.hide();
13699         if(this.hideEl !== false){
13700             this.boundEl.show();
13701         }
13702         if(Roo.QuickTips){
13703             Roo.QuickTips.enable();
13704         }
13705     },
13706
13707     /**
13708      * Sets the data value of the editor
13709      * @param {Mixed} value Any valid value supported by the underlying field
13710      */
13711     setValue : function(v){
13712         this.field.setValue(v);
13713     },
13714
13715     /**
13716      * Gets the data value of the editor
13717      * @return {Mixed} The data value
13718      */
13719     getValue : function(){
13720         return this.field.getValue();
13721     }
13722 });/*
13723  * Based on:
13724  * Ext JS Library 1.1.1
13725  * Copyright(c) 2006-2007, Ext JS, LLC.
13726  *
13727  * Originally Released Under LGPL - original licence link has changed is not relivant.
13728  *
13729  * Fork - LGPL
13730  * <script type="text/javascript">
13731  */
13732  
13733 /**
13734  * @class Roo.BasicDialog
13735  * @extends Roo.util.Observable
13736  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13737  * <pre><code>
13738 var dlg = new Roo.BasicDialog("my-dlg", {
13739     height: 200,
13740     width: 300,
13741     minHeight: 100,
13742     minWidth: 150,
13743     modal: true,
13744     proxyDrag: true,
13745     shadow: true
13746 });
13747 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13748 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13749 dlg.addButton('Cancel', dlg.hide, dlg);
13750 dlg.show();
13751 </code></pre>
13752   <b>A Dialog should always be a direct child of the body element.</b>
13753  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13754  * @cfg {String} title Default text to display in the title bar (defaults to null)
13755  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13756  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13757  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13758  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13759  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13760  * (defaults to null with no animation)
13761  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13762  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13763  * property for valid values (defaults to 'all')
13764  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13765  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13766  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13767  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13768  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13769  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13770  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13771  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13772  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
13773  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
13774  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
13775  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
13776  * draggable = true (defaults to false)
13777  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
13778  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13779  * shadow (defaults to false)
13780  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
13781  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
13782  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
13783  * @cfg {Array} buttons Array of buttons
13784  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
13785  * @constructor
13786  * Create a new BasicDialog.
13787  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
13788  * @param {Object} config Configuration options
13789  */
13790 Roo.BasicDialog = function(el, config){
13791     this.el = Roo.get(el);
13792     var dh = Roo.DomHelper;
13793     if(!this.el && config && config.autoCreate){
13794         if(typeof config.autoCreate == "object"){
13795             if(!config.autoCreate.id){
13796                 config.autoCreate.id = el;
13797             }
13798             this.el = dh.append(document.body,
13799                         config.autoCreate, true);
13800         }else{
13801             this.el = dh.append(document.body,
13802                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
13803         }
13804     }
13805     el = this.el;
13806     el.setDisplayed(true);
13807     el.hide = this.hideAction;
13808     this.id = el.id;
13809     el.addClass("x-dlg");
13810
13811     Roo.apply(this, config);
13812
13813     this.proxy = el.createProxy("x-dlg-proxy");
13814     this.proxy.hide = this.hideAction;
13815     this.proxy.setOpacity(.5);
13816     this.proxy.hide();
13817
13818     if(config.width){
13819         el.setWidth(config.width);
13820     }
13821     if(config.height){
13822         el.setHeight(config.height);
13823     }
13824     this.size = el.getSize();
13825     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
13826         this.xy = [config.x,config.y];
13827     }else{
13828         this.xy = el.getCenterXY(true);
13829     }
13830     /** The header element @type Roo.Element */
13831     this.header = el.child("> .x-dlg-hd");
13832     /** The body element @type Roo.Element */
13833     this.body = el.child("> .x-dlg-bd");
13834     /** The footer element @type Roo.Element */
13835     this.footer = el.child("> .x-dlg-ft");
13836
13837     if(!this.header){
13838         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
13839     }
13840     if(!this.body){
13841         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
13842     }
13843
13844     this.header.unselectable();
13845     if(this.title){
13846         this.header.update(this.title);
13847     }
13848     // this element allows the dialog to be focused for keyboard event
13849     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
13850     this.focusEl.swallowEvent("click", true);
13851
13852     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
13853
13854     // wrap the body and footer for special rendering
13855     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
13856     if(this.footer){
13857         this.bwrap.dom.appendChild(this.footer.dom);
13858     }
13859
13860     this.bg = this.el.createChild({
13861         tag: "div", cls:"x-dlg-bg",
13862         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
13863     });
13864     this.centerBg = this.bg.child("div.x-dlg-bg-center");
13865
13866
13867     if(this.autoScroll !== false && !this.autoTabs){
13868         this.body.setStyle("overflow", "auto");
13869     }
13870
13871     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
13872
13873     if(this.closable !== false){
13874         this.el.addClass("x-dlg-closable");
13875         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
13876         this.close.on("click", this.closeClick, this);
13877         this.close.addClassOnOver("x-dlg-close-over");
13878     }
13879     if(this.collapsible !== false){
13880         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
13881         this.collapseBtn.on("click", this.collapseClick, this);
13882         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
13883         this.header.on("dblclick", this.collapseClick, this);
13884     }
13885     if(this.resizable !== false){
13886         this.el.addClass("x-dlg-resizable");
13887         this.resizer = new Roo.Resizable(el, {
13888             minWidth: this.minWidth || 80,
13889             minHeight:this.minHeight || 80,
13890             handles: this.resizeHandles || "all",
13891             pinned: true
13892         });
13893         this.resizer.on("beforeresize", this.beforeResize, this);
13894         this.resizer.on("resize", this.onResize, this);
13895     }
13896     if(this.draggable !== false){
13897         el.addClass("x-dlg-draggable");
13898         if (!this.proxyDrag) {
13899             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
13900         }
13901         else {
13902             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
13903         }
13904         dd.setHandleElId(this.header.id);
13905         dd.endDrag = this.endMove.createDelegate(this);
13906         dd.startDrag = this.startMove.createDelegate(this);
13907         dd.onDrag = this.onDrag.createDelegate(this);
13908         dd.scroll = false;
13909         this.dd = dd;
13910     }
13911     if(this.modal){
13912         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
13913         this.mask.enableDisplayMode("block");
13914         this.mask.hide();
13915         this.el.addClass("x-dlg-modal");
13916     }
13917     if(this.shadow){
13918         this.shadow = new Roo.Shadow({
13919             mode : typeof this.shadow == "string" ? this.shadow : "sides",
13920             offset : this.shadowOffset
13921         });
13922     }else{
13923         this.shadowOffset = 0;
13924     }
13925     if(Roo.useShims && this.shim !== false){
13926         this.shim = this.el.createShim();
13927         this.shim.hide = this.hideAction;
13928         this.shim.hide();
13929     }else{
13930         this.shim = false;
13931     }
13932     if(this.autoTabs){
13933         this.initTabs();
13934     }
13935     if (this.buttons) { 
13936         var bts= this.buttons;
13937         this.buttons = [];
13938         Roo.each(bts, function(b) {
13939             this.addButton(b);
13940         }, this);
13941     }
13942     
13943     
13944     this.addEvents({
13945         /**
13946          * @event keydown
13947          * Fires when a key is pressed
13948          * @param {Roo.BasicDialog} this
13949          * @param {Roo.EventObject} e
13950          */
13951         "keydown" : true,
13952         /**
13953          * @event move
13954          * Fires when this dialog is moved by the user.
13955          * @param {Roo.BasicDialog} this
13956          * @param {Number} x The new page X
13957          * @param {Number} y The new page Y
13958          */
13959         "move" : true,
13960         /**
13961          * @event resize
13962          * Fires when this dialog is resized by the user.
13963          * @param {Roo.BasicDialog} this
13964          * @param {Number} width The new width
13965          * @param {Number} height The new height
13966          */
13967         "resize" : true,
13968         /**
13969          * @event beforehide
13970          * Fires before this dialog is hidden.
13971          * @param {Roo.BasicDialog} this
13972          */
13973         "beforehide" : true,
13974         /**
13975          * @event hide
13976          * Fires when this dialog is hidden.
13977          * @param {Roo.BasicDialog} this
13978          */
13979         "hide" : true,
13980         /**
13981          * @event beforeshow
13982          * Fires before this dialog is shown.
13983          * @param {Roo.BasicDialog} this
13984          */
13985         "beforeshow" : true,
13986         /**
13987          * @event show
13988          * Fires when this dialog is shown.
13989          * @param {Roo.BasicDialog} this
13990          */
13991         "show" : true
13992     });
13993     el.on("keydown", this.onKeyDown, this);
13994     el.on("mousedown", this.toFront, this);
13995     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
13996     this.el.hide();
13997     Roo.DialogManager.register(this);
13998     Roo.BasicDialog.superclass.constructor.call(this);
13999 };
14000
14001 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14002     shadowOffset: Roo.isIE ? 6 : 5,
14003     minHeight: 80,
14004     minWidth: 200,
14005     minButtonWidth: 75,
14006     defaultButton: null,
14007     buttonAlign: "right",
14008     tabTag: 'div',
14009     firstShow: true,
14010
14011     /**
14012      * Sets the dialog title text
14013      * @param {String} text The title text to display
14014      * @return {Roo.BasicDialog} this
14015      */
14016     setTitle : function(text){
14017         this.header.update(text);
14018         return this;
14019     },
14020
14021     // private
14022     closeClick : function(){
14023         this.hide();
14024     },
14025
14026     // private
14027     collapseClick : function(){
14028         this[this.collapsed ? "expand" : "collapse"]();
14029     },
14030
14031     /**
14032      * Collapses the dialog to its minimized state (only the title bar is visible).
14033      * Equivalent to the user clicking the collapse dialog button.
14034      */
14035     collapse : function(){
14036         if(!this.collapsed){
14037             this.collapsed = true;
14038             this.el.addClass("x-dlg-collapsed");
14039             this.restoreHeight = this.el.getHeight();
14040             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14041         }
14042     },
14043
14044     /**
14045      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14046      * clicking the expand dialog button.
14047      */
14048     expand : function(){
14049         if(this.collapsed){
14050             this.collapsed = false;
14051             this.el.removeClass("x-dlg-collapsed");
14052             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14053         }
14054     },
14055
14056     /**
14057      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14058      * @return {Roo.TabPanel} The tabs component
14059      */
14060     initTabs : function(){
14061         var tabs = this.getTabs();
14062         while(tabs.getTab(0)){
14063             tabs.removeTab(0);
14064         }
14065         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14066             var dom = el.dom;
14067             tabs.addTab(Roo.id(dom), dom.title);
14068             dom.title = "";
14069         });
14070         tabs.activate(0);
14071         return tabs;
14072     },
14073
14074     // private
14075     beforeResize : function(){
14076         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14077     },
14078
14079     // private
14080     onResize : function(){
14081         this.refreshSize();
14082         this.syncBodyHeight();
14083         this.adjustAssets();
14084         this.focus();
14085         this.fireEvent("resize", this, this.size.width, this.size.height);
14086     },
14087
14088     // private
14089     onKeyDown : function(e){
14090         if(this.isVisible()){
14091             this.fireEvent("keydown", this, e);
14092         }
14093     },
14094
14095     /**
14096      * Resizes the dialog.
14097      * @param {Number} width
14098      * @param {Number} height
14099      * @return {Roo.BasicDialog} this
14100      */
14101     resizeTo : function(width, height){
14102         this.el.setSize(width, height);
14103         this.size = {width: width, height: height};
14104         this.syncBodyHeight();
14105         if(this.fixedcenter){
14106             this.center();
14107         }
14108         if(this.isVisible()){
14109             this.constrainXY();
14110             this.adjustAssets();
14111         }
14112         this.fireEvent("resize", this, width, height);
14113         return this;
14114     },
14115
14116
14117     /**
14118      * Resizes the dialog to fit the specified content size.
14119      * @param {Number} width
14120      * @param {Number} height
14121      * @return {Roo.BasicDialog} this
14122      */
14123     setContentSize : function(w, h){
14124         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14125         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14126         //if(!this.el.isBorderBox()){
14127             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14128             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14129         //}
14130         if(this.tabs){
14131             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14132             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14133         }
14134         this.resizeTo(w, h);
14135         return this;
14136     },
14137
14138     /**
14139      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14140      * executed in response to a particular key being pressed while the dialog is active.
14141      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14142      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14143      * @param {Function} fn The function to call
14144      * @param {Object} scope (optional) The scope of the function
14145      * @return {Roo.BasicDialog} this
14146      */
14147     addKeyListener : function(key, fn, scope){
14148         var keyCode, shift, ctrl, alt;
14149         if(typeof key == "object" && !(key instanceof Array)){
14150             keyCode = key["key"];
14151             shift = key["shift"];
14152             ctrl = key["ctrl"];
14153             alt = key["alt"];
14154         }else{
14155             keyCode = key;
14156         }
14157         var handler = function(dlg, e){
14158             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14159                 var k = e.getKey();
14160                 if(keyCode instanceof Array){
14161                     for(var i = 0, len = keyCode.length; i < len; i++){
14162                         if(keyCode[i] == k){
14163                           fn.call(scope || window, dlg, k, e);
14164                           return;
14165                         }
14166                     }
14167                 }else{
14168                     if(k == keyCode){
14169                         fn.call(scope || window, dlg, k, e);
14170                     }
14171                 }
14172             }
14173         };
14174         this.on("keydown", handler);
14175         return this;
14176     },
14177
14178     /**
14179      * Returns the TabPanel component (creates it if it doesn't exist).
14180      * Note: If you wish to simply check for the existence of tabs without creating them,
14181      * check for a null 'tabs' property.
14182      * @return {Roo.TabPanel} The tabs component
14183      */
14184     getTabs : function(){
14185         if(!this.tabs){
14186             this.el.addClass("x-dlg-auto-tabs");
14187             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14188             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14189         }
14190         return this.tabs;
14191     },
14192
14193     /**
14194      * Adds a button to the footer section of the dialog.
14195      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14196      * object or a valid Roo.DomHelper element config
14197      * @param {Function} handler The function called when the button is clicked
14198      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14199      * @return {Roo.Button} The new button
14200      */
14201     addButton : function(config, handler, scope){
14202         var dh = Roo.DomHelper;
14203         if(!this.footer){
14204             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14205         }
14206         if(!this.btnContainer){
14207             var tb = this.footer.createChild({
14208
14209                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14210                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14211             }, null, true);
14212             this.btnContainer = tb.firstChild.firstChild.firstChild;
14213         }
14214         var bconfig = {
14215             handler: handler,
14216             scope: scope,
14217             minWidth: this.minButtonWidth,
14218             hideParent:true
14219         };
14220         if(typeof config == "string"){
14221             bconfig.text = config;
14222         }else{
14223             if(config.tag){
14224                 bconfig.dhconfig = config;
14225             }else{
14226                 Roo.apply(bconfig, config);
14227             }
14228         }
14229         var fc = false;
14230         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14231             bconfig.position = Math.max(0, bconfig.position);
14232             fc = this.btnContainer.childNodes[bconfig.position];
14233         }
14234          
14235         var btn = new Roo.Button(
14236             fc ? 
14237                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14238                 : this.btnContainer.appendChild(document.createElement("td")),
14239             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14240             bconfig
14241         );
14242         this.syncBodyHeight();
14243         if(!this.buttons){
14244             /**
14245              * Array of all the buttons that have been added to this dialog via addButton
14246              * @type Array
14247              */
14248             this.buttons = [];
14249         }
14250         this.buttons.push(btn);
14251         return btn;
14252     },
14253
14254     /**
14255      * Sets the default button to be focused when the dialog is displayed.
14256      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14257      * @return {Roo.BasicDialog} this
14258      */
14259     setDefaultButton : function(btn){
14260         this.defaultButton = btn;
14261         return this;
14262     },
14263
14264     // private
14265     getHeaderFooterHeight : function(safe){
14266         var height = 0;
14267         if(this.header){
14268            height += this.header.getHeight();
14269         }
14270         if(this.footer){
14271            var fm = this.footer.getMargins();
14272             height += (this.footer.getHeight()+fm.top+fm.bottom);
14273         }
14274         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14275         height += this.centerBg.getPadding("tb");
14276         return height;
14277     },
14278
14279     // private
14280     syncBodyHeight : function()
14281     {
14282         var bd = this.body, // the text
14283             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14284             bw = this.bwrap;
14285         var height = this.size.height - this.getHeaderFooterHeight(false);
14286         bd.setHeight(height-bd.getMargins("tb"));
14287         var hh = this.header.getHeight();
14288         var h = this.size.height-hh;
14289         cb.setHeight(h);
14290         
14291         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14292         bw.setHeight(h-cb.getPadding("tb"));
14293         
14294         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14295         bd.setWidth(bw.getWidth(true));
14296         if(this.tabs){
14297             this.tabs.syncHeight();
14298             if(Roo.isIE){
14299                 this.tabs.el.repaint();
14300             }
14301         }
14302     },
14303
14304     /**
14305      * Restores the previous state of the dialog if Roo.state is configured.
14306      * @return {Roo.BasicDialog} this
14307      */
14308     restoreState : function(){
14309         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14310         if(box && box.width){
14311             this.xy = [box.x, box.y];
14312             this.resizeTo(box.width, box.height);
14313         }
14314         return this;
14315     },
14316
14317     // private
14318     beforeShow : function(){
14319         this.expand();
14320         if(this.fixedcenter){
14321             this.xy = this.el.getCenterXY(true);
14322         }
14323         if(this.modal){
14324             Roo.get(document.body).addClass("x-body-masked");
14325             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14326             this.mask.show();
14327         }
14328         this.constrainXY();
14329     },
14330
14331     // private
14332     animShow : function(){
14333         var b = Roo.get(this.animateTarget).getBox();
14334         this.proxy.setSize(b.width, b.height);
14335         this.proxy.setLocation(b.x, b.y);
14336         this.proxy.show();
14337         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14338                     true, .35, this.showEl.createDelegate(this));
14339     },
14340
14341     /**
14342      * Shows the dialog.
14343      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14344      * @return {Roo.BasicDialog} this
14345      */
14346     show : function(animateTarget){
14347         if (this.fireEvent("beforeshow", this) === false){
14348             return;
14349         }
14350         if(this.syncHeightBeforeShow){
14351             this.syncBodyHeight();
14352         }else if(this.firstShow){
14353             this.firstShow = false;
14354             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14355         }
14356         this.animateTarget = animateTarget || this.animateTarget;
14357         if(!this.el.isVisible()){
14358             this.beforeShow();
14359             if(this.animateTarget && Roo.get(this.animateTarget)){
14360                 this.animShow();
14361             }else{
14362                 this.showEl();
14363             }
14364         }
14365         return this;
14366     },
14367
14368     // private
14369     showEl : function(){
14370         this.proxy.hide();
14371         this.el.setXY(this.xy);
14372         this.el.show();
14373         this.adjustAssets(true);
14374         this.toFront();
14375         this.focus();
14376         // IE peekaboo bug - fix found by Dave Fenwick
14377         if(Roo.isIE){
14378             this.el.repaint();
14379         }
14380         this.fireEvent("show", this);
14381     },
14382
14383     /**
14384      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14385      * dialog itself will receive focus.
14386      */
14387     focus : function(){
14388         if(this.defaultButton){
14389             this.defaultButton.focus();
14390         }else{
14391             this.focusEl.focus();
14392         }
14393     },
14394
14395     // private
14396     constrainXY : function(){
14397         if(this.constraintoviewport !== false){
14398             if(!this.viewSize){
14399                 if(this.container){
14400                     var s = this.container.getSize();
14401                     this.viewSize = [s.width, s.height];
14402                 }else{
14403                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14404                 }
14405             }
14406             var s = Roo.get(this.container||document).getScroll();
14407
14408             var x = this.xy[0], y = this.xy[1];
14409             var w = this.size.width, h = this.size.height;
14410             var vw = this.viewSize[0], vh = this.viewSize[1];
14411             // only move it if it needs it
14412             var moved = false;
14413             // first validate right/bottom
14414             if(x + w > vw+s.left){
14415                 x = vw - w;
14416                 moved = true;
14417             }
14418             if(y + h > vh+s.top){
14419                 y = vh - h;
14420                 moved = true;
14421             }
14422             // then make sure top/left isn't negative
14423             if(x < s.left){
14424                 x = s.left;
14425                 moved = true;
14426             }
14427             if(y < s.top){
14428                 y = s.top;
14429                 moved = true;
14430             }
14431             if(moved){
14432                 // cache xy
14433                 this.xy = [x, y];
14434                 if(this.isVisible()){
14435                     this.el.setLocation(x, y);
14436                     this.adjustAssets();
14437                 }
14438             }
14439         }
14440     },
14441
14442     // private
14443     onDrag : function(){
14444         if(!this.proxyDrag){
14445             this.xy = this.el.getXY();
14446             this.adjustAssets();
14447         }
14448     },
14449
14450     // private
14451     adjustAssets : function(doShow){
14452         var x = this.xy[0], y = this.xy[1];
14453         var w = this.size.width, h = this.size.height;
14454         if(doShow === true){
14455             if(this.shadow){
14456                 this.shadow.show(this.el);
14457             }
14458             if(this.shim){
14459                 this.shim.show();
14460             }
14461         }
14462         if(this.shadow && this.shadow.isVisible()){
14463             this.shadow.show(this.el);
14464         }
14465         if(this.shim && this.shim.isVisible()){
14466             this.shim.setBounds(x, y, w, h);
14467         }
14468     },
14469
14470     // private
14471     adjustViewport : function(w, h){
14472         if(!w || !h){
14473             w = Roo.lib.Dom.getViewWidth();
14474             h = Roo.lib.Dom.getViewHeight();
14475         }
14476         // cache the size
14477         this.viewSize = [w, h];
14478         if(this.modal && this.mask.isVisible()){
14479             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14480             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14481         }
14482         if(this.isVisible()){
14483             this.constrainXY();
14484         }
14485     },
14486
14487     /**
14488      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14489      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14490      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14491      */
14492     destroy : function(removeEl){
14493         if(this.isVisible()){
14494             this.animateTarget = null;
14495             this.hide();
14496         }
14497         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14498         if(this.tabs){
14499             this.tabs.destroy(removeEl);
14500         }
14501         Roo.destroy(
14502              this.shim,
14503              this.proxy,
14504              this.resizer,
14505              this.close,
14506              this.mask
14507         );
14508         if(this.dd){
14509             this.dd.unreg();
14510         }
14511         if(this.buttons){
14512            for(var i = 0, len = this.buttons.length; i < len; i++){
14513                this.buttons[i].destroy();
14514            }
14515         }
14516         this.el.removeAllListeners();
14517         if(removeEl === true){
14518             this.el.update("");
14519             this.el.remove();
14520         }
14521         Roo.DialogManager.unregister(this);
14522     },
14523
14524     // private
14525     startMove : function(){
14526         if(this.proxyDrag){
14527             this.proxy.show();
14528         }
14529         if(this.constraintoviewport !== false){
14530             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14531         }
14532     },
14533
14534     // private
14535     endMove : function(){
14536         if(!this.proxyDrag){
14537             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14538         }else{
14539             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14540             this.proxy.hide();
14541         }
14542         this.refreshSize();
14543         this.adjustAssets();
14544         this.focus();
14545         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14546     },
14547
14548     /**
14549      * Brings this dialog to the front of any other visible dialogs
14550      * @return {Roo.BasicDialog} this
14551      */
14552     toFront : function(){
14553         Roo.DialogManager.bringToFront(this);
14554         return this;
14555     },
14556
14557     /**
14558      * Sends this dialog to the back (under) of any other visible dialogs
14559      * @return {Roo.BasicDialog} this
14560      */
14561     toBack : function(){
14562         Roo.DialogManager.sendToBack(this);
14563         return this;
14564     },
14565
14566     /**
14567      * Centers this dialog in the viewport
14568      * @return {Roo.BasicDialog} this
14569      */
14570     center : function(){
14571         var xy = this.el.getCenterXY(true);
14572         this.moveTo(xy[0], xy[1]);
14573         return this;
14574     },
14575
14576     /**
14577      * Moves the dialog's top-left corner to the specified point
14578      * @param {Number} x
14579      * @param {Number} y
14580      * @return {Roo.BasicDialog} this
14581      */
14582     moveTo : function(x, y){
14583         this.xy = [x,y];
14584         if(this.isVisible()){
14585             this.el.setXY(this.xy);
14586             this.adjustAssets();
14587         }
14588         return this;
14589     },
14590
14591     /**
14592      * Aligns the dialog to the specified element
14593      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14594      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14595      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14596      * @return {Roo.BasicDialog} this
14597      */
14598     alignTo : function(element, position, offsets){
14599         this.xy = this.el.getAlignToXY(element, position, offsets);
14600         if(this.isVisible()){
14601             this.el.setXY(this.xy);
14602             this.adjustAssets();
14603         }
14604         return this;
14605     },
14606
14607     /**
14608      * Anchors an element to another element and realigns it when the window is resized.
14609      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14610      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14611      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14612      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14613      * is a number, it is used as the buffer delay (defaults to 50ms).
14614      * @return {Roo.BasicDialog} this
14615      */
14616     anchorTo : function(el, alignment, offsets, monitorScroll){
14617         var action = function(){
14618             this.alignTo(el, alignment, offsets);
14619         };
14620         Roo.EventManager.onWindowResize(action, this);
14621         var tm = typeof monitorScroll;
14622         if(tm != 'undefined'){
14623             Roo.EventManager.on(window, 'scroll', action, this,
14624                 {buffer: tm == 'number' ? monitorScroll : 50});
14625         }
14626         action.call(this);
14627         return this;
14628     },
14629
14630     /**
14631      * Returns true if the dialog is visible
14632      * @return {Boolean}
14633      */
14634     isVisible : function(){
14635         return this.el.isVisible();
14636     },
14637
14638     // private
14639     animHide : function(callback){
14640         var b = Roo.get(this.animateTarget).getBox();
14641         this.proxy.show();
14642         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14643         this.el.hide();
14644         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14645                     this.hideEl.createDelegate(this, [callback]));
14646     },
14647
14648     /**
14649      * Hides the dialog.
14650      * @param {Function} callback (optional) Function to call when the dialog is hidden
14651      * @return {Roo.BasicDialog} this
14652      */
14653     hide : function(callback){
14654         if (this.fireEvent("beforehide", this) === false){
14655             return;
14656         }
14657         if(this.shadow){
14658             this.shadow.hide();
14659         }
14660         if(this.shim) {
14661           this.shim.hide();
14662         }
14663         // sometimes animateTarget seems to get set.. causing problems...
14664         // this just double checks..
14665         if(this.animateTarget && Roo.get(this.animateTarget)) {
14666            this.animHide(callback);
14667         }else{
14668             this.el.hide();
14669             this.hideEl(callback);
14670         }
14671         return this;
14672     },
14673
14674     // private
14675     hideEl : function(callback){
14676         this.proxy.hide();
14677         if(this.modal){
14678             this.mask.hide();
14679             Roo.get(document.body).removeClass("x-body-masked");
14680         }
14681         this.fireEvent("hide", this);
14682         if(typeof callback == "function"){
14683             callback();
14684         }
14685     },
14686
14687     // private
14688     hideAction : function(){
14689         this.setLeft("-10000px");
14690         this.setTop("-10000px");
14691         this.setStyle("visibility", "hidden");
14692     },
14693
14694     // private
14695     refreshSize : function(){
14696         this.size = this.el.getSize();
14697         this.xy = this.el.getXY();
14698         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14699     },
14700
14701     // private
14702     // z-index is managed by the DialogManager and may be overwritten at any time
14703     setZIndex : function(index){
14704         if(this.modal){
14705             this.mask.setStyle("z-index", index);
14706         }
14707         if(this.shim){
14708             this.shim.setStyle("z-index", ++index);
14709         }
14710         if(this.shadow){
14711             this.shadow.setZIndex(++index);
14712         }
14713         this.el.setStyle("z-index", ++index);
14714         if(this.proxy){
14715             this.proxy.setStyle("z-index", ++index);
14716         }
14717         if(this.resizer){
14718             this.resizer.proxy.setStyle("z-index", ++index);
14719         }
14720
14721         this.lastZIndex = index;
14722     },
14723
14724     /**
14725      * Returns the element for this dialog
14726      * @return {Roo.Element} The underlying dialog Element
14727      */
14728     getEl : function(){
14729         return this.el;
14730     }
14731 });
14732
14733 /**
14734  * @class Roo.DialogManager
14735  * Provides global access to BasicDialogs that have been created and
14736  * support for z-indexing (layering) multiple open dialogs.
14737  */
14738 Roo.DialogManager = function(){
14739     var list = {};
14740     var accessList = [];
14741     var front = null;
14742
14743     // private
14744     var sortDialogs = function(d1, d2){
14745         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14746     };
14747
14748     // private
14749     var orderDialogs = function(){
14750         accessList.sort(sortDialogs);
14751         var seed = Roo.DialogManager.zseed;
14752         for(var i = 0, len = accessList.length; i < len; i++){
14753             var dlg = accessList[i];
14754             if(dlg){
14755                 dlg.setZIndex(seed + (i*10));
14756             }
14757         }
14758     };
14759
14760     return {
14761         /**
14762          * The starting z-index for BasicDialogs (defaults to 9000)
14763          * @type Number The z-index value
14764          */
14765         zseed : 9000,
14766
14767         // private
14768         register : function(dlg){
14769             list[dlg.id] = dlg;
14770             accessList.push(dlg);
14771         },
14772
14773         // private
14774         unregister : function(dlg){
14775             delete list[dlg.id];
14776             var i=0;
14777             var len=0;
14778             if(!accessList.indexOf){
14779                 for(  i = 0, len = accessList.length; i < len; i++){
14780                     if(accessList[i] == dlg){
14781                         accessList.splice(i, 1);
14782                         return;
14783                     }
14784                 }
14785             }else{
14786                  i = accessList.indexOf(dlg);
14787                 if(i != -1){
14788                     accessList.splice(i, 1);
14789                 }
14790             }
14791         },
14792
14793         /**
14794          * Gets a registered dialog by id
14795          * @param {String/Object} id The id of the dialog or a dialog
14796          * @return {Roo.BasicDialog} this
14797          */
14798         get : function(id){
14799             return typeof id == "object" ? id : list[id];
14800         },
14801
14802         /**
14803          * Brings the specified dialog to the front
14804          * @param {String/Object} dlg The id of the dialog or a dialog
14805          * @return {Roo.BasicDialog} this
14806          */
14807         bringToFront : function(dlg){
14808             dlg = this.get(dlg);
14809             if(dlg != front){
14810                 front = dlg;
14811                 dlg._lastAccess = new Date().getTime();
14812                 orderDialogs();
14813             }
14814             return dlg;
14815         },
14816
14817         /**
14818          * Sends the specified dialog to the back
14819          * @param {String/Object} dlg The id of the dialog or a dialog
14820          * @return {Roo.BasicDialog} this
14821          */
14822         sendToBack : function(dlg){
14823             dlg = this.get(dlg);
14824             dlg._lastAccess = -(new Date().getTime());
14825             orderDialogs();
14826             return dlg;
14827         },
14828
14829         /**
14830          * Hides all dialogs
14831          */
14832         hideAll : function(){
14833             for(var id in list){
14834                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
14835                     list[id].hide();
14836                 }
14837             }
14838         }
14839     };
14840 }();
14841
14842 /**
14843  * @class Roo.LayoutDialog
14844  * @extends Roo.BasicDialog
14845  * Dialog which provides adjustments for working with a layout in a Dialog.
14846  * Add your necessary layout config options to the dialog's config.<br>
14847  * Example usage (including a nested layout):
14848  * <pre><code>
14849 if(!dialog){
14850     dialog = new Roo.LayoutDialog("download-dlg", {
14851         modal: true,
14852         width:600,
14853         height:450,
14854         shadow:true,
14855         minWidth:500,
14856         minHeight:350,
14857         autoTabs:true,
14858         proxyDrag:true,
14859         // layout config merges with the dialog config
14860         center:{
14861             tabPosition: "top",
14862             alwaysShowTabs: true
14863         }
14864     });
14865     dialog.addKeyListener(27, dialog.hide, dialog);
14866     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
14867     dialog.addButton("Build It!", this.getDownload, this);
14868
14869     // we can even add nested layouts
14870     var innerLayout = new Roo.BorderLayout("dl-inner", {
14871         east: {
14872             initialSize: 200,
14873             autoScroll:true,
14874             split:true
14875         },
14876         center: {
14877             autoScroll:true
14878         }
14879     });
14880     innerLayout.beginUpdate();
14881     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
14882     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
14883     innerLayout.endUpdate(true);
14884
14885     var layout = dialog.getLayout();
14886     layout.beginUpdate();
14887     layout.add("center", new Roo.ContentPanel("standard-panel",
14888                         {title: "Download the Source", fitToFrame:true}));
14889     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
14890                {title: "Build your own roo.js"}));
14891     layout.getRegion("center").showPanel(sp);
14892     layout.endUpdate();
14893 }
14894 </code></pre>
14895     * @constructor
14896     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
14897     * @param {Object} config configuration options
14898   */
14899 Roo.LayoutDialog = function(el, cfg){
14900     
14901     var config=  cfg;
14902     if (typeof(cfg) == 'undefined') {
14903         config = Roo.apply({}, el);
14904         // not sure why we use documentElement here.. - it should always be body.
14905         // IE7 borks horribly if we use documentElement.
14906         // webkit also does not like documentElement - it creates a body element...
14907         el = Roo.get( document.body || document.documentElement ).createChild();
14908         //config.autoCreate = true;
14909     }
14910     
14911     
14912     config.autoTabs = false;
14913     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
14914     this.body.setStyle({overflow:"hidden", position:"relative"});
14915     this.layout = new Roo.BorderLayout(this.body.dom, config);
14916     this.layout.monitorWindowResize = false;
14917     this.el.addClass("x-dlg-auto-layout");
14918     // fix case when center region overwrites center function
14919     this.center = Roo.BasicDialog.prototype.center;
14920     this.on("show", this.layout.layout, this.layout, true);
14921     if (config.items) {
14922         var xitems = config.items;
14923         delete config.items;
14924         Roo.each(xitems, this.addxtype, this);
14925     }
14926     
14927     
14928 };
14929 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
14930     /**
14931      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
14932      * @deprecated
14933      */
14934     endUpdate : function(){
14935         this.layout.endUpdate();
14936     },
14937
14938     /**
14939      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
14940      *  @deprecated
14941      */
14942     beginUpdate : function(){
14943         this.layout.beginUpdate();
14944     },
14945
14946     /**
14947      * Get the BorderLayout for this dialog
14948      * @return {Roo.BorderLayout}
14949      */
14950     getLayout : function(){
14951         return this.layout;
14952     },
14953
14954     showEl : function(){
14955         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
14956         if(Roo.isIE7){
14957             this.layout.layout();
14958         }
14959     },
14960
14961     // private
14962     // Use the syncHeightBeforeShow config option to control this automatically
14963     syncBodyHeight : function(){
14964         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
14965         if(this.layout){this.layout.layout();}
14966     },
14967     
14968       /**
14969      * Add an xtype element (actually adds to the layout.)
14970      * @return {Object} xdata xtype object data.
14971      */
14972     
14973     addxtype : function(c) {
14974         return this.layout.addxtype(c);
14975     }
14976 });/*
14977  * Based on:
14978  * Ext JS Library 1.1.1
14979  * Copyright(c) 2006-2007, Ext JS, LLC.
14980  *
14981  * Originally Released Under LGPL - original licence link has changed is not relivant.
14982  *
14983  * Fork - LGPL
14984  * <script type="text/javascript">
14985  */
14986  
14987 /**
14988  * @class Roo.MessageBox
14989  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
14990  * Example usage:
14991  *<pre><code>
14992 // Basic alert:
14993 Roo.Msg.alert('Status', 'Changes saved successfully.');
14994
14995 // Prompt for user data:
14996 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
14997     if (btn == 'ok'){
14998         // process text value...
14999     }
15000 });
15001
15002 // Show a dialog using config options:
15003 Roo.Msg.show({
15004    title:'Save Changes?',
15005    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15006    buttons: Roo.Msg.YESNOCANCEL,
15007    fn: processResult,
15008    animEl: 'elId'
15009 });
15010 </code></pre>
15011  * @singleton
15012  */
15013 Roo.MessageBox = function(){
15014     var dlg, opt, mask, waitTimer;
15015     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15016     var buttons, activeTextEl, bwidth;
15017
15018     // private
15019     var handleButton = function(button){
15020         dlg.hide();
15021         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15022     };
15023
15024     // private
15025     var handleHide = function(){
15026         if(opt && opt.cls){
15027             dlg.el.removeClass(opt.cls);
15028         }
15029         if(waitTimer){
15030             Roo.TaskMgr.stop(waitTimer);
15031             waitTimer = null;
15032         }
15033     };
15034
15035     // private
15036     var updateButtons = function(b){
15037         var width = 0;
15038         if(!b){
15039             buttons["ok"].hide();
15040             buttons["cancel"].hide();
15041             buttons["yes"].hide();
15042             buttons["no"].hide();
15043             dlg.footer.dom.style.display = 'none';
15044             return width;
15045         }
15046         dlg.footer.dom.style.display = '';
15047         for(var k in buttons){
15048             if(typeof buttons[k] != "function"){
15049                 if(b[k]){
15050                     buttons[k].show();
15051                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15052                     width += buttons[k].el.getWidth()+15;
15053                 }else{
15054                     buttons[k].hide();
15055                 }
15056             }
15057         }
15058         return width;
15059     };
15060
15061     // private
15062     var handleEsc = function(d, k, e){
15063         if(opt && opt.closable !== false){
15064             dlg.hide();
15065         }
15066         if(e){
15067             e.stopEvent();
15068         }
15069     };
15070
15071     return {
15072         /**
15073          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15074          * @return {Roo.BasicDialog} The BasicDialog element
15075          */
15076         getDialog : function(){
15077            if(!dlg){
15078                 dlg = new Roo.BasicDialog("x-msg-box", {
15079                     autoCreate : true,
15080                     shadow: true,
15081                     draggable: true,
15082                     resizable:false,
15083                     constraintoviewport:false,
15084                     fixedcenter:true,
15085                     collapsible : false,
15086                     shim:true,
15087                     modal: true,
15088                     width:400, height:100,
15089                     buttonAlign:"center",
15090                     closeClick : function(){
15091                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15092                             handleButton("no");
15093                         }else{
15094                             handleButton("cancel");
15095                         }
15096                     }
15097                 });
15098                 dlg.on("hide", handleHide);
15099                 mask = dlg.mask;
15100                 dlg.addKeyListener(27, handleEsc);
15101                 buttons = {};
15102                 var bt = this.buttonText;
15103                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15104                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15105                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15106                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15107                 bodyEl = dlg.body.createChild({
15108
15109                     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>'
15110                 });
15111                 msgEl = bodyEl.dom.firstChild;
15112                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15113                 textboxEl.enableDisplayMode();
15114                 textboxEl.addKeyListener([10,13], function(){
15115                     if(dlg.isVisible() && opt && opt.buttons){
15116                         if(opt.buttons.ok){
15117                             handleButton("ok");
15118                         }else if(opt.buttons.yes){
15119                             handleButton("yes");
15120                         }
15121                     }
15122                 });
15123                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15124                 textareaEl.enableDisplayMode();
15125                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15126                 progressEl.enableDisplayMode();
15127                 var pf = progressEl.dom.firstChild;
15128                 if (pf) {
15129                     pp = Roo.get(pf.firstChild);
15130                     pp.setHeight(pf.offsetHeight);
15131                 }
15132                 
15133             }
15134             return dlg;
15135         },
15136
15137         /**
15138          * Updates the message box body text
15139          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15140          * the XHTML-compliant non-breaking space character '&amp;#160;')
15141          * @return {Roo.MessageBox} This message box
15142          */
15143         updateText : function(text){
15144             if(!dlg.isVisible() && !opt.width){
15145                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15146             }
15147             msgEl.innerHTML = text || '&#160;';
15148       
15149             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15150             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15151             var w = Math.max(
15152                     Math.min(opt.width || cw , this.maxWidth), 
15153                     Math.max(opt.minWidth || this.minWidth, bwidth)
15154             );
15155             if(opt.prompt){
15156                 activeTextEl.setWidth(w);
15157             }
15158             if(dlg.isVisible()){
15159                 dlg.fixedcenter = false;
15160             }
15161             // to big, make it scroll. = But as usual stupid IE does not support
15162             // !important..
15163             
15164             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15165                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15166                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15167             } else {
15168                 bodyEl.dom.style.height = '';
15169                 bodyEl.dom.style.overflowY = '';
15170             }
15171             if (cw > w) {
15172                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15173             } else {
15174                 bodyEl.dom.style.overflowX = '';
15175             }
15176             
15177             dlg.setContentSize(w, bodyEl.getHeight());
15178             if(dlg.isVisible()){
15179                 dlg.fixedcenter = true;
15180             }
15181             return this;
15182         },
15183
15184         /**
15185          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15186          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15187          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15188          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15189          * @return {Roo.MessageBox} This message box
15190          */
15191         updateProgress : function(value, text){
15192             if(text){
15193                 this.updateText(text);
15194             }
15195             if (pp) { // weird bug on my firefox - for some reason this is not defined
15196                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15197             }
15198             return this;
15199         },        
15200
15201         /**
15202          * Returns true if the message box is currently displayed
15203          * @return {Boolean} True if the message box is visible, else false
15204          */
15205         isVisible : function(){
15206             return dlg && dlg.isVisible();  
15207         },
15208
15209         /**
15210          * Hides the message box if it is displayed
15211          */
15212         hide : function(){
15213             if(this.isVisible()){
15214                 dlg.hide();
15215             }  
15216         },
15217
15218         /**
15219          * Displays a new message box, or reinitializes an existing message box, based on the config options
15220          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15221          * The following config object properties are supported:
15222          * <pre>
15223 Property    Type             Description
15224 ----------  ---------------  ------------------------------------------------------------------------------------
15225 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15226                                    closes (defaults to undefined)
15227 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15228                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15229 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15230                                    progress and wait dialogs will ignore this property and always hide the
15231                                    close button as they can only be closed programmatically.
15232 cls               String           A custom CSS class to apply to the message box element
15233 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15234                                    displayed (defaults to 75)
15235 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15236                                    function will be btn (the name of the button that was clicked, if applicable,
15237                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15238                                    Progress and wait dialogs will ignore this option since they do not respond to
15239                                    user actions and can only be closed programmatically, so any required function
15240                                    should be called by the same code after it closes the dialog.
15241 icon              String           A CSS class that provides a background image to be used as an icon for
15242                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15243 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15244 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15245 modal             Boolean          False to allow user interaction with the page while the message box is
15246                                    displayed (defaults to true)
15247 msg               String           A string that will replace the existing message box body text (defaults
15248                                    to the XHTML-compliant non-breaking space character '&#160;')
15249 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15250 progress          Boolean          True to display a progress bar (defaults to false)
15251 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15252 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15253 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15254 title             String           The title text
15255 value             String           The string value to set into the active textbox element if displayed
15256 wait              Boolean          True to display a progress bar (defaults to false)
15257 width             Number           The width of the dialog in pixels
15258 </pre>
15259          *
15260          * Example usage:
15261          * <pre><code>
15262 Roo.Msg.show({
15263    title: 'Address',
15264    msg: 'Please enter your address:',
15265    width: 300,
15266    buttons: Roo.MessageBox.OKCANCEL,
15267    multiline: true,
15268    fn: saveAddress,
15269    animEl: 'addAddressBtn'
15270 });
15271 </code></pre>
15272          * @param {Object} config Configuration options
15273          * @return {Roo.MessageBox} This message box
15274          */
15275         show : function(options)
15276         {
15277             
15278             // this causes nightmares if you show one dialog after another
15279             // especially on callbacks..
15280              
15281             if(this.isVisible()){
15282                 
15283                 this.hide();
15284                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15285                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15286                 Roo.log("New Dialog Message:" +  options.msg )
15287                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15288                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15289                 
15290             }
15291             var d = this.getDialog();
15292             opt = options;
15293             d.setTitle(opt.title || "&#160;");
15294             d.close.setDisplayed(opt.closable !== false);
15295             activeTextEl = textboxEl;
15296             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15297             if(opt.prompt){
15298                 if(opt.multiline){
15299                     textboxEl.hide();
15300                     textareaEl.show();
15301                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15302                         opt.multiline : this.defaultTextHeight);
15303                     activeTextEl = textareaEl;
15304                 }else{
15305                     textboxEl.show();
15306                     textareaEl.hide();
15307                 }
15308             }else{
15309                 textboxEl.hide();
15310                 textareaEl.hide();
15311             }
15312             progressEl.setDisplayed(opt.progress === true);
15313             this.updateProgress(0);
15314             activeTextEl.dom.value = opt.value || "";
15315             if(opt.prompt){
15316                 dlg.setDefaultButton(activeTextEl);
15317             }else{
15318                 var bs = opt.buttons;
15319                 var db = null;
15320                 if(bs && bs.ok){
15321                     db = buttons["ok"];
15322                 }else if(bs && bs.yes){
15323                     db = buttons["yes"];
15324                 }
15325                 dlg.setDefaultButton(db);
15326             }
15327             bwidth = updateButtons(opt.buttons);
15328             this.updateText(opt.msg);
15329             if(opt.cls){
15330                 d.el.addClass(opt.cls);
15331             }
15332             d.proxyDrag = opt.proxyDrag === true;
15333             d.modal = opt.modal !== false;
15334             d.mask = opt.modal !== false ? mask : false;
15335             if(!d.isVisible()){
15336                 // force it to the end of the z-index stack so it gets a cursor in FF
15337                 document.body.appendChild(dlg.el.dom);
15338                 d.animateTarget = null;
15339                 d.show(options.animEl);
15340             }
15341             return this;
15342         },
15343
15344         /**
15345          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15346          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15347          * and closing the message box when the process is complete.
15348          * @param {String} title The title bar text
15349          * @param {String} msg The message box body text
15350          * @return {Roo.MessageBox} This message box
15351          */
15352         progress : function(title, msg){
15353             this.show({
15354                 title : title,
15355                 msg : msg,
15356                 buttons: false,
15357                 progress:true,
15358                 closable:false,
15359                 minWidth: this.minProgressWidth,
15360                 modal : true
15361             });
15362             return this;
15363         },
15364
15365         /**
15366          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15367          * If a callback function is passed it will be called after the user clicks the button, and the
15368          * id of the button that was clicked will be passed as the only parameter to the callback
15369          * (could also be the top-right close button).
15370          * @param {String} title The title bar text
15371          * @param {String} msg The message box body text
15372          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15373          * @param {Object} scope (optional) The scope of the callback function
15374          * @return {Roo.MessageBox} This message box
15375          */
15376         alert : function(title, msg, fn, scope){
15377             this.show({
15378                 title : title,
15379                 msg : msg,
15380                 buttons: this.OK,
15381                 fn: fn,
15382                 scope : scope,
15383                 modal : true
15384             });
15385             return this;
15386         },
15387
15388         /**
15389          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15390          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15391          * You are responsible for closing the message box when the process is complete.
15392          * @param {String} msg The message box body text
15393          * @param {String} title (optional) The title bar text
15394          * @return {Roo.MessageBox} This message box
15395          */
15396         wait : function(msg, title){
15397             this.show({
15398                 title : title,
15399                 msg : msg,
15400                 buttons: false,
15401                 closable:false,
15402                 progress:true,
15403                 modal:true,
15404                 width:300,
15405                 wait:true
15406             });
15407             waitTimer = Roo.TaskMgr.start({
15408                 run: function(i){
15409                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15410                 },
15411                 interval: 1000
15412             });
15413             return this;
15414         },
15415
15416         /**
15417          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15418          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15419          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15420          * @param {String} title The title bar text
15421          * @param {String} msg The message box body text
15422          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15423          * @param {Object} scope (optional) The scope of the callback function
15424          * @return {Roo.MessageBox} This message box
15425          */
15426         confirm : function(title, msg, fn, scope){
15427             this.show({
15428                 title : title,
15429                 msg : msg,
15430                 buttons: this.YESNO,
15431                 fn: fn,
15432                 scope : scope,
15433                 modal : true
15434             });
15435             return this;
15436         },
15437
15438         /**
15439          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15440          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15441          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15442          * (could also be the top-right close button) and the text that was entered will be passed as the two
15443          * parameters to the callback.
15444          * @param {String} title The title bar text
15445          * @param {String} msg The message box body text
15446          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15447          * @param {Object} scope (optional) The scope of the callback function
15448          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15449          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15450          * @return {Roo.MessageBox} This message box
15451          */
15452         prompt : function(title, msg, fn, scope, multiline){
15453             this.show({
15454                 title : title,
15455                 msg : msg,
15456                 buttons: this.OKCANCEL,
15457                 fn: fn,
15458                 minWidth:250,
15459                 scope : scope,
15460                 prompt:true,
15461                 multiline: multiline,
15462                 modal : true
15463             });
15464             return this;
15465         },
15466
15467         /**
15468          * Button config that displays a single OK button
15469          * @type Object
15470          */
15471         OK : {ok:true},
15472         /**
15473          * Button config that displays Yes and No buttons
15474          * @type Object
15475          */
15476         YESNO : {yes:true, no:true},
15477         /**
15478          * Button config that displays OK and Cancel buttons
15479          * @type Object
15480          */
15481         OKCANCEL : {ok:true, cancel:true},
15482         /**
15483          * Button config that displays Yes, No and Cancel buttons
15484          * @type Object
15485          */
15486         YESNOCANCEL : {yes:true, no:true, cancel:true},
15487
15488         /**
15489          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15490          * @type Number
15491          */
15492         defaultTextHeight : 75,
15493         /**
15494          * The maximum width in pixels of the message box (defaults to 600)
15495          * @type Number
15496          */
15497         maxWidth : 600,
15498         /**
15499          * The minimum width in pixels of the message box (defaults to 100)
15500          * @type Number
15501          */
15502         minWidth : 100,
15503         /**
15504          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15505          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15506          * @type Number
15507          */
15508         minProgressWidth : 250,
15509         /**
15510          * An object containing the default button text strings that can be overriden for localized language support.
15511          * Supported properties are: ok, cancel, yes and no.
15512          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15513          * @type Object
15514          */
15515         buttonText : {
15516             ok : "OK",
15517             cancel : "Cancel",
15518             yes : "Yes",
15519             no : "No"
15520         }
15521     };
15522 }();
15523
15524 /**
15525  * Shorthand for {@link Roo.MessageBox}
15526  */
15527 Roo.Msg = Roo.MessageBox;/*
15528  * Based on:
15529  * Ext JS Library 1.1.1
15530  * Copyright(c) 2006-2007, Ext JS, LLC.
15531  *
15532  * Originally Released Under LGPL - original licence link has changed is not relivant.
15533  *
15534  * Fork - LGPL
15535  * <script type="text/javascript">
15536  */
15537 /**
15538  * @class Roo.QuickTips
15539  * Provides attractive and customizable tooltips for any element.
15540  * @singleton
15541  */
15542 Roo.QuickTips = function(){
15543     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15544     var ce, bd, xy, dd;
15545     var visible = false, disabled = true, inited = false;
15546     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15547     
15548     var onOver = function(e){
15549         if(disabled){
15550             return;
15551         }
15552         var t = e.getTarget();
15553         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15554             return;
15555         }
15556         if(ce && t == ce.el){
15557             clearTimeout(hideProc);
15558             return;
15559         }
15560         if(t && tagEls[t.id]){
15561             tagEls[t.id].el = t;
15562             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15563             return;
15564         }
15565         var ttp, et = Roo.fly(t);
15566         var ns = cfg.namespace;
15567         if(tm.interceptTitles && t.title){
15568             ttp = t.title;
15569             t.qtip = ttp;
15570             t.removeAttribute("title");
15571             e.preventDefault();
15572         }else{
15573             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15574         }
15575         if(ttp){
15576             showProc = show.defer(tm.showDelay, tm, [{
15577                 el: t, 
15578                 text: ttp, 
15579                 width: et.getAttributeNS(ns, cfg.width),
15580                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15581                 title: et.getAttributeNS(ns, cfg.title),
15582                     cls: et.getAttributeNS(ns, cfg.cls)
15583             }]);
15584         }
15585     };
15586     
15587     var onOut = function(e){
15588         clearTimeout(showProc);
15589         var t = e.getTarget();
15590         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15591             hideProc = setTimeout(hide, tm.hideDelay);
15592         }
15593     };
15594     
15595     var onMove = function(e){
15596         if(disabled){
15597             return;
15598         }
15599         xy = e.getXY();
15600         xy[1] += 18;
15601         if(tm.trackMouse && ce){
15602             el.setXY(xy);
15603         }
15604     };
15605     
15606     var onDown = function(e){
15607         clearTimeout(showProc);
15608         clearTimeout(hideProc);
15609         if(!e.within(el)){
15610             if(tm.hideOnClick){
15611                 hide();
15612                 tm.disable();
15613                 tm.enable.defer(100, tm);
15614             }
15615         }
15616     };
15617     
15618     var getPad = function(){
15619         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15620     };
15621
15622     var show = function(o){
15623         if(disabled){
15624             return;
15625         }
15626         clearTimeout(dismissProc);
15627         ce = o;
15628         if(removeCls){ // in case manually hidden
15629             el.removeClass(removeCls);
15630             removeCls = null;
15631         }
15632         if(ce.cls){
15633             el.addClass(ce.cls);
15634             removeCls = ce.cls;
15635         }
15636         if(ce.title){
15637             tipTitle.update(ce.title);
15638             tipTitle.show();
15639         }else{
15640             tipTitle.update('');
15641             tipTitle.hide();
15642         }
15643         el.dom.style.width  = tm.maxWidth+'px';
15644         //tipBody.dom.style.width = '';
15645         tipBodyText.update(o.text);
15646         var p = getPad(), w = ce.width;
15647         if(!w){
15648             var td = tipBodyText.dom;
15649             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15650             if(aw > tm.maxWidth){
15651                 w = tm.maxWidth;
15652             }else if(aw < tm.minWidth){
15653                 w = tm.minWidth;
15654             }else{
15655                 w = aw;
15656             }
15657         }
15658         //tipBody.setWidth(w);
15659         el.setWidth(parseInt(w, 10) + p);
15660         if(ce.autoHide === false){
15661             close.setDisplayed(true);
15662             if(dd){
15663                 dd.unlock();
15664             }
15665         }else{
15666             close.setDisplayed(false);
15667             if(dd){
15668                 dd.lock();
15669             }
15670         }
15671         if(xy){
15672             el.avoidY = xy[1]-18;
15673             el.setXY(xy);
15674         }
15675         if(tm.animate){
15676             el.setOpacity(.1);
15677             el.setStyle("visibility", "visible");
15678             el.fadeIn({callback: afterShow});
15679         }else{
15680             afterShow();
15681         }
15682     };
15683     
15684     var afterShow = function(){
15685         if(ce){
15686             el.show();
15687             esc.enable();
15688             if(tm.autoDismiss && ce.autoHide !== false){
15689                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15690             }
15691         }
15692     };
15693     
15694     var hide = function(noanim){
15695         clearTimeout(dismissProc);
15696         clearTimeout(hideProc);
15697         ce = null;
15698         if(el.isVisible()){
15699             esc.disable();
15700             if(noanim !== true && tm.animate){
15701                 el.fadeOut({callback: afterHide});
15702             }else{
15703                 afterHide();
15704             } 
15705         }
15706     };
15707     
15708     var afterHide = function(){
15709         el.hide();
15710         if(removeCls){
15711             el.removeClass(removeCls);
15712             removeCls = null;
15713         }
15714     };
15715     
15716     return {
15717         /**
15718         * @cfg {Number} minWidth
15719         * The minimum width of the quick tip (defaults to 40)
15720         */
15721        minWidth : 40,
15722         /**
15723         * @cfg {Number} maxWidth
15724         * The maximum width of the quick tip (defaults to 300)
15725         */
15726        maxWidth : 300,
15727         /**
15728         * @cfg {Boolean} interceptTitles
15729         * True to automatically use the element's DOM title value if available (defaults to false)
15730         */
15731        interceptTitles : false,
15732         /**
15733         * @cfg {Boolean} trackMouse
15734         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15735         */
15736        trackMouse : false,
15737         /**
15738         * @cfg {Boolean} hideOnClick
15739         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15740         */
15741        hideOnClick : true,
15742         /**
15743         * @cfg {Number} showDelay
15744         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15745         */
15746        showDelay : 500,
15747         /**
15748         * @cfg {Number} hideDelay
15749         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15750         */
15751        hideDelay : 200,
15752         /**
15753         * @cfg {Boolean} autoHide
15754         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15755         * Used in conjunction with hideDelay.
15756         */
15757        autoHide : true,
15758         /**
15759         * @cfg {Boolean}
15760         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15761         * (defaults to true).  Used in conjunction with autoDismissDelay.
15762         */
15763        autoDismiss : true,
15764         /**
15765         * @cfg {Number}
15766         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15767         */
15768        autoDismissDelay : 5000,
15769        /**
15770         * @cfg {Boolean} animate
15771         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15772         */
15773        animate : false,
15774
15775        /**
15776         * @cfg {String} title
15777         * Title text to display (defaults to '').  This can be any valid HTML markup.
15778         */
15779         title: '',
15780        /**
15781         * @cfg {String} text
15782         * Body text to display (defaults to '').  This can be any valid HTML markup.
15783         */
15784         text : '',
15785        /**
15786         * @cfg {String} cls
15787         * A CSS class to apply to the base quick tip element (defaults to '').
15788         */
15789         cls : '',
15790        /**
15791         * @cfg {Number} width
15792         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15793         * minWidth or maxWidth.
15794         */
15795         width : null,
15796
15797     /**
15798      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15799      * or display QuickTips in a page.
15800      */
15801        init : function(){
15802           tm = Roo.QuickTips;
15803           cfg = tm.tagConfig;
15804           if(!inited){
15805               if(!Roo.isReady){ // allow calling of init() before onReady
15806                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15807                   return;
15808               }
15809               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15810               el.fxDefaults = {stopFx: true};
15811               // maximum custom styling
15812               //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>');
15813               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>');              
15814               tipTitle = el.child('h3');
15815               tipTitle.enableDisplayMode("block");
15816               tipBody = el.child('div.x-tip-bd');
15817               tipBodyText = el.child('div.x-tip-bd-inner');
15818               //bdLeft = el.child('div.x-tip-bd-left');
15819               //bdRight = el.child('div.x-tip-bd-right');
15820               close = el.child('div.x-tip-close');
15821               close.enableDisplayMode("block");
15822               close.on("click", hide);
15823               var d = Roo.get(document);
15824               d.on("mousedown", onDown);
15825               d.on("mouseover", onOver);
15826               d.on("mouseout", onOut);
15827               d.on("mousemove", onMove);
15828               esc = d.addKeyListener(27, hide);
15829               esc.disable();
15830               if(Roo.dd.DD){
15831                   dd = el.initDD("default", null, {
15832                       onDrag : function(){
15833                           el.sync();  
15834                       }
15835                   });
15836                   dd.setHandleElId(tipTitle.id);
15837                   dd.lock();
15838               }
15839               inited = true;
15840           }
15841           this.enable(); 
15842        },
15843
15844     /**
15845      * Configures a new quick tip instance and assigns it to a target element.  The following config options
15846      * are supported:
15847      * <pre>
15848 Property    Type                   Description
15849 ----------  ---------------------  ------------------------------------------------------------------------
15850 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
15851      * </ul>
15852      * @param {Object} config The config object
15853      */
15854        register : function(config){
15855            var cs = config instanceof Array ? config : arguments;
15856            for(var i = 0, len = cs.length; i < len; i++) {
15857                var c = cs[i];
15858                var target = c.target;
15859                if(target){
15860                    if(target instanceof Array){
15861                        for(var j = 0, jlen = target.length; j < jlen; j++){
15862                            tagEls[target[j]] = c;
15863                        }
15864                    }else{
15865                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
15866                    }
15867                }
15868            }
15869        },
15870
15871     /**
15872      * Removes this quick tip from its element and destroys it.
15873      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
15874      */
15875        unregister : function(el){
15876            delete tagEls[Roo.id(el)];
15877        },
15878
15879     /**
15880      * Enable this quick tip.
15881      */
15882        enable : function(){
15883            if(inited && disabled){
15884                locks.pop();
15885                if(locks.length < 1){
15886                    disabled = false;
15887                }
15888            }
15889        },
15890
15891     /**
15892      * Disable this quick tip.
15893      */
15894        disable : function(){
15895           disabled = true;
15896           clearTimeout(showProc);
15897           clearTimeout(hideProc);
15898           clearTimeout(dismissProc);
15899           if(ce){
15900               hide(true);
15901           }
15902           locks.push(1);
15903        },
15904
15905     /**
15906      * Returns true if the quick tip is enabled, else false.
15907      */
15908        isEnabled : function(){
15909             return !disabled;
15910        },
15911
15912         // private
15913        tagConfig : {
15914            namespace : "ext",
15915            attribute : "qtip",
15916            width : "width",
15917            target : "target",
15918            title : "qtitle",
15919            hide : "hide",
15920            cls : "qclass"
15921        }
15922    };
15923 }();
15924
15925 // backwards compat
15926 Roo.QuickTips.tips = Roo.QuickTips.register;/*
15927  * Based on:
15928  * Ext JS Library 1.1.1
15929  * Copyright(c) 2006-2007, Ext JS, LLC.
15930  *
15931  * Originally Released Under LGPL - original licence link has changed is not relivant.
15932  *
15933  * Fork - LGPL
15934  * <script type="text/javascript">
15935  */
15936  
15937
15938 /**
15939  * @class Roo.tree.TreePanel
15940  * @extends Roo.data.Tree
15941
15942  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
15943  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
15944  * @cfg {Boolean} enableDD true to enable drag and drop
15945  * @cfg {Boolean} enableDrag true to enable just drag
15946  * @cfg {Boolean} enableDrop true to enable just drop
15947  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
15948  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
15949  * @cfg {String} ddGroup The DD group this TreePanel belongs to
15950  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
15951  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
15952  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
15953  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
15954  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
15955  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
15956  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
15957  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
15958  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
15959  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
15960  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
15961  * @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>
15962  * @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>
15963  * 
15964  * @constructor
15965  * @param {String/HTMLElement/Element} el The container element
15966  * @param {Object} config
15967  */
15968 Roo.tree.TreePanel = function(el, config){
15969     var root = false;
15970     var loader = false;
15971     if (config.root) {
15972         root = config.root;
15973         delete config.root;
15974     }
15975     if (config.loader) {
15976         loader = config.loader;
15977         delete config.loader;
15978     }
15979     
15980     Roo.apply(this, config);
15981     Roo.tree.TreePanel.superclass.constructor.call(this);
15982     this.el = Roo.get(el);
15983     this.el.addClass('x-tree');
15984     //console.log(root);
15985     if (root) {
15986         this.setRootNode( Roo.factory(root, Roo.tree));
15987     }
15988     if (loader) {
15989         this.loader = Roo.factory(loader, Roo.tree);
15990     }
15991    /**
15992     * Read-only. The id of the container element becomes this TreePanel's id.
15993     */
15994     this.id = this.el.id;
15995     this.addEvents({
15996         /**
15997         * @event beforeload
15998         * Fires before a node is loaded, return false to cancel
15999         * @param {Node} node The node being loaded
16000         */
16001         "beforeload" : true,
16002         /**
16003         * @event load
16004         * Fires when a node is loaded
16005         * @param {Node} node The node that was loaded
16006         */
16007         "load" : true,
16008         /**
16009         * @event textchange
16010         * Fires when the text for a node is changed
16011         * @param {Node} node The node
16012         * @param {String} text The new text
16013         * @param {String} oldText The old text
16014         */
16015         "textchange" : true,
16016         /**
16017         * @event beforeexpand
16018         * Fires before a node is expanded, return false to cancel.
16019         * @param {Node} node The node
16020         * @param {Boolean} deep
16021         * @param {Boolean} anim
16022         */
16023         "beforeexpand" : true,
16024         /**
16025         * @event beforecollapse
16026         * Fires before a node is collapsed, return false to cancel.
16027         * @param {Node} node The node
16028         * @param {Boolean} deep
16029         * @param {Boolean} anim
16030         */
16031         "beforecollapse" : true,
16032         /**
16033         * @event expand
16034         * Fires when a node is expanded
16035         * @param {Node} node The node
16036         */
16037         "expand" : true,
16038         /**
16039         * @event disabledchange
16040         * Fires when the disabled status of a node changes
16041         * @param {Node} node The node
16042         * @param {Boolean} disabled
16043         */
16044         "disabledchange" : true,
16045         /**
16046         * @event collapse
16047         * Fires when a node is collapsed
16048         * @param {Node} node The node
16049         */
16050         "collapse" : true,
16051         /**
16052         * @event beforeclick
16053         * Fires before click processing on a node. Return false to cancel the default action.
16054         * @param {Node} node The node
16055         * @param {Roo.EventObject} e The event object
16056         */
16057         "beforeclick":true,
16058         /**
16059         * @event checkchange
16060         * Fires when a node with a checkbox's checked property changes
16061         * @param {Node} this This node
16062         * @param {Boolean} checked
16063         */
16064         "checkchange":true,
16065         /**
16066         * @event click
16067         * Fires when a node is clicked
16068         * @param {Node} node The node
16069         * @param {Roo.EventObject} e The event object
16070         */
16071         "click":true,
16072         /**
16073         * @event dblclick
16074         * Fires when a node is double clicked
16075         * @param {Node} node The node
16076         * @param {Roo.EventObject} e The event object
16077         */
16078         "dblclick":true,
16079         /**
16080         * @event contextmenu
16081         * Fires when a node is right clicked
16082         * @param {Node} node The node
16083         * @param {Roo.EventObject} e The event object
16084         */
16085         "contextmenu":true,
16086         /**
16087         * @event beforechildrenrendered
16088         * Fires right before the child nodes for a node are rendered
16089         * @param {Node} node The node
16090         */
16091         "beforechildrenrendered":true,
16092         /**
16093         * @event startdrag
16094         * Fires when a node starts being dragged
16095         * @param {Roo.tree.TreePanel} this
16096         * @param {Roo.tree.TreeNode} node
16097         * @param {event} e The raw browser event
16098         */ 
16099        "startdrag" : true,
16100        /**
16101         * @event enddrag
16102         * Fires when a drag operation is complete
16103         * @param {Roo.tree.TreePanel} this
16104         * @param {Roo.tree.TreeNode} node
16105         * @param {event} e The raw browser event
16106         */
16107        "enddrag" : true,
16108        /**
16109         * @event dragdrop
16110         * Fires when a dragged node is dropped on a valid DD target
16111         * @param {Roo.tree.TreePanel} this
16112         * @param {Roo.tree.TreeNode} node
16113         * @param {DD} dd The dd it was dropped on
16114         * @param {event} e The raw browser event
16115         */
16116        "dragdrop" : true,
16117        /**
16118         * @event beforenodedrop
16119         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16120         * passed to handlers has the following properties:<br />
16121         * <ul style="padding:5px;padding-left:16px;">
16122         * <li>tree - The TreePanel</li>
16123         * <li>target - The node being targeted for the drop</li>
16124         * <li>data - The drag data from the drag source</li>
16125         * <li>point - The point of the drop - append, above or below</li>
16126         * <li>source - The drag source</li>
16127         * <li>rawEvent - Raw mouse event</li>
16128         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16129         * to be inserted by setting them on this object.</li>
16130         * <li>cancel - Set this to true to cancel the drop.</li>
16131         * </ul>
16132         * @param {Object} dropEvent
16133         */
16134        "beforenodedrop" : true,
16135        /**
16136         * @event nodedrop
16137         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16138         * passed to handlers has the following properties:<br />
16139         * <ul style="padding:5px;padding-left:16px;">
16140         * <li>tree - The TreePanel</li>
16141         * <li>target - The node being targeted for the drop</li>
16142         * <li>data - The drag data from the drag source</li>
16143         * <li>point - The point of the drop - append, above or below</li>
16144         * <li>source - The drag source</li>
16145         * <li>rawEvent - Raw mouse event</li>
16146         * <li>dropNode - Dropped node(s).</li>
16147         * </ul>
16148         * @param {Object} dropEvent
16149         */
16150        "nodedrop" : true,
16151         /**
16152         * @event nodedragover
16153         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16154         * passed to handlers has the following properties:<br />
16155         * <ul style="padding:5px;padding-left:16px;">
16156         * <li>tree - The TreePanel</li>
16157         * <li>target - The node being targeted for the drop</li>
16158         * <li>data - The drag data from the drag source</li>
16159         * <li>point - The point of the drop - append, above or below</li>
16160         * <li>source - The drag source</li>
16161         * <li>rawEvent - Raw mouse event</li>
16162         * <li>dropNode - Drop node(s) provided by the source.</li>
16163         * <li>cancel - Set this to true to signal drop not allowed.</li>
16164         * </ul>
16165         * @param {Object} dragOverEvent
16166         */
16167        "nodedragover" : true
16168         
16169     });
16170     if(this.singleExpand){
16171        this.on("beforeexpand", this.restrictExpand, this);
16172     }
16173     if (this.editor) {
16174         this.editor.tree = this;
16175         this.editor = Roo.factory(this.editor, Roo.tree);
16176     }
16177     
16178     if (this.selModel) {
16179         this.selModel = Roo.factory(this.selModel, Roo.tree);
16180     }
16181    
16182 };
16183 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16184     rootVisible : true,
16185     animate: Roo.enableFx,
16186     lines : true,
16187     enableDD : false,
16188     hlDrop : Roo.enableFx,
16189   
16190     renderer: false,
16191     
16192     rendererTip: false,
16193     // private
16194     restrictExpand : function(node){
16195         var p = node.parentNode;
16196         if(p){
16197             if(p.expandedChild && p.expandedChild.parentNode == p){
16198                 p.expandedChild.collapse();
16199             }
16200             p.expandedChild = node;
16201         }
16202     },
16203
16204     // private override
16205     setRootNode : function(node){
16206         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16207         if(!this.rootVisible){
16208             node.ui = new Roo.tree.RootTreeNodeUI(node);
16209         }
16210         return node;
16211     },
16212
16213     /**
16214      * Returns the container element for this TreePanel
16215      */
16216     getEl : function(){
16217         return this.el;
16218     },
16219
16220     /**
16221      * Returns the default TreeLoader for this TreePanel
16222      */
16223     getLoader : function(){
16224         return this.loader;
16225     },
16226
16227     /**
16228      * Expand all nodes
16229      */
16230     expandAll : function(){
16231         this.root.expand(true);
16232     },
16233
16234     /**
16235      * Collapse all nodes
16236      */
16237     collapseAll : function(){
16238         this.root.collapse(true);
16239     },
16240
16241     /**
16242      * Returns the selection model used by this TreePanel
16243      */
16244     getSelectionModel : function(){
16245         if(!this.selModel){
16246             this.selModel = new Roo.tree.DefaultSelectionModel();
16247         }
16248         return this.selModel;
16249     },
16250
16251     /**
16252      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16253      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16254      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16255      * @return {Array}
16256      */
16257     getChecked : function(a, startNode){
16258         startNode = startNode || this.root;
16259         var r = [];
16260         var f = function(){
16261             if(this.attributes.checked){
16262                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16263             }
16264         }
16265         startNode.cascade(f);
16266         return r;
16267     },
16268
16269     /**
16270      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16271      * @param {String} path
16272      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16273      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16274      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16275      */
16276     expandPath : function(path, attr, callback){
16277         attr = attr || "id";
16278         var keys = path.split(this.pathSeparator);
16279         var curNode = this.root;
16280         if(curNode.attributes[attr] != keys[1]){ // invalid root
16281             if(callback){
16282                 callback(false, null);
16283             }
16284             return;
16285         }
16286         var index = 1;
16287         var f = function(){
16288             if(++index == keys.length){
16289                 if(callback){
16290                     callback(true, curNode);
16291                 }
16292                 return;
16293             }
16294             var c = curNode.findChild(attr, keys[index]);
16295             if(!c){
16296                 if(callback){
16297                     callback(false, curNode);
16298                 }
16299                 return;
16300             }
16301             curNode = c;
16302             c.expand(false, false, f);
16303         };
16304         curNode.expand(false, false, f);
16305     },
16306
16307     /**
16308      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16309      * @param {String} path
16310      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16311      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16312      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16313      */
16314     selectPath : function(path, attr, callback){
16315         attr = attr || "id";
16316         var keys = path.split(this.pathSeparator);
16317         var v = keys.pop();
16318         if(keys.length > 0){
16319             var f = function(success, node){
16320                 if(success && node){
16321                     var n = node.findChild(attr, v);
16322                     if(n){
16323                         n.select();
16324                         if(callback){
16325                             callback(true, n);
16326                         }
16327                     }else if(callback){
16328                         callback(false, n);
16329                     }
16330                 }else{
16331                     if(callback){
16332                         callback(false, n);
16333                     }
16334                 }
16335             };
16336             this.expandPath(keys.join(this.pathSeparator), attr, f);
16337         }else{
16338             this.root.select();
16339             if(callback){
16340                 callback(true, this.root);
16341             }
16342         }
16343     },
16344
16345     getTreeEl : function(){
16346         return this.el;
16347     },
16348
16349     /**
16350      * Trigger rendering of this TreePanel
16351      */
16352     render : function(){
16353         if (this.innerCt) {
16354             return this; // stop it rendering more than once!!
16355         }
16356         
16357         this.innerCt = this.el.createChild({tag:"ul",
16358                cls:"x-tree-root-ct " +
16359                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16360
16361         if(this.containerScroll){
16362             Roo.dd.ScrollManager.register(this.el);
16363         }
16364         if((this.enableDD || this.enableDrop) && !this.dropZone){
16365            /**
16366             * The dropZone used by this tree if drop is enabled
16367             * @type Roo.tree.TreeDropZone
16368             */
16369              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16370                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16371            });
16372         }
16373         if((this.enableDD || this.enableDrag) && !this.dragZone){
16374            /**
16375             * The dragZone used by this tree if drag is enabled
16376             * @type Roo.tree.TreeDragZone
16377             */
16378             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16379                ddGroup: this.ddGroup || "TreeDD",
16380                scroll: this.ddScroll
16381            });
16382         }
16383         this.getSelectionModel().init(this);
16384         if (!this.root) {
16385             Roo.log("ROOT not set in tree");
16386             return this;
16387         }
16388         this.root.render();
16389         if(!this.rootVisible){
16390             this.root.renderChildren();
16391         }
16392         return this;
16393     }
16394 });/*
16395  * Based on:
16396  * Ext JS Library 1.1.1
16397  * Copyright(c) 2006-2007, Ext JS, LLC.
16398  *
16399  * Originally Released Under LGPL - original licence link has changed is not relivant.
16400  *
16401  * Fork - LGPL
16402  * <script type="text/javascript">
16403  */
16404  
16405
16406 /**
16407  * @class Roo.tree.DefaultSelectionModel
16408  * @extends Roo.util.Observable
16409  * The default single selection for a TreePanel.
16410  * @param {Object} cfg Configuration
16411  */
16412 Roo.tree.DefaultSelectionModel = function(cfg){
16413    this.selNode = null;
16414    
16415    
16416    
16417    this.addEvents({
16418        /**
16419         * @event selectionchange
16420         * Fires when the selected node changes
16421         * @param {DefaultSelectionModel} this
16422         * @param {TreeNode} node the new selection
16423         */
16424        "selectionchange" : true,
16425
16426        /**
16427         * @event beforeselect
16428         * Fires before the selected node changes, return false to cancel the change
16429         * @param {DefaultSelectionModel} this
16430         * @param {TreeNode} node the new selection
16431         * @param {TreeNode} node the old selection
16432         */
16433        "beforeselect" : true
16434    });
16435    
16436     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16437 };
16438
16439 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16440     init : function(tree){
16441         this.tree = tree;
16442         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16443         tree.on("click", this.onNodeClick, this);
16444     },
16445     
16446     onNodeClick : function(node, e){
16447         if (e.ctrlKey && this.selNode == node)  {
16448             this.unselect(node);
16449             return;
16450         }
16451         this.select(node);
16452     },
16453     
16454     /**
16455      * Select a node.
16456      * @param {TreeNode} node The node to select
16457      * @return {TreeNode} The selected node
16458      */
16459     select : function(node){
16460         var last = this.selNode;
16461         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16462             if(last){
16463                 last.ui.onSelectedChange(false);
16464             }
16465             this.selNode = node;
16466             node.ui.onSelectedChange(true);
16467             this.fireEvent("selectionchange", this, node, last);
16468         }
16469         return node;
16470     },
16471     
16472     /**
16473      * Deselect a node.
16474      * @param {TreeNode} node The node to unselect
16475      */
16476     unselect : function(node){
16477         if(this.selNode == node){
16478             this.clearSelections();
16479         }    
16480     },
16481     
16482     /**
16483      * Clear all selections
16484      */
16485     clearSelections : function(){
16486         var n = this.selNode;
16487         if(n){
16488             n.ui.onSelectedChange(false);
16489             this.selNode = null;
16490             this.fireEvent("selectionchange", this, null);
16491         }
16492         return n;
16493     },
16494     
16495     /**
16496      * Get the selected node
16497      * @return {TreeNode} The selected node
16498      */
16499     getSelectedNode : function(){
16500         return this.selNode;    
16501     },
16502     
16503     /**
16504      * Returns true if the node is selected
16505      * @param {TreeNode} node The node to check
16506      * @return {Boolean}
16507      */
16508     isSelected : function(node){
16509         return this.selNode == node;  
16510     },
16511
16512     /**
16513      * Selects the node above the selected node in the tree, intelligently walking the nodes
16514      * @return TreeNode The new selection
16515      */
16516     selectPrevious : function(){
16517         var s = this.selNode || this.lastSelNode;
16518         if(!s){
16519             return null;
16520         }
16521         var ps = s.previousSibling;
16522         if(ps){
16523             if(!ps.isExpanded() || ps.childNodes.length < 1){
16524                 return this.select(ps);
16525             } else{
16526                 var lc = ps.lastChild;
16527                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16528                     lc = lc.lastChild;
16529                 }
16530                 return this.select(lc);
16531             }
16532         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16533             return this.select(s.parentNode);
16534         }
16535         return null;
16536     },
16537
16538     /**
16539      * Selects the node above the selected node in the tree, intelligently walking the nodes
16540      * @return TreeNode The new selection
16541      */
16542     selectNext : function(){
16543         var s = this.selNode || this.lastSelNode;
16544         if(!s){
16545             return null;
16546         }
16547         if(s.firstChild && s.isExpanded()){
16548              return this.select(s.firstChild);
16549          }else if(s.nextSibling){
16550              return this.select(s.nextSibling);
16551          }else if(s.parentNode){
16552             var newS = null;
16553             s.parentNode.bubble(function(){
16554                 if(this.nextSibling){
16555                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16556                     return false;
16557                 }
16558             });
16559             return newS;
16560          }
16561         return null;
16562     },
16563
16564     onKeyDown : function(e){
16565         var s = this.selNode || this.lastSelNode;
16566         // undesirable, but required
16567         var sm = this;
16568         if(!s){
16569             return;
16570         }
16571         var k = e.getKey();
16572         switch(k){
16573              case e.DOWN:
16574                  e.stopEvent();
16575                  this.selectNext();
16576              break;
16577              case e.UP:
16578                  e.stopEvent();
16579                  this.selectPrevious();
16580              break;
16581              case e.RIGHT:
16582                  e.preventDefault();
16583                  if(s.hasChildNodes()){
16584                      if(!s.isExpanded()){
16585                          s.expand();
16586                      }else if(s.firstChild){
16587                          this.select(s.firstChild, e);
16588                      }
16589                  }
16590              break;
16591              case e.LEFT:
16592                  e.preventDefault();
16593                  if(s.hasChildNodes() && s.isExpanded()){
16594                      s.collapse();
16595                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16596                      this.select(s.parentNode, e);
16597                  }
16598              break;
16599         };
16600     }
16601 });
16602
16603 /**
16604  * @class Roo.tree.MultiSelectionModel
16605  * @extends Roo.util.Observable
16606  * Multi selection for a TreePanel.
16607  * @param {Object} cfg Configuration
16608  */
16609 Roo.tree.MultiSelectionModel = function(){
16610    this.selNodes = [];
16611    this.selMap = {};
16612    this.addEvents({
16613        /**
16614         * @event selectionchange
16615         * Fires when the selected nodes change
16616         * @param {MultiSelectionModel} this
16617         * @param {Array} nodes Array of the selected nodes
16618         */
16619        "selectionchange" : true
16620    });
16621    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
16622    
16623 };
16624
16625 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16626     init : function(tree){
16627         this.tree = tree;
16628         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16629         tree.on("click", this.onNodeClick, this);
16630     },
16631     
16632     onNodeClick : function(node, e){
16633         this.select(node, e, e.ctrlKey);
16634     },
16635     
16636     /**
16637      * Select a node.
16638      * @param {TreeNode} node The node to select
16639      * @param {EventObject} e (optional) An event associated with the selection
16640      * @param {Boolean} keepExisting True to retain existing selections
16641      * @return {TreeNode} The selected node
16642      */
16643     select : function(node, e, keepExisting){
16644         if(keepExisting !== true){
16645             this.clearSelections(true);
16646         }
16647         if(this.isSelected(node)){
16648             this.lastSelNode = node;
16649             return node;
16650         }
16651         this.selNodes.push(node);
16652         this.selMap[node.id] = node;
16653         this.lastSelNode = node;
16654         node.ui.onSelectedChange(true);
16655         this.fireEvent("selectionchange", this, this.selNodes);
16656         return node;
16657     },
16658     
16659     /**
16660      * Deselect a node.
16661      * @param {TreeNode} node The node to unselect
16662      */
16663     unselect : function(node){
16664         if(this.selMap[node.id]){
16665             node.ui.onSelectedChange(false);
16666             var sn = this.selNodes;
16667             var index = -1;
16668             if(sn.indexOf){
16669                 index = sn.indexOf(node);
16670             }else{
16671                 for(var i = 0, len = sn.length; i < len; i++){
16672                     if(sn[i] == node){
16673                         index = i;
16674                         break;
16675                     }
16676                 }
16677             }
16678             if(index != -1){
16679                 this.selNodes.splice(index, 1);
16680             }
16681             delete this.selMap[node.id];
16682             this.fireEvent("selectionchange", this, this.selNodes);
16683         }
16684     },
16685     
16686     /**
16687      * Clear all selections
16688      */
16689     clearSelections : function(suppressEvent){
16690         var sn = this.selNodes;
16691         if(sn.length > 0){
16692             for(var i = 0, len = sn.length; i < len; i++){
16693                 sn[i].ui.onSelectedChange(false);
16694             }
16695             this.selNodes = [];
16696             this.selMap = {};
16697             if(suppressEvent !== true){
16698                 this.fireEvent("selectionchange", this, this.selNodes);
16699             }
16700         }
16701     },
16702     
16703     /**
16704      * Returns true if the node is selected
16705      * @param {TreeNode} node The node to check
16706      * @return {Boolean}
16707      */
16708     isSelected : function(node){
16709         return this.selMap[node.id] ? true : false;  
16710     },
16711     
16712     /**
16713      * Returns an array of the selected nodes
16714      * @return {Array}
16715      */
16716     getSelectedNodes : function(){
16717         return this.selNodes;    
16718     },
16719
16720     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16721
16722     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16723
16724     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16725 });/*
16726  * Based on:
16727  * Ext JS Library 1.1.1
16728  * Copyright(c) 2006-2007, Ext JS, LLC.
16729  *
16730  * Originally Released Under LGPL - original licence link has changed is not relivant.
16731  *
16732  * Fork - LGPL
16733  * <script type="text/javascript">
16734  */
16735  
16736 /**
16737  * @class Roo.tree.TreeNode
16738  * @extends Roo.data.Node
16739  * @cfg {String} text The text for this node
16740  * @cfg {Boolean} expanded true to start the node expanded
16741  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16742  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16743  * @cfg {Boolean} disabled true to start the node disabled
16744  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16745  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16746  * @cfg {String} cls A css class to be added to the node
16747  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16748  * @cfg {String} href URL of the link used for the node (defaults to #)
16749  * @cfg {String} hrefTarget target frame for the link
16750  * @cfg {String} qtip An Ext QuickTip for the node
16751  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16752  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16753  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16754  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16755  * (defaults to undefined with no checkbox rendered)
16756  * @constructor
16757  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16758  */
16759 Roo.tree.TreeNode = function(attributes){
16760     attributes = attributes || {};
16761     if(typeof attributes == "string"){
16762         attributes = {text: attributes};
16763     }
16764     this.childrenRendered = false;
16765     this.rendered = false;
16766     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16767     this.expanded = attributes.expanded === true;
16768     this.isTarget = attributes.isTarget !== false;
16769     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16770     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16771
16772     /**
16773      * Read-only. The text for this node. To change it use setText().
16774      * @type String
16775      */
16776     this.text = attributes.text;
16777     /**
16778      * True if this node is disabled.
16779      * @type Boolean
16780      */
16781     this.disabled = attributes.disabled === true;
16782
16783     this.addEvents({
16784         /**
16785         * @event textchange
16786         * Fires when the text for this node is changed
16787         * @param {Node} this This node
16788         * @param {String} text The new text
16789         * @param {String} oldText The old text
16790         */
16791         "textchange" : true,
16792         /**
16793         * @event beforeexpand
16794         * Fires before this node is expanded, return false to cancel.
16795         * @param {Node} this This node
16796         * @param {Boolean} deep
16797         * @param {Boolean} anim
16798         */
16799         "beforeexpand" : true,
16800         /**
16801         * @event beforecollapse
16802         * Fires before this node is collapsed, return false to cancel.
16803         * @param {Node} this This node
16804         * @param {Boolean} deep
16805         * @param {Boolean} anim
16806         */
16807         "beforecollapse" : true,
16808         /**
16809         * @event expand
16810         * Fires when this node is expanded
16811         * @param {Node} this This node
16812         */
16813         "expand" : true,
16814         /**
16815         * @event disabledchange
16816         * Fires when the disabled status of this node changes
16817         * @param {Node} this This node
16818         * @param {Boolean} disabled
16819         */
16820         "disabledchange" : true,
16821         /**
16822         * @event collapse
16823         * Fires when this node is collapsed
16824         * @param {Node} this This node
16825         */
16826         "collapse" : true,
16827         /**
16828         * @event beforeclick
16829         * Fires before click processing. Return false to cancel the default action.
16830         * @param {Node} this This node
16831         * @param {Roo.EventObject} e The event object
16832         */
16833         "beforeclick":true,
16834         /**
16835         * @event checkchange
16836         * Fires when a node with a checkbox's checked property changes
16837         * @param {Node} this This node
16838         * @param {Boolean} checked
16839         */
16840         "checkchange":true,
16841         /**
16842         * @event click
16843         * Fires when this node is clicked
16844         * @param {Node} this This node
16845         * @param {Roo.EventObject} e The event object
16846         */
16847         "click":true,
16848         /**
16849         * @event dblclick
16850         * Fires when this node is double clicked
16851         * @param {Node} this This node
16852         * @param {Roo.EventObject} e The event object
16853         */
16854         "dblclick":true,
16855         /**
16856         * @event contextmenu
16857         * Fires when this node is right clicked
16858         * @param {Node} this This node
16859         * @param {Roo.EventObject} e The event object
16860         */
16861         "contextmenu":true,
16862         /**
16863         * @event beforechildrenrendered
16864         * Fires right before the child nodes for this node are rendered
16865         * @param {Node} this This node
16866         */
16867         "beforechildrenrendered":true
16868     });
16869
16870     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
16871
16872     /**
16873      * Read-only. The UI for this node
16874      * @type TreeNodeUI
16875      */
16876     this.ui = new uiClass(this);
16877     
16878     // finally support items[]
16879     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
16880         return;
16881     }
16882     
16883     
16884     Roo.each(this.attributes.items, function(c) {
16885         this.appendChild(Roo.factory(c,Roo.Tree));
16886     }, this);
16887     delete this.attributes.items;
16888     
16889     
16890     
16891 };
16892 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
16893     preventHScroll: true,
16894     /**
16895      * Returns true if this node is expanded
16896      * @return {Boolean}
16897      */
16898     isExpanded : function(){
16899         return this.expanded;
16900     },
16901
16902     /**
16903      * Returns the UI object for this node
16904      * @return {TreeNodeUI}
16905      */
16906     getUI : function(){
16907         return this.ui;
16908     },
16909
16910     // private override
16911     setFirstChild : function(node){
16912         var of = this.firstChild;
16913         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
16914         if(this.childrenRendered && of && node != of){
16915             of.renderIndent(true, true);
16916         }
16917         if(this.rendered){
16918             this.renderIndent(true, true);
16919         }
16920     },
16921
16922     // private override
16923     setLastChild : function(node){
16924         var ol = this.lastChild;
16925         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
16926         if(this.childrenRendered && ol && node != ol){
16927             ol.renderIndent(true, true);
16928         }
16929         if(this.rendered){
16930             this.renderIndent(true, true);
16931         }
16932     },
16933
16934     // these methods are overridden to provide lazy rendering support
16935     // private override
16936     appendChild : function()
16937     {
16938         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
16939         if(node && this.childrenRendered){
16940             node.render();
16941         }
16942         this.ui.updateExpandIcon();
16943         return node;
16944     },
16945
16946     // private override
16947     removeChild : function(node){
16948         this.ownerTree.getSelectionModel().unselect(node);
16949         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
16950         // if it's been rendered remove dom node
16951         if(this.childrenRendered){
16952             node.ui.remove();
16953         }
16954         if(this.childNodes.length < 1){
16955             this.collapse(false, false);
16956         }else{
16957             this.ui.updateExpandIcon();
16958         }
16959         if(!this.firstChild) {
16960             this.childrenRendered = false;
16961         }
16962         return node;
16963     },
16964
16965     // private override
16966     insertBefore : function(node, refNode){
16967         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
16968         if(newNode && refNode && this.childrenRendered){
16969             node.render();
16970         }
16971         this.ui.updateExpandIcon();
16972         return newNode;
16973     },
16974
16975     /**
16976      * Sets the text for this node
16977      * @param {String} text
16978      */
16979     setText : function(text){
16980         var oldText = this.text;
16981         this.text = text;
16982         this.attributes.text = text;
16983         if(this.rendered){ // event without subscribing
16984             this.ui.onTextChange(this, text, oldText);
16985         }
16986         this.fireEvent("textchange", this, text, oldText);
16987     },
16988
16989     /**
16990      * Triggers selection of this node
16991      */
16992     select : function(){
16993         this.getOwnerTree().getSelectionModel().select(this);
16994     },
16995
16996     /**
16997      * Triggers deselection of this node
16998      */
16999     unselect : function(){
17000         this.getOwnerTree().getSelectionModel().unselect(this);
17001     },
17002
17003     /**
17004      * Returns true if this node is selected
17005      * @return {Boolean}
17006      */
17007     isSelected : function(){
17008         return this.getOwnerTree().getSelectionModel().isSelected(this);
17009     },
17010
17011     /**
17012      * Expand this node.
17013      * @param {Boolean} deep (optional) True to expand all children as well
17014      * @param {Boolean} anim (optional) false to cancel the default animation
17015      * @param {Function} callback (optional) A callback to be called when
17016      * expanding this node completes (does not wait for deep expand to complete).
17017      * Called with 1 parameter, this node.
17018      */
17019     expand : function(deep, anim, callback){
17020         if(!this.expanded){
17021             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17022                 return;
17023             }
17024             if(!this.childrenRendered){
17025                 this.renderChildren();
17026             }
17027             this.expanded = true;
17028             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17029                 this.ui.animExpand(function(){
17030                     this.fireEvent("expand", this);
17031                     if(typeof callback == "function"){
17032                         callback(this);
17033                     }
17034                     if(deep === true){
17035                         this.expandChildNodes(true);
17036                     }
17037                 }.createDelegate(this));
17038                 return;
17039             }else{
17040                 this.ui.expand();
17041                 this.fireEvent("expand", this);
17042                 if(typeof callback == "function"){
17043                     callback(this);
17044                 }
17045             }
17046         }else{
17047            if(typeof callback == "function"){
17048                callback(this);
17049            }
17050         }
17051         if(deep === true){
17052             this.expandChildNodes(true);
17053         }
17054     },
17055
17056     isHiddenRoot : function(){
17057         return this.isRoot && !this.getOwnerTree().rootVisible;
17058     },
17059
17060     /**
17061      * Collapse this node.
17062      * @param {Boolean} deep (optional) True to collapse all children as well
17063      * @param {Boolean} anim (optional) false to cancel the default animation
17064      */
17065     collapse : function(deep, anim){
17066         if(this.expanded && !this.isHiddenRoot()){
17067             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17068                 return;
17069             }
17070             this.expanded = false;
17071             if((this.getOwnerTree().animate && anim !== false) || anim){
17072                 this.ui.animCollapse(function(){
17073                     this.fireEvent("collapse", this);
17074                     if(deep === true){
17075                         this.collapseChildNodes(true);
17076                     }
17077                 }.createDelegate(this));
17078                 return;
17079             }else{
17080                 this.ui.collapse();
17081                 this.fireEvent("collapse", this);
17082             }
17083         }
17084         if(deep === true){
17085             var cs = this.childNodes;
17086             for(var i = 0, len = cs.length; i < len; i++) {
17087                 cs[i].collapse(true, false);
17088             }
17089         }
17090     },
17091
17092     // private
17093     delayedExpand : function(delay){
17094         if(!this.expandProcId){
17095             this.expandProcId = this.expand.defer(delay, this);
17096         }
17097     },
17098
17099     // private
17100     cancelExpand : function(){
17101         if(this.expandProcId){
17102             clearTimeout(this.expandProcId);
17103         }
17104         this.expandProcId = false;
17105     },
17106
17107     /**
17108      * Toggles expanded/collapsed state of the node
17109      */
17110     toggle : function(){
17111         if(this.expanded){
17112             this.collapse();
17113         }else{
17114             this.expand();
17115         }
17116     },
17117
17118     /**
17119      * Ensures all parent nodes are expanded
17120      */
17121     ensureVisible : function(callback){
17122         var tree = this.getOwnerTree();
17123         tree.expandPath(this.parentNode.getPath(), false, function(){
17124             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17125             Roo.callback(callback);
17126         }.createDelegate(this));
17127     },
17128
17129     /**
17130      * Expand all child nodes
17131      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17132      */
17133     expandChildNodes : function(deep){
17134         var cs = this.childNodes;
17135         for(var i = 0, len = cs.length; i < len; i++) {
17136                 cs[i].expand(deep);
17137         }
17138     },
17139
17140     /**
17141      * Collapse all child nodes
17142      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17143      */
17144     collapseChildNodes : function(deep){
17145         var cs = this.childNodes;
17146         for(var i = 0, len = cs.length; i < len; i++) {
17147                 cs[i].collapse(deep);
17148         }
17149     },
17150
17151     /**
17152      * Disables this node
17153      */
17154     disable : function(){
17155         this.disabled = true;
17156         this.unselect();
17157         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17158             this.ui.onDisableChange(this, true);
17159         }
17160         this.fireEvent("disabledchange", this, true);
17161     },
17162
17163     /**
17164      * Enables this node
17165      */
17166     enable : function(){
17167         this.disabled = false;
17168         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17169             this.ui.onDisableChange(this, false);
17170         }
17171         this.fireEvent("disabledchange", this, false);
17172     },
17173
17174     // private
17175     renderChildren : function(suppressEvent){
17176         if(suppressEvent !== false){
17177             this.fireEvent("beforechildrenrendered", this);
17178         }
17179         var cs = this.childNodes;
17180         for(var i = 0, len = cs.length; i < len; i++){
17181             cs[i].render(true);
17182         }
17183         this.childrenRendered = true;
17184     },
17185
17186     // private
17187     sort : function(fn, scope){
17188         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17189         if(this.childrenRendered){
17190             var cs = this.childNodes;
17191             for(var i = 0, len = cs.length; i < len; i++){
17192                 cs[i].render(true);
17193             }
17194         }
17195     },
17196
17197     // private
17198     render : function(bulkRender){
17199         this.ui.render(bulkRender);
17200         if(!this.rendered){
17201             this.rendered = true;
17202             if(this.expanded){
17203                 this.expanded = false;
17204                 this.expand(false, false);
17205             }
17206         }
17207     },
17208
17209     // private
17210     renderIndent : function(deep, refresh){
17211         if(refresh){
17212             this.ui.childIndent = null;
17213         }
17214         this.ui.renderIndent();
17215         if(deep === true && this.childrenRendered){
17216             var cs = this.childNodes;
17217             for(var i = 0, len = cs.length; i < len; i++){
17218                 cs[i].renderIndent(true, refresh);
17219             }
17220         }
17221     }
17222 });/*
17223  * Based on:
17224  * Ext JS Library 1.1.1
17225  * Copyright(c) 2006-2007, Ext JS, LLC.
17226  *
17227  * Originally Released Under LGPL - original licence link has changed is not relivant.
17228  *
17229  * Fork - LGPL
17230  * <script type="text/javascript">
17231  */
17232  
17233 /**
17234  * @class Roo.tree.AsyncTreeNode
17235  * @extends Roo.tree.TreeNode
17236  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17237  * @constructor
17238  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17239  */
17240  Roo.tree.AsyncTreeNode = function(config){
17241     this.loaded = false;
17242     this.loading = false;
17243     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17244     /**
17245     * @event beforeload
17246     * Fires before this node is loaded, return false to cancel
17247     * @param {Node} this This node
17248     */
17249     this.addEvents({'beforeload':true, 'load': true});
17250     /**
17251     * @event load
17252     * Fires when this node is loaded
17253     * @param {Node} this This node
17254     */
17255     /**
17256      * The loader used by this node (defaults to using the tree's defined loader)
17257      * @type TreeLoader
17258      * @property loader
17259      */
17260 };
17261 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17262     expand : function(deep, anim, callback){
17263         if(this.loading){ // if an async load is already running, waiting til it's done
17264             var timer;
17265             var f = function(){
17266                 if(!this.loading){ // done loading
17267                     clearInterval(timer);
17268                     this.expand(deep, anim, callback);
17269                 }
17270             }.createDelegate(this);
17271             timer = setInterval(f, 200);
17272             return;
17273         }
17274         if(!this.loaded){
17275             if(this.fireEvent("beforeload", this) === false){
17276                 return;
17277             }
17278             this.loading = true;
17279             this.ui.beforeLoad(this);
17280             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17281             if(loader){
17282                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17283                 return;
17284             }
17285         }
17286         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17287     },
17288     
17289     /**
17290      * Returns true if this node is currently loading
17291      * @return {Boolean}
17292      */
17293     isLoading : function(){
17294         return this.loading;  
17295     },
17296     
17297     loadComplete : function(deep, anim, callback){
17298         this.loading = false;
17299         this.loaded = true;
17300         this.ui.afterLoad(this);
17301         this.fireEvent("load", this);
17302         this.expand(deep, anim, callback);
17303     },
17304     
17305     /**
17306      * Returns true if this node has been loaded
17307      * @return {Boolean}
17308      */
17309     isLoaded : function(){
17310         return this.loaded;
17311     },
17312     
17313     hasChildNodes : function(){
17314         if(!this.isLeaf() && !this.loaded){
17315             return true;
17316         }else{
17317             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17318         }
17319     },
17320
17321     /**
17322      * Trigger a reload for this node
17323      * @param {Function} callback
17324      */
17325     reload : function(callback){
17326         this.collapse(false, false);
17327         while(this.firstChild){
17328             this.removeChild(this.firstChild);
17329         }
17330         this.childrenRendered = false;
17331         this.loaded = false;
17332         if(this.isHiddenRoot()){
17333             this.expanded = false;
17334         }
17335         this.expand(false, false, callback);
17336     }
17337 });/*
17338  * Based on:
17339  * Ext JS Library 1.1.1
17340  * Copyright(c) 2006-2007, Ext JS, LLC.
17341  *
17342  * Originally Released Under LGPL - original licence link has changed is not relivant.
17343  *
17344  * Fork - LGPL
17345  * <script type="text/javascript">
17346  */
17347  
17348 /**
17349  * @class Roo.tree.TreeNodeUI
17350  * @constructor
17351  * @param {Object} node The node to render
17352  * The TreeNode UI implementation is separate from the
17353  * tree implementation. Unless you are customizing the tree UI,
17354  * you should never have to use this directly.
17355  */
17356 Roo.tree.TreeNodeUI = function(node){
17357     this.node = node;
17358     this.rendered = false;
17359     this.animating = false;
17360     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17361 };
17362
17363 Roo.tree.TreeNodeUI.prototype = {
17364     removeChild : function(node){
17365         if(this.rendered){
17366             this.ctNode.removeChild(node.ui.getEl());
17367         }
17368     },
17369
17370     beforeLoad : function(){
17371          this.addClass("x-tree-node-loading");
17372     },
17373
17374     afterLoad : function(){
17375          this.removeClass("x-tree-node-loading");
17376     },
17377
17378     onTextChange : function(node, text, oldText){
17379         if(this.rendered){
17380             this.textNode.innerHTML = text;
17381         }
17382     },
17383
17384     onDisableChange : function(node, state){
17385         this.disabled = state;
17386         if(state){
17387             this.addClass("x-tree-node-disabled");
17388         }else{
17389             this.removeClass("x-tree-node-disabled");
17390         }
17391     },
17392
17393     onSelectedChange : function(state){
17394         if(state){
17395             this.focus();
17396             this.addClass("x-tree-selected");
17397         }else{
17398             //this.blur();
17399             this.removeClass("x-tree-selected");
17400         }
17401     },
17402
17403     onMove : function(tree, node, oldParent, newParent, index, refNode){
17404         this.childIndent = null;
17405         if(this.rendered){
17406             var targetNode = newParent.ui.getContainer();
17407             if(!targetNode){//target not rendered
17408                 this.holder = document.createElement("div");
17409                 this.holder.appendChild(this.wrap);
17410                 return;
17411             }
17412             var insertBefore = refNode ? refNode.ui.getEl() : null;
17413             if(insertBefore){
17414                 targetNode.insertBefore(this.wrap, insertBefore);
17415             }else{
17416                 targetNode.appendChild(this.wrap);
17417             }
17418             this.node.renderIndent(true);
17419         }
17420     },
17421
17422     addClass : function(cls){
17423         if(this.elNode){
17424             Roo.fly(this.elNode).addClass(cls);
17425         }
17426     },
17427
17428     removeClass : function(cls){
17429         if(this.elNode){
17430             Roo.fly(this.elNode).removeClass(cls);
17431         }
17432     },
17433
17434     remove : function(){
17435         if(this.rendered){
17436             this.holder = document.createElement("div");
17437             this.holder.appendChild(this.wrap);
17438         }
17439     },
17440
17441     fireEvent : function(){
17442         return this.node.fireEvent.apply(this.node, arguments);
17443     },
17444
17445     initEvents : function(){
17446         this.node.on("move", this.onMove, this);
17447         var E = Roo.EventManager;
17448         var a = this.anchor;
17449
17450         var el = Roo.fly(a, '_treeui');
17451
17452         if(Roo.isOpera){ // opera render bug ignores the CSS
17453             el.setStyle("text-decoration", "none");
17454         }
17455
17456         el.on("click", this.onClick, this);
17457         el.on("dblclick", this.onDblClick, this);
17458
17459         if(this.checkbox){
17460             Roo.EventManager.on(this.checkbox,
17461                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17462         }
17463
17464         el.on("contextmenu", this.onContextMenu, this);
17465
17466         var icon = Roo.fly(this.iconNode);
17467         icon.on("click", this.onClick, this);
17468         icon.on("dblclick", this.onDblClick, this);
17469         icon.on("contextmenu", this.onContextMenu, this);
17470         E.on(this.ecNode, "click", this.ecClick, this, true);
17471
17472         if(this.node.disabled){
17473             this.addClass("x-tree-node-disabled");
17474         }
17475         if(this.node.hidden){
17476             this.addClass("x-tree-node-disabled");
17477         }
17478         var ot = this.node.getOwnerTree();
17479         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17480         if(dd && (!this.node.isRoot || ot.rootVisible)){
17481             Roo.dd.Registry.register(this.elNode, {
17482                 node: this.node,
17483                 handles: this.getDDHandles(),
17484                 isHandle: false
17485             });
17486         }
17487     },
17488
17489     getDDHandles : function(){
17490         return [this.iconNode, this.textNode];
17491     },
17492
17493     hide : function(){
17494         if(this.rendered){
17495             this.wrap.style.display = "none";
17496         }
17497     },
17498
17499     show : function(){
17500         if(this.rendered){
17501             this.wrap.style.display = "";
17502         }
17503     },
17504
17505     onContextMenu : function(e){
17506         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17507             e.preventDefault();
17508             this.focus();
17509             this.fireEvent("contextmenu", this.node, e);
17510         }
17511     },
17512
17513     onClick : function(e){
17514         if(this.dropping){
17515             e.stopEvent();
17516             return;
17517         }
17518         if(this.fireEvent("beforeclick", this.node, e) !== false){
17519             if(!this.disabled && this.node.attributes.href){
17520                 this.fireEvent("click", this.node, e);
17521                 return;
17522             }
17523             e.preventDefault();
17524             if(this.disabled){
17525                 return;
17526             }
17527
17528             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17529                 this.node.toggle();
17530             }
17531
17532             this.fireEvent("click", this.node, e);
17533         }else{
17534             e.stopEvent();
17535         }
17536     },
17537
17538     onDblClick : function(e){
17539         e.preventDefault();
17540         if(this.disabled){
17541             return;
17542         }
17543         if(this.checkbox){
17544             this.toggleCheck();
17545         }
17546         if(!this.animating && this.node.hasChildNodes()){
17547             this.node.toggle();
17548         }
17549         this.fireEvent("dblclick", this.node, e);
17550     },
17551
17552     onCheckChange : function(){
17553         var checked = this.checkbox.checked;
17554         this.node.attributes.checked = checked;
17555         this.fireEvent('checkchange', this.node, checked);
17556     },
17557
17558     ecClick : function(e){
17559         if(!this.animating && this.node.hasChildNodes()){
17560             this.node.toggle();
17561         }
17562     },
17563
17564     startDrop : function(){
17565         this.dropping = true;
17566     },
17567
17568     // delayed drop so the click event doesn't get fired on a drop
17569     endDrop : function(){
17570        setTimeout(function(){
17571            this.dropping = false;
17572        }.createDelegate(this), 50);
17573     },
17574
17575     expand : function(){
17576         this.updateExpandIcon();
17577         this.ctNode.style.display = "";
17578     },
17579
17580     focus : function(){
17581         if(!this.node.preventHScroll){
17582             try{this.anchor.focus();
17583             }catch(e){}
17584         }else if(!Roo.isIE){
17585             try{
17586                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17587                 var l = noscroll.scrollLeft;
17588                 this.anchor.focus();
17589                 noscroll.scrollLeft = l;
17590             }catch(e){}
17591         }
17592     },
17593
17594     toggleCheck : function(value){
17595         var cb = this.checkbox;
17596         if(cb){
17597             cb.checked = (value === undefined ? !cb.checked : value);
17598         }
17599     },
17600
17601     blur : function(){
17602         try{
17603             this.anchor.blur();
17604         }catch(e){}
17605     },
17606
17607     animExpand : function(callback){
17608         var ct = Roo.get(this.ctNode);
17609         ct.stopFx();
17610         if(!this.node.hasChildNodes()){
17611             this.updateExpandIcon();
17612             this.ctNode.style.display = "";
17613             Roo.callback(callback);
17614             return;
17615         }
17616         this.animating = true;
17617         this.updateExpandIcon();
17618
17619         ct.slideIn('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     highlight : function(){
17630         var tree = this.node.getOwnerTree();
17631         Roo.fly(this.wrap).highlight(
17632             tree.hlColor || "C3DAF9",
17633             {endColor: tree.hlBaseColor}
17634         );
17635     },
17636
17637     collapse : function(){
17638         this.updateExpandIcon();
17639         this.ctNode.style.display = "none";
17640     },
17641
17642     animCollapse : function(callback){
17643         var ct = Roo.get(this.ctNode);
17644         ct.enableDisplayMode('block');
17645         ct.stopFx();
17646
17647         this.animating = true;
17648         this.updateExpandIcon();
17649
17650         ct.slideOut('t', {
17651             callback : function(){
17652                this.animating = false;
17653                Roo.callback(callback);
17654             },
17655             scope: this,
17656             duration: this.node.ownerTree.duration || .25
17657         });
17658     },
17659
17660     getContainer : function(){
17661         return this.ctNode;
17662     },
17663
17664     getEl : function(){
17665         return this.wrap;
17666     },
17667
17668     appendDDGhost : function(ghostNode){
17669         ghostNode.appendChild(this.elNode.cloneNode(true));
17670     },
17671
17672     getDDRepairXY : function(){
17673         return Roo.lib.Dom.getXY(this.iconNode);
17674     },
17675
17676     onRender : function(){
17677         this.render();
17678     },
17679
17680     render : function(bulkRender){
17681         var n = this.node, a = n.attributes;
17682         var targetNode = n.parentNode ?
17683               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17684
17685         if(!this.rendered){
17686             this.rendered = true;
17687
17688             this.renderElements(n, a, targetNode, bulkRender);
17689
17690             if(a.qtip){
17691                if(this.textNode.setAttributeNS){
17692                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17693                    if(a.qtipTitle){
17694                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17695                    }
17696                }else{
17697                    this.textNode.setAttribute("ext:qtip", a.qtip);
17698                    if(a.qtipTitle){
17699                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17700                    }
17701                }
17702             }else if(a.qtipCfg){
17703                 a.qtipCfg.target = Roo.id(this.textNode);
17704                 Roo.QuickTips.register(a.qtipCfg);
17705             }
17706             this.initEvents();
17707             if(!this.node.expanded){
17708                 this.updateExpandIcon();
17709             }
17710         }else{
17711             if(bulkRender === true) {
17712                 targetNode.appendChild(this.wrap);
17713             }
17714         }
17715     },
17716
17717     renderElements : function(n, a, targetNode, bulkRender)
17718     {
17719         // add some indent caching, this helps performance when rendering a large tree
17720         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17721         var t = n.getOwnerTree();
17722         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17723         if (typeof(n.attributes.html) != 'undefined') {
17724             txt = n.attributes.html;
17725         }
17726         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17727         var cb = typeof a.checked == 'boolean';
17728         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17729         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17730             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17731             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17732             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17733             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17734             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17735              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17736                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17737             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17738             "</li>"];
17739
17740         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17741             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17742                                 n.nextSibling.ui.getEl(), buf.join(""));
17743         }else{
17744             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17745         }
17746
17747         this.elNode = this.wrap.childNodes[0];
17748         this.ctNode = this.wrap.childNodes[1];
17749         var cs = this.elNode.childNodes;
17750         this.indentNode = cs[0];
17751         this.ecNode = cs[1];
17752         this.iconNode = cs[2];
17753         var index = 3;
17754         if(cb){
17755             this.checkbox = cs[3];
17756             index++;
17757         }
17758         this.anchor = cs[index];
17759         this.textNode = cs[index].firstChild;
17760     },
17761
17762     getAnchor : function(){
17763         return this.anchor;
17764     },
17765
17766     getTextEl : function(){
17767         return this.textNode;
17768     },
17769
17770     getIconEl : function(){
17771         return this.iconNode;
17772     },
17773
17774     isChecked : function(){
17775         return this.checkbox ? this.checkbox.checked : false;
17776     },
17777
17778     updateExpandIcon : function(){
17779         if(this.rendered){
17780             var n = this.node, c1, c2;
17781             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17782             var hasChild = n.hasChildNodes();
17783             if(hasChild){
17784                 if(n.expanded){
17785                     cls += "-minus";
17786                     c1 = "x-tree-node-collapsed";
17787                     c2 = "x-tree-node-expanded";
17788                 }else{
17789                     cls += "-plus";
17790                     c1 = "x-tree-node-expanded";
17791                     c2 = "x-tree-node-collapsed";
17792                 }
17793                 if(this.wasLeaf){
17794                     this.removeClass("x-tree-node-leaf");
17795                     this.wasLeaf = false;
17796                 }
17797                 if(this.c1 != c1 || this.c2 != c2){
17798                     Roo.fly(this.elNode).replaceClass(c1, c2);
17799                     this.c1 = c1; this.c2 = c2;
17800                 }
17801             }else{
17802                 // this changes non-leafs into leafs if they have no children.
17803                 // it's not very rational behaviour..
17804                 
17805                 if(!this.wasLeaf && this.node.leaf){
17806                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17807                     delete this.c1;
17808                     delete this.c2;
17809                     this.wasLeaf = true;
17810                 }
17811             }
17812             var ecc = "x-tree-ec-icon "+cls;
17813             if(this.ecc != ecc){
17814                 this.ecNode.className = ecc;
17815                 this.ecc = ecc;
17816             }
17817         }
17818     },
17819
17820     getChildIndent : function(){
17821         if(!this.childIndent){
17822             var buf = [];
17823             var p = this.node;
17824             while(p){
17825                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17826                     if(!p.isLast()) {
17827                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17828                     } else {
17829                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17830                     }
17831                 }
17832                 p = p.parentNode;
17833             }
17834             this.childIndent = buf.join("");
17835         }
17836         return this.childIndent;
17837     },
17838
17839     renderIndent : function(){
17840         if(this.rendered){
17841             var indent = "";
17842             var p = this.node.parentNode;
17843             if(p){
17844                 indent = p.ui.getChildIndent();
17845             }
17846             if(this.indentMarkup != indent){ // don't rerender if not required
17847                 this.indentNode.innerHTML = indent;
17848                 this.indentMarkup = indent;
17849             }
17850             this.updateExpandIcon();
17851         }
17852     }
17853 };
17854
17855 Roo.tree.RootTreeNodeUI = function(){
17856     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
17857 };
17858 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
17859     render : function(){
17860         if(!this.rendered){
17861             var targetNode = this.node.ownerTree.innerCt.dom;
17862             this.node.expanded = true;
17863             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
17864             this.wrap = this.ctNode = targetNode.firstChild;
17865         }
17866     },
17867     collapse : function(){
17868     },
17869     expand : function(){
17870     }
17871 });/*
17872  * Based on:
17873  * Ext JS Library 1.1.1
17874  * Copyright(c) 2006-2007, Ext JS, LLC.
17875  *
17876  * Originally Released Under LGPL - original licence link has changed is not relivant.
17877  *
17878  * Fork - LGPL
17879  * <script type="text/javascript">
17880  */
17881 /**
17882  * @class Roo.tree.TreeLoader
17883  * @extends Roo.util.Observable
17884  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
17885  * nodes from a specified URL. The response must be a javascript Array definition
17886  * who's elements are node definition objects. eg:
17887  * <pre><code>
17888 {  success : true,
17889    data :      [
17890    
17891     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
17892     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
17893     ]
17894 }
17895
17896
17897 </code></pre>
17898  * <br><br>
17899  * The old style respose with just an array is still supported, but not recommended.
17900  * <br><br>
17901  *
17902  * A server request is sent, and child nodes are loaded only when a node is expanded.
17903  * The loading node's id is passed to the server under the parameter name "node" to
17904  * enable the server to produce the correct child nodes.
17905  * <br><br>
17906  * To pass extra parameters, an event handler may be attached to the "beforeload"
17907  * event, and the parameters specified in the TreeLoader's baseParams property:
17908  * <pre><code>
17909     myTreeLoader.on("beforeload", function(treeLoader, node) {
17910         this.baseParams.category = node.attributes.category;
17911     }, this);
17912 </code></pre><
17913  * This would pass an HTTP parameter called "category" to the server containing
17914  * the value of the Node's "category" attribute.
17915  * @constructor
17916  * Creates a new Treeloader.
17917  * @param {Object} config A config object containing config properties.
17918  */
17919 Roo.tree.TreeLoader = function(config){
17920     this.baseParams = {};
17921     this.requestMethod = "POST";
17922     Roo.apply(this, config);
17923
17924     this.addEvents({
17925     
17926         /**
17927          * @event beforeload
17928          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
17929          * @param {Object} This TreeLoader object.
17930          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17931          * @param {Object} callback The callback function specified in the {@link #load} call.
17932          */
17933         beforeload : true,
17934         /**
17935          * @event load
17936          * Fires when the node has been successfuly loaded.
17937          * @param {Object} This TreeLoader object.
17938          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17939          * @param {Object} response The response object containing the data from the server.
17940          */
17941         load : true,
17942         /**
17943          * @event loadexception
17944          * Fires if the network request failed.
17945          * @param {Object} This TreeLoader object.
17946          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
17947          * @param {Object} response The response object containing the data from the server.
17948          */
17949         loadexception : true,
17950         /**
17951          * @event create
17952          * Fires before a node is created, enabling you to return custom Node types 
17953          * @param {Object} This TreeLoader object.
17954          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
17955          */
17956         create : true
17957     });
17958
17959     Roo.tree.TreeLoader.superclass.constructor.call(this);
17960 };
17961
17962 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
17963     /**
17964     * @cfg {String} dataUrl The URL from which to request a Json string which
17965     * specifies an array of node definition object representing the child nodes
17966     * to be loaded.
17967     */
17968     /**
17969     * @cfg {String} requestMethod either GET or POST
17970     * defaults to POST (due to BC)
17971     * to be loaded.
17972     */
17973     /**
17974     * @cfg {Object} baseParams (optional) An object containing properties which
17975     * specify HTTP parameters to be passed to each request for child nodes.
17976     */
17977     /**
17978     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
17979     * created by this loader. If the attributes sent by the server have an attribute in this object,
17980     * they take priority.
17981     */
17982     /**
17983     * @cfg {Object} uiProviders (optional) An object containing properties which
17984     * 
17985     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
17986     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
17987     * <i>uiProvider</i> attribute of a returned child node is a string rather
17988     * than a reference to a TreeNodeUI implementation, this that string value
17989     * is used as a property name in the uiProviders object. You can define the provider named
17990     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
17991     */
17992     uiProviders : {},
17993
17994     /**
17995     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
17996     * child nodes before loading.
17997     */
17998     clearOnLoad : true,
17999
18000     /**
18001     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18002     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18003     * Grid query { data : [ .....] }
18004     */
18005     
18006     root : false,
18007      /**
18008     * @cfg {String} queryParam (optional) 
18009     * Name of the query as it will be passed on the querystring (defaults to 'node')
18010     * eg. the request will be ?node=[id]
18011     */
18012     
18013     
18014     queryParam: false,
18015     
18016     /**
18017      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18018      * This is called automatically when a node is expanded, but may be used to reload
18019      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18020      * @param {Roo.tree.TreeNode} node
18021      * @param {Function} callback
18022      */
18023     load : function(node, callback){
18024         if(this.clearOnLoad){
18025             while(node.firstChild){
18026                 node.removeChild(node.firstChild);
18027             }
18028         }
18029         if(node.attributes.children){ // preloaded json children
18030             var cs = node.attributes.children;
18031             for(var i = 0, len = cs.length; i < len; i++){
18032                 node.appendChild(this.createNode(cs[i]));
18033             }
18034             if(typeof callback == "function"){
18035                 callback();
18036             }
18037         }else if(this.dataUrl){
18038             this.requestData(node, callback);
18039         }
18040     },
18041
18042     getParams: function(node){
18043         var buf = [], bp = this.baseParams;
18044         for(var key in bp){
18045             if(typeof bp[key] != "function"){
18046                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18047             }
18048         }
18049         var n = this.queryParam === false ? 'node' : this.queryParam;
18050         buf.push(n + "=", encodeURIComponent(node.id));
18051         return buf.join("");
18052     },
18053
18054     requestData : function(node, callback){
18055         if(this.fireEvent("beforeload", this, node, callback) !== false){
18056             this.transId = Roo.Ajax.request({
18057                 method:this.requestMethod,
18058                 url: this.dataUrl||this.url,
18059                 success: this.handleResponse,
18060                 failure: this.handleFailure,
18061                 scope: this,
18062                 argument: {callback: callback, node: node},
18063                 params: this.getParams(node)
18064             });
18065         }else{
18066             // if the load is cancelled, make sure we notify
18067             // the node that we are done
18068             if(typeof callback == "function"){
18069                 callback();
18070             }
18071         }
18072     },
18073
18074     isLoading : function(){
18075         return this.transId ? true : false;
18076     },
18077
18078     abort : function(){
18079         if(this.isLoading()){
18080             Roo.Ajax.abort(this.transId);
18081         }
18082     },
18083
18084     // private
18085     createNode : function(attr)
18086     {
18087         // apply baseAttrs, nice idea Corey!
18088         if(this.baseAttrs){
18089             Roo.applyIf(attr, this.baseAttrs);
18090         }
18091         if(this.applyLoader !== false){
18092             attr.loader = this;
18093         }
18094         // uiProvider = depreciated..
18095         
18096         if(typeof(attr.uiProvider) == 'string'){
18097            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18098                 /**  eval:var:attr */ eval(attr.uiProvider);
18099         }
18100         if(typeof(this.uiProviders['default']) != 'undefined') {
18101             attr.uiProvider = this.uiProviders['default'];
18102         }
18103         
18104         this.fireEvent('create', this, attr);
18105         
18106         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18107         return(attr.leaf ?
18108                         new Roo.tree.TreeNode(attr) :
18109                         new Roo.tree.AsyncTreeNode(attr));
18110     },
18111
18112     processResponse : function(response, node, callback)
18113     {
18114         var json = response.responseText;
18115         try {
18116             
18117             var o = Roo.decode(json);
18118             
18119             if (this.root === false && typeof(o.success) != undefined) {
18120                 this.root = 'data'; // the default behaviour for list like data..
18121                 }
18122                 
18123             if (this.root !== false &&  !o.success) {
18124                 // it's a failure condition.
18125                 var a = response.argument;
18126                 this.fireEvent("loadexception", this, a.node, response);
18127                 Roo.log("Load failed - should have a handler really");
18128                 return;
18129             }
18130             
18131             
18132             
18133             if (this.root !== false) {
18134                  o = o[this.root];
18135             }
18136             
18137             for(var i = 0, len = o.length; i < len; i++){
18138                 var n = this.createNode(o[i]);
18139                 if(n){
18140                     node.appendChild(n);
18141                 }
18142             }
18143             if(typeof callback == "function"){
18144                 callback(this, node);
18145             }
18146         }catch(e){
18147             this.handleFailure(response);
18148         }
18149     },
18150
18151     handleResponse : function(response){
18152         this.transId = false;
18153         var a = response.argument;
18154         this.processResponse(response, a.node, a.callback);
18155         this.fireEvent("load", this, a.node, response);
18156     },
18157
18158     handleFailure : function(response)
18159     {
18160         // should handle failure better..
18161         this.transId = false;
18162         var a = response.argument;
18163         this.fireEvent("loadexception", this, a.node, response);
18164         if(typeof a.callback == "function"){
18165             a.callback(this, a.node);
18166         }
18167     }
18168 });/*
18169  * Based on:
18170  * Ext JS Library 1.1.1
18171  * Copyright(c) 2006-2007, Ext JS, LLC.
18172  *
18173  * Originally Released Under LGPL - original licence link has changed is not relivant.
18174  *
18175  * Fork - LGPL
18176  * <script type="text/javascript">
18177  */
18178
18179 /**
18180 * @class Roo.tree.TreeFilter
18181 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18182 * @param {TreePanel} tree
18183 * @param {Object} config (optional)
18184  */
18185 Roo.tree.TreeFilter = function(tree, config){
18186     this.tree = tree;
18187     this.filtered = {};
18188     Roo.apply(this, config);
18189 };
18190
18191 Roo.tree.TreeFilter.prototype = {
18192     clearBlank:false,
18193     reverse:false,
18194     autoClear:false,
18195     remove:false,
18196
18197      /**
18198      * Filter the data by a specific attribute.
18199      * @param {String/RegExp} value Either string that the attribute value
18200      * should start with or a RegExp to test against the attribute
18201      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18202      * @param {TreeNode} startNode (optional) The node to start the filter at.
18203      */
18204     filter : function(value, attr, startNode){
18205         attr = attr || "text";
18206         var f;
18207         if(typeof value == "string"){
18208             var vlen = value.length;
18209             // auto clear empty filter
18210             if(vlen == 0 && this.clearBlank){
18211                 this.clear();
18212                 return;
18213             }
18214             value = value.toLowerCase();
18215             f = function(n){
18216                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18217             };
18218         }else if(value.exec){ // regex?
18219             f = function(n){
18220                 return value.test(n.attributes[attr]);
18221             };
18222         }else{
18223             throw 'Illegal filter type, must be string or regex';
18224         }
18225         this.filterBy(f, null, startNode);
18226         },
18227
18228     /**
18229      * Filter by a function. The passed function will be called with each
18230      * node in the tree (or from the startNode). If the function returns true, the node is kept
18231      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18232      * @param {Function} fn The filter function
18233      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18234      */
18235     filterBy : function(fn, scope, startNode){
18236         startNode = startNode || this.tree.root;
18237         if(this.autoClear){
18238             this.clear();
18239         }
18240         var af = this.filtered, rv = this.reverse;
18241         var f = function(n){
18242             if(n == startNode){
18243                 return true;
18244             }
18245             if(af[n.id]){
18246                 return false;
18247             }
18248             var m = fn.call(scope || n, n);
18249             if(!m || rv){
18250                 af[n.id] = n;
18251                 n.ui.hide();
18252                 return false;
18253             }
18254             return true;
18255         };
18256         startNode.cascade(f);
18257         if(this.remove){
18258            for(var id in af){
18259                if(typeof id != "function"){
18260                    var n = af[id];
18261                    if(n && n.parentNode){
18262                        n.parentNode.removeChild(n);
18263                    }
18264                }
18265            }
18266         }
18267     },
18268
18269     /**
18270      * Clears the current filter. Note: with the "remove" option
18271      * set a filter cannot be cleared.
18272      */
18273     clear : function(){
18274         var t = this.tree;
18275         var af = this.filtered;
18276         for(var id in af){
18277             if(typeof id != "function"){
18278                 var n = af[id];
18279                 if(n){
18280                     n.ui.show();
18281                 }
18282             }
18283         }
18284         this.filtered = {};
18285     }
18286 };
18287 /*
18288  * Based on:
18289  * Ext JS Library 1.1.1
18290  * Copyright(c) 2006-2007, Ext JS, LLC.
18291  *
18292  * Originally Released Under LGPL - original licence link has changed is not relivant.
18293  *
18294  * Fork - LGPL
18295  * <script type="text/javascript">
18296  */
18297  
18298
18299 /**
18300  * @class Roo.tree.TreeSorter
18301  * Provides sorting of nodes in a TreePanel
18302  * 
18303  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18304  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18305  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18306  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18307  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18308  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18309  * @constructor
18310  * @param {TreePanel} tree
18311  * @param {Object} config
18312  */
18313 Roo.tree.TreeSorter = function(tree, config){
18314     Roo.apply(this, config);
18315     tree.on("beforechildrenrendered", this.doSort, this);
18316     tree.on("append", this.updateSort, this);
18317     tree.on("insert", this.updateSort, this);
18318     
18319     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18320     var p = this.property || "text";
18321     var sortType = this.sortType;
18322     var fs = this.folderSort;
18323     var cs = this.caseSensitive === true;
18324     var leafAttr = this.leafAttr || 'leaf';
18325
18326     this.sortFn = function(n1, n2){
18327         if(fs){
18328             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18329                 return 1;
18330             }
18331             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18332                 return -1;
18333             }
18334         }
18335         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18336         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18337         if(v1 < v2){
18338                         return dsc ? +1 : -1;
18339                 }else if(v1 > v2){
18340                         return dsc ? -1 : +1;
18341         }else{
18342                 return 0;
18343         }
18344     };
18345 };
18346
18347 Roo.tree.TreeSorter.prototype = {
18348     doSort : function(node){
18349         node.sort(this.sortFn);
18350     },
18351     
18352     compareNodes : function(n1, n2){
18353         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18354     },
18355     
18356     updateSort : function(tree, node){
18357         if(node.childrenRendered){
18358             this.doSort.defer(1, this, [node]);
18359         }
18360     }
18361 };/*
18362  * Based on:
18363  * Ext JS Library 1.1.1
18364  * Copyright(c) 2006-2007, Ext JS, LLC.
18365  *
18366  * Originally Released Under LGPL - original licence link has changed is not relivant.
18367  *
18368  * Fork - LGPL
18369  * <script type="text/javascript">
18370  */
18371
18372 if(Roo.dd.DropZone){
18373     
18374 Roo.tree.TreeDropZone = function(tree, config){
18375     this.allowParentInsert = false;
18376     this.allowContainerDrop = false;
18377     this.appendOnly = false;
18378     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18379     this.tree = tree;
18380     this.lastInsertClass = "x-tree-no-status";
18381     this.dragOverData = {};
18382 };
18383
18384 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18385     ddGroup : "TreeDD",
18386     scroll:  true,
18387     
18388     expandDelay : 1000,
18389     
18390     expandNode : function(node){
18391         if(node.hasChildNodes() && !node.isExpanded()){
18392             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18393         }
18394     },
18395     
18396     queueExpand : function(node){
18397         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18398     },
18399     
18400     cancelExpand : function(){
18401         if(this.expandProcId){
18402             clearTimeout(this.expandProcId);
18403             this.expandProcId = false;
18404         }
18405     },
18406     
18407     isValidDropPoint : function(n, pt, dd, e, data){
18408         if(!n || !data){ return false; }
18409         var targetNode = n.node;
18410         var dropNode = data.node;
18411         // default drop rules
18412         if(!(targetNode && targetNode.isTarget && pt)){
18413             return false;
18414         }
18415         if(pt == "append" && targetNode.allowChildren === false){
18416             return false;
18417         }
18418         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18419             return false;
18420         }
18421         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18422             return false;
18423         }
18424         // reuse the object
18425         var overEvent = this.dragOverData;
18426         overEvent.tree = this.tree;
18427         overEvent.target = targetNode;
18428         overEvent.data = data;
18429         overEvent.point = pt;
18430         overEvent.source = dd;
18431         overEvent.rawEvent = e;
18432         overEvent.dropNode = dropNode;
18433         overEvent.cancel = false;  
18434         var result = this.tree.fireEvent("nodedragover", overEvent);
18435         return overEvent.cancel === false && result !== false;
18436     },
18437     
18438     getDropPoint : function(e, n, dd)
18439     {
18440         var tn = n.node;
18441         if(tn.isRoot){
18442             return tn.allowChildren !== false ? "append" : false; // always append for root
18443         }
18444         var dragEl = n.ddel;
18445         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18446         var y = Roo.lib.Event.getPageY(e);
18447         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18448         
18449         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18450         var noAppend = tn.allowChildren === false;
18451         if(this.appendOnly || tn.parentNode.allowChildren === false){
18452             return noAppend ? false : "append";
18453         }
18454         var noBelow = false;
18455         if(!this.allowParentInsert){
18456             noBelow = tn.hasChildNodes() && tn.isExpanded();
18457         }
18458         var q = (b - t) / (noAppend ? 2 : 3);
18459         if(y >= t && y < (t + q)){
18460             return "above";
18461         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18462             return "below";
18463         }else{
18464             return "append";
18465         }
18466     },
18467     
18468     onNodeEnter : function(n, dd, e, data)
18469     {
18470         this.cancelExpand();
18471     },
18472     
18473     onNodeOver : function(n, dd, e, data)
18474     {
18475        
18476         var pt = this.getDropPoint(e, n, dd);
18477         var node = n.node;
18478         
18479         // auto node expand check
18480         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18481             this.queueExpand(node);
18482         }else if(pt != "append"){
18483             this.cancelExpand();
18484         }
18485         
18486         // set the insert point style on the target node
18487         var returnCls = this.dropNotAllowed;
18488         if(this.isValidDropPoint(n, pt, dd, e, data)){
18489            if(pt){
18490                var el = n.ddel;
18491                var cls;
18492                if(pt == "above"){
18493                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18494                    cls = "x-tree-drag-insert-above";
18495                }else if(pt == "below"){
18496                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18497                    cls = "x-tree-drag-insert-below";
18498                }else{
18499                    returnCls = "x-tree-drop-ok-append";
18500                    cls = "x-tree-drag-append";
18501                }
18502                if(this.lastInsertClass != cls){
18503                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18504                    this.lastInsertClass = cls;
18505                }
18506            }
18507        }
18508        return returnCls;
18509     },
18510     
18511     onNodeOut : function(n, dd, e, data){
18512         
18513         this.cancelExpand();
18514         this.removeDropIndicators(n);
18515     },
18516     
18517     onNodeDrop : function(n, dd, e, data){
18518         var point = this.getDropPoint(e, n, dd);
18519         var targetNode = n.node;
18520         targetNode.ui.startDrop();
18521         if(!this.isValidDropPoint(n, point, dd, e, data)){
18522             targetNode.ui.endDrop();
18523             return false;
18524         }
18525         // first try to find the drop node
18526         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18527         var dropEvent = {
18528             tree : this.tree,
18529             target: targetNode,
18530             data: data,
18531             point: point,
18532             source: dd,
18533             rawEvent: e,
18534             dropNode: dropNode,
18535             cancel: !dropNode   
18536         };
18537         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18538         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18539             targetNode.ui.endDrop();
18540             return false;
18541         }
18542         // allow target changing
18543         targetNode = dropEvent.target;
18544         if(point == "append" && !targetNode.isExpanded()){
18545             targetNode.expand(false, null, function(){
18546                 this.completeDrop(dropEvent);
18547             }.createDelegate(this));
18548         }else{
18549             this.completeDrop(dropEvent);
18550         }
18551         return true;
18552     },
18553     
18554     completeDrop : function(de){
18555         var ns = de.dropNode, p = de.point, t = de.target;
18556         if(!(ns instanceof Array)){
18557             ns = [ns];
18558         }
18559         var n;
18560         for(var i = 0, len = ns.length; i < len; i++){
18561             n = ns[i];
18562             if(p == "above"){
18563                 t.parentNode.insertBefore(n, t);
18564             }else if(p == "below"){
18565                 t.parentNode.insertBefore(n, t.nextSibling);
18566             }else{
18567                 t.appendChild(n);
18568             }
18569         }
18570         n.ui.focus();
18571         if(this.tree.hlDrop){
18572             n.ui.highlight();
18573         }
18574         t.ui.endDrop();
18575         this.tree.fireEvent("nodedrop", de);
18576     },
18577     
18578     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18579         if(this.tree.hlDrop){
18580             dropNode.ui.focus();
18581             dropNode.ui.highlight();
18582         }
18583         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18584     },
18585     
18586     getTree : function(){
18587         return this.tree;
18588     },
18589     
18590     removeDropIndicators : function(n){
18591         if(n && n.ddel){
18592             var el = n.ddel;
18593             Roo.fly(el).removeClass([
18594                     "x-tree-drag-insert-above",
18595                     "x-tree-drag-insert-below",
18596                     "x-tree-drag-append"]);
18597             this.lastInsertClass = "_noclass";
18598         }
18599     },
18600     
18601     beforeDragDrop : function(target, e, id){
18602         this.cancelExpand();
18603         return true;
18604     },
18605     
18606     afterRepair : function(data){
18607         if(data && Roo.enableFx){
18608             data.node.ui.highlight();
18609         }
18610         this.hideProxy();
18611     } 
18612     
18613 });
18614
18615 }
18616 /*
18617  * Based on:
18618  * Ext JS Library 1.1.1
18619  * Copyright(c) 2006-2007, Ext JS, LLC.
18620  *
18621  * Originally Released Under LGPL - original licence link has changed is not relivant.
18622  *
18623  * Fork - LGPL
18624  * <script type="text/javascript">
18625  */
18626  
18627
18628 if(Roo.dd.DragZone){
18629 Roo.tree.TreeDragZone = function(tree, config){
18630     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18631     this.tree = tree;
18632 };
18633
18634 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18635     ddGroup : "TreeDD",
18636    
18637     onBeforeDrag : function(data, e){
18638         var n = data.node;
18639         return n && n.draggable && !n.disabled;
18640     },
18641      
18642     
18643     onInitDrag : function(e){
18644         var data = this.dragData;
18645         this.tree.getSelectionModel().select(data.node);
18646         this.proxy.update("");
18647         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18648         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18649     },
18650     
18651     getRepairXY : function(e, data){
18652         return data.node.ui.getDDRepairXY();
18653     },
18654     
18655     onEndDrag : function(data, e){
18656         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18657         
18658         
18659     },
18660     
18661     onValidDrop : function(dd, e, id){
18662         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18663         this.hideProxy();
18664     },
18665     
18666     beforeInvalidDrop : function(e, id){
18667         // this scrolls the original position back into view
18668         var sm = this.tree.getSelectionModel();
18669         sm.clearSelections();
18670         sm.select(this.dragData.node);
18671     }
18672 });
18673 }/*
18674  * Based on:
18675  * Ext JS Library 1.1.1
18676  * Copyright(c) 2006-2007, Ext JS, LLC.
18677  *
18678  * Originally Released Under LGPL - original licence link has changed is not relivant.
18679  *
18680  * Fork - LGPL
18681  * <script type="text/javascript">
18682  */
18683 /**
18684  * @class Roo.tree.TreeEditor
18685  * @extends Roo.Editor
18686  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18687  * as the editor field.
18688  * @constructor
18689  * @param {Object} config (used to be the tree panel.)
18690  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18691  * 
18692  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
18693  * @cfg {Roo.form.TextField|Object} field The field configuration
18694  *
18695  * 
18696  */
18697 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
18698     var tree = config;
18699     var field;
18700     if (oldconfig) { // old style..
18701         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
18702     } else {
18703         // new style..
18704         tree = config.tree;
18705         config.field = config.field  || {};
18706         config.field.xtype = 'TextField';
18707         field = Roo.factory(config.field, Roo.form);
18708     }
18709     config = config || {};
18710     
18711     
18712     this.addEvents({
18713         /**
18714          * @event beforenodeedit
18715          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
18716          * false from the handler of this event.
18717          * @param {Editor} this
18718          * @param {Roo.tree.Node} node 
18719          */
18720         "beforenodeedit" : true
18721     });
18722     
18723     //Roo.log(config);
18724     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
18725
18726     this.tree = tree;
18727
18728     tree.on('beforeclick', this.beforeNodeClick, this);
18729     tree.getTreeEl().on('mousedown', this.hide, this);
18730     this.on('complete', this.updateNode, this);
18731     this.on('beforestartedit', this.fitToTree, this);
18732     this.on('startedit', this.bindScroll, this, {delay:10});
18733     this.on('specialkey', this.onSpecialKey, this);
18734 };
18735
18736 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18737     /**
18738      * @cfg {String} alignment
18739      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18740      */
18741     alignment: "l-l",
18742     // inherit
18743     autoSize: false,
18744     /**
18745      * @cfg {Boolean} hideEl
18746      * True to hide the bound element while the editor is displayed (defaults to false)
18747      */
18748     hideEl : false,
18749     /**
18750      * @cfg {String} cls
18751      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18752      */
18753     cls: "x-small-editor x-tree-editor",
18754     /**
18755      * @cfg {Boolean} shim
18756      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18757      */
18758     shim:false,
18759     // inherit
18760     shadow:"frame",
18761     /**
18762      * @cfg {Number} maxWidth
18763      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18764      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18765      * scroll and client offsets into account prior to each edit.
18766      */
18767     maxWidth: 250,
18768
18769     editDelay : 350,
18770
18771     // private
18772     fitToTree : function(ed, el){
18773         var td = this.tree.getTreeEl().dom, nd = el.dom;
18774         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18775             td.scrollLeft = nd.offsetLeft;
18776         }
18777         var w = Math.min(
18778                 this.maxWidth,
18779                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18780         this.setSize(w, '');
18781         
18782         return this.fireEvent('beforenodeedit', this, this.editNode);
18783         
18784     },
18785
18786     // private
18787     triggerEdit : function(node){
18788         this.completeEdit();
18789         this.editNode = node;
18790         this.startEdit(node.ui.textNode, node.text);
18791     },
18792
18793     // private
18794     bindScroll : function(){
18795         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18796     },
18797
18798     // private
18799     beforeNodeClick : function(node, e){
18800         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18801         this.lastClick = new Date();
18802         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18803             e.stopEvent();
18804             this.triggerEdit(node);
18805             return false;
18806         }
18807         return true;
18808     },
18809
18810     // private
18811     updateNode : function(ed, value){
18812         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18813         this.editNode.setText(value);
18814     },
18815
18816     // private
18817     onHide : function(){
18818         Roo.tree.TreeEditor.superclass.onHide.call(this);
18819         if(this.editNode){
18820             this.editNode.ui.focus();
18821         }
18822     },
18823
18824     // private
18825     onSpecialKey : function(field, e){
18826         var k = e.getKey();
18827         if(k == e.ESC){
18828             e.stopEvent();
18829             this.cancelEdit();
18830         }else if(k == e.ENTER && !e.hasModifier()){
18831             e.stopEvent();
18832             this.completeEdit();
18833         }
18834     }
18835 });//<Script type="text/javascript">
18836 /*
18837  * Based on:
18838  * Ext JS Library 1.1.1
18839  * Copyright(c) 2006-2007, Ext JS, LLC.
18840  *
18841  * Originally Released Under LGPL - original licence link has changed is not relivant.
18842  *
18843  * Fork - LGPL
18844  * <script type="text/javascript">
18845  */
18846  
18847 /**
18848  * Not documented??? - probably should be...
18849  */
18850
18851 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18852     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18853     
18854     renderElements : function(n, a, targetNode, bulkRender){
18855         //consel.log("renderElements?");
18856         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18857
18858         var t = n.getOwnerTree();
18859         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18860         
18861         var cols = t.columns;
18862         var bw = t.borderWidth;
18863         var c = cols[0];
18864         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18865          var cb = typeof a.checked == "boolean";
18866         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18867         var colcls = 'x-t-' + tid + '-c0';
18868         var buf = [
18869             '<li class="x-tree-node">',
18870             
18871                 
18872                 '<div class="x-tree-node-el ', a.cls,'">',
18873                     // extran...
18874                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18875                 
18876                 
18877                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18878                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18879                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18880                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18881                            (a.iconCls ? ' '+a.iconCls : ''),
18882                            '" unselectable="on" />',
18883                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18884                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18885                              
18886                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18887                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18888                             '<span unselectable="on" qtip="' + tx + '">',
18889                              tx,
18890                              '</span></a>' ,
18891                     '</div>',
18892                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18893                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18894                  ];
18895         for(var i = 1, len = cols.length; i < len; i++){
18896             c = cols[i];
18897             colcls = 'x-t-' + tid + '-c' +i;
18898             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18899             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18900                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18901                       "</div>");
18902          }
18903          
18904          buf.push(
18905             '</a>',
18906             '<div class="x-clear"></div></div>',
18907             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18908             "</li>");
18909         
18910         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18911             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18912                                 n.nextSibling.ui.getEl(), buf.join(""));
18913         }else{
18914             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18915         }
18916         var el = this.wrap.firstChild;
18917         this.elRow = el;
18918         this.elNode = el.firstChild;
18919         this.ranchor = el.childNodes[1];
18920         this.ctNode = this.wrap.childNodes[1];
18921         var cs = el.firstChild.childNodes;
18922         this.indentNode = cs[0];
18923         this.ecNode = cs[1];
18924         this.iconNode = cs[2];
18925         var index = 3;
18926         if(cb){
18927             this.checkbox = cs[3];
18928             index++;
18929         }
18930         this.anchor = cs[index];
18931         
18932         this.textNode = cs[index].firstChild;
18933         
18934         //el.on("click", this.onClick, this);
18935         //el.on("dblclick", this.onDblClick, this);
18936         
18937         
18938        // console.log(this);
18939     },
18940     initEvents : function(){
18941         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
18942         
18943             
18944         var a = this.ranchor;
18945
18946         var el = Roo.get(a);
18947
18948         if(Roo.isOpera){ // opera render bug ignores the CSS
18949             el.setStyle("text-decoration", "none");
18950         }
18951
18952         el.on("click", this.onClick, this);
18953         el.on("dblclick", this.onDblClick, this);
18954         el.on("contextmenu", this.onContextMenu, this);
18955         
18956     },
18957     
18958     /*onSelectedChange : function(state){
18959         if(state){
18960             this.focus();
18961             this.addClass("x-tree-selected");
18962         }else{
18963             //this.blur();
18964             this.removeClass("x-tree-selected");
18965         }
18966     },*/
18967     addClass : function(cls){
18968         if(this.elRow){
18969             Roo.fly(this.elRow).addClass(cls);
18970         }
18971         
18972     },
18973     
18974     
18975     removeClass : function(cls){
18976         if(this.elRow){
18977             Roo.fly(this.elRow).removeClass(cls);
18978         }
18979     }
18980
18981     
18982     
18983 });//<Script type="text/javascript">
18984
18985 /*
18986  * Based on:
18987  * Ext JS Library 1.1.1
18988  * Copyright(c) 2006-2007, Ext JS, LLC.
18989  *
18990  * Originally Released Under LGPL - original licence link has changed is not relivant.
18991  *
18992  * Fork - LGPL
18993  * <script type="text/javascript">
18994  */
18995  
18996
18997 /**
18998  * @class Roo.tree.ColumnTree
18999  * @extends Roo.data.TreePanel
19000  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19001  * @cfg {int} borderWidth  compined right/left border allowance
19002  * @constructor
19003  * @param {String/HTMLElement/Element} el The container element
19004  * @param {Object} config
19005  */
19006 Roo.tree.ColumnTree =  function(el, config)
19007 {
19008    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19009    this.addEvents({
19010         /**
19011         * @event resize
19012         * Fire this event on a container when it resizes
19013         * @param {int} w Width
19014         * @param {int} h Height
19015         */
19016        "resize" : true
19017     });
19018     this.on('resize', this.onResize, this);
19019 };
19020
19021 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19022     //lines:false,
19023     
19024     
19025     borderWidth: Roo.isBorderBox ? 0 : 2, 
19026     headEls : false,
19027     
19028     render : function(){
19029         // add the header.....
19030        
19031         Roo.tree.ColumnTree.superclass.render.apply(this);
19032         
19033         this.el.addClass('x-column-tree');
19034         
19035         this.headers = this.el.createChild(
19036             {cls:'x-tree-headers'},this.innerCt.dom);
19037    
19038         var cols = this.columns, c;
19039         var totalWidth = 0;
19040         this.headEls = [];
19041         var  len = cols.length;
19042         for(var i = 0; i < len; i++){
19043              c = cols[i];
19044              totalWidth += c.width;
19045             this.headEls.push(this.headers.createChild({
19046                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19047                  cn: {
19048                      cls:'x-tree-hd-text',
19049                      html: c.header
19050                  },
19051                  style:'width:'+(c.width-this.borderWidth)+'px;'
19052              }));
19053         }
19054         this.headers.createChild({cls:'x-clear'});
19055         // prevent floats from wrapping when clipped
19056         this.headers.setWidth(totalWidth);
19057         //this.innerCt.setWidth(totalWidth);
19058         this.innerCt.setStyle({ overflow: 'auto' });
19059         this.onResize(this.width, this.height);
19060              
19061         
19062     },
19063     onResize : function(w,h)
19064     {
19065         this.height = h;
19066         this.width = w;
19067         // resize cols..
19068         this.innerCt.setWidth(this.width);
19069         this.innerCt.setHeight(this.height-20);
19070         
19071         // headers...
19072         var cols = this.columns, c;
19073         var totalWidth = 0;
19074         var expEl = false;
19075         var len = cols.length;
19076         for(var i = 0; i < len; i++){
19077             c = cols[i];
19078             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19079                 // it's the expander..
19080                 expEl  = this.headEls[i];
19081                 continue;
19082             }
19083             totalWidth += c.width;
19084             
19085         }
19086         if (expEl) {
19087             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19088         }
19089         this.headers.setWidth(w-20);
19090
19091         
19092         
19093         
19094     }
19095 });
19096 /*
19097  * Based on:
19098  * Ext JS Library 1.1.1
19099  * Copyright(c) 2006-2007, Ext JS, LLC.
19100  *
19101  * Originally Released Under LGPL - original licence link has changed is not relivant.
19102  *
19103  * Fork - LGPL
19104  * <script type="text/javascript">
19105  */
19106  
19107 /**
19108  * @class Roo.menu.Menu
19109  * @extends Roo.util.Observable
19110  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19111  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19112  * @constructor
19113  * Creates a new Menu
19114  * @param {Object} config Configuration options
19115  */
19116 Roo.menu.Menu = function(config){
19117     Roo.apply(this, config);
19118     this.id = this.id || Roo.id();
19119     this.addEvents({
19120         /**
19121          * @event beforeshow
19122          * Fires before this menu is displayed
19123          * @param {Roo.menu.Menu} this
19124          */
19125         beforeshow : true,
19126         /**
19127          * @event beforehide
19128          * Fires before this menu is hidden
19129          * @param {Roo.menu.Menu} this
19130          */
19131         beforehide : true,
19132         /**
19133          * @event show
19134          * Fires after this menu is displayed
19135          * @param {Roo.menu.Menu} this
19136          */
19137         show : true,
19138         /**
19139          * @event hide
19140          * Fires after this menu is hidden
19141          * @param {Roo.menu.Menu} this
19142          */
19143         hide : true,
19144         /**
19145          * @event click
19146          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19147          * @param {Roo.menu.Menu} this
19148          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19149          * @param {Roo.EventObject} e
19150          */
19151         click : true,
19152         /**
19153          * @event mouseover
19154          * Fires when the mouse is hovering over this menu
19155          * @param {Roo.menu.Menu} this
19156          * @param {Roo.EventObject} e
19157          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19158          */
19159         mouseover : true,
19160         /**
19161          * @event mouseout
19162          * Fires when the mouse exits this menu
19163          * @param {Roo.menu.Menu} this
19164          * @param {Roo.EventObject} e
19165          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19166          */
19167         mouseout : true,
19168         /**
19169          * @event itemclick
19170          * Fires when a menu item contained in this menu is clicked
19171          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19172          * @param {Roo.EventObject} e
19173          */
19174         itemclick: true
19175     });
19176     if (this.registerMenu) {
19177         Roo.menu.MenuMgr.register(this);
19178     }
19179     
19180     var mis = this.items;
19181     this.items = new Roo.util.MixedCollection();
19182     if(mis){
19183         this.add.apply(this, mis);
19184     }
19185 };
19186
19187 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19188     /**
19189      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19190      */
19191     minWidth : 120,
19192     /**
19193      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19194      * for bottom-right shadow (defaults to "sides")
19195      */
19196     shadow : "sides",
19197     /**
19198      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19199      * this menu (defaults to "tl-tr?")
19200      */
19201     subMenuAlign : "tl-tr?",
19202     /**
19203      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19204      * relative to its element of origin (defaults to "tl-bl?")
19205      */
19206     defaultAlign : "tl-bl?",
19207     /**
19208      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19209      */
19210     allowOtherMenus : false,
19211     /**
19212      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19213      */
19214     registerMenu : true,
19215
19216     hidden:true,
19217
19218     // private
19219     render : function(){
19220         if(this.el){
19221             return;
19222         }
19223         var el = this.el = new Roo.Layer({
19224             cls: "x-menu",
19225             shadow:this.shadow,
19226             constrain: false,
19227             parentEl: this.parentEl || document.body,
19228             zindex:15000
19229         });
19230
19231         this.keyNav = new Roo.menu.MenuNav(this);
19232
19233         if(this.plain){
19234             el.addClass("x-menu-plain");
19235         }
19236         if(this.cls){
19237             el.addClass(this.cls);
19238         }
19239         // generic focus element
19240         this.focusEl = el.createChild({
19241             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19242         });
19243         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19244         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
19245         
19246         ul.on("mouseover", this.onMouseOver, this);
19247         ul.on("mouseout", this.onMouseOut, this);
19248         this.items.each(function(item){
19249             if (item.hidden) {
19250                 return;
19251             }
19252             
19253             var li = document.createElement("li");
19254             li.className = "x-menu-list-item";
19255             ul.dom.appendChild(li);
19256             item.render(li, this);
19257         }, this);
19258         this.ul = ul;
19259         this.autoWidth();
19260     },
19261
19262     // private
19263     autoWidth : function(){
19264         var el = this.el, ul = this.ul;
19265         if(!el){
19266             return;
19267         }
19268         var w = this.width;
19269         if(w){
19270             el.setWidth(w);
19271         }else if(Roo.isIE){
19272             el.setWidth(this.minWidth);
19273             var t = el.dom.offsetWidth; // force recalc
19274             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19275         }
19276     },
19277
19278     // private
19279     delayAutoWidth : function(){
19280         if(this.rendered){
19281             if(!this.awTask){
19282                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19283             }
19284             this.awTask.delay(20);
19285         }
19286     },
19287
19288     // private
19289     findTargetItem : function(e){
19290         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19291         if(t && t.menuItemId){
19292             return this.items.get(t.menuItemId);
19293         }
19294     },
19295
19296     // private
19297     onClick : function(e){
19298         Roo.log("menu.onClick");
19299         var t = this.findTargetItem(e);
19300         if(!t){
19301             return;
19302         }
19303         Roo.log(e);
19304         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
19305             if(t == this.activeItem && t.shouldDeactivate(e)){
19306                 this.activeItem.deactivate();
19307                 delete this.activeItem;
19308                 return;
19309             }
19310             if(t.canActivate){
19311                 this.setActiveItem(t, true);
19312             }
19313             return;
19314             
19315             
19316         }
19317         
19318         t.onClick(e);
19319         this.fireEvent("click", this, t, e);
19320     },
19321
19322     // private
19323     setActiveItem : function(item, autoExpand){
19324         if(item != this.activeItem){
19325             if(this.activeItem){
19326                 this.activeItem.deactivate();
19327             }
19328             this.activeItem = item;
19329             item.activate(autoExpand);
19330         }else if(autoExpand){
19331             item.expandMenu();
19332         }
19333     },
19334
19335     // private
19336     tryActivate : function(start, step){
19337         var items = this.items;
19338         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19339             var item = items.get(i);
19340             if(!item.disabled && item.canActivate){
19341                 this.setActiveItem(item, false);
19342                 return item;
19343             }
19344         }
19345         return false;
19346     },
19347
19348     // private
19349     onMouseOver : function(e){
19350         var t;
19351         if(t = this.findTargetItem(e)){
19352             if(t.canActivate && !t.disabled){
19353                 this.setActiveItem(t, true);
19354             }
19355         }
19356         this.fireEvent("mouseover", this, e, t);
19357     },
19358
19359     // private
19360     onMouseOut : function(e){
19361         var t;
19362         if(t = this.findTargetItem(e)){
19363             if(t == this.activeItem && t.shouldDeactivate(e)){
19364                 this.activeItem.deactivate();
19365                 delete this.activeItem;
19366             }
19367         }
19368         this.fireEvent("mouseout", this, e, t);
19369     },
19370
19371     /**
19372      * Read-only.  Returns true if the menu is currently displayed, else false.
19373      * @type Boolean
19374      */
19375     isVisible : function(){
19376         return this.el && !this.hidden;
19377     },
19378
19379     /**
19380      * Displays this menu relative to another element
19381      * @param {String/HTMLElement/Roo.Element} element The element to align to
19382      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19383      * the element (defaults to this.defaultAlign)
19384      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19385      */
19386     show : function(el, pos, parentMenu){
19387         this.parentMenu = parentMenu;
19388         if(!this.el){
19389             this.render();
19390         }
19391         this.fireEvent("beforeshow", this);
19392         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19393     },
19394
19395     /**
19396      * Displays this menu at a specific xy position
19397      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19398      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19399      */
19400     showAt : function(xy, parentMenu, /* private: */_e){
19401         this.parentMenu = parentMenu;
19402         if(!this.el){
19403             this.render();
19404         }
19405         if(_e !== false){
19406             this.fireEvent("beforeshow", this);
19407             xy = this.el.adjustForConstraints(xy);
19408         }
19409         this.el.setXY(xy);
19410         this.el.show();
19411         this.hidden = false;
19412         this.focus();
19413         this.fireEvent("show", this);
19414     },
19415
19416     focus : function(){
19417         if(!this.hidden){
19418             this.doFocus.defer(50, this);
19419         }
19420     },
19421
19422     doFocus : function(){
19423         if(!this.hidden){
19424             this.focusEl.focus();
19425         }
19426     },
19427
19428     /**
19429      * Hides this menu and optionally all parent menus
19430      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19431      */
19432     hide : function(deep){
19433         if(this.el && this.isVisible()){
19434             this.fireEvent("beforehide", this);
19435             if(this.activeItem){
19436                 this.activeItem.deactivate();
19437                 this.activeItem = null;
19438             }
19439             this.el.hide();
19440             this.hidden = true;
19441             this.fireEvent("hide", this);
19442         }
19443         if(deep === true && this.parentMenu){
19444             this.parentMenu.hide(true);
19445         }
19446     },
19447
19448     /**
19449      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19450      * Any of the following are valid:
19451      * <ul>
19452      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19453      * <li>An HTMLElement object which will be converted to a menu item</li>
19454      * <li>A menu item config object that will be created as a new menu item</li>
19455      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19456      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19457      * </ul>
19458      * Usage:
19459      * <pre><code>
19460 // Create the menu
19461 var menu = new Roo.menu.Menu();
19462
19463 // Create a menu item to add by reference
19464 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19465
19466 // Add a bunch of items at once using different methods.
19467 // Only the last item added will be returned.
19468 var item = menu.add(
19469     menuItem,                // add existing item by ref
19470     'Dynamic Item',          // new TextItem
19471     '-',                     // new separator
19472     { text: 'Config Item' }  // new item by config
19473 );
19474 </code></pre>
19475      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19476      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19477      */
19478     add : function(){
19479         var a = arguments, l = a.length, item;
19480         for(var i = 0; i < l; i++){
19481             var el = a[i];
19482             if ((typeof(el) == "object") && el.xtype && el.xns) {
19483                 el = Roo.factory(el, Roo.menu);
19484             }
19485             
19486             if(el.render){ // some kind of Item
19487                 item = this.addItem(el);
19488             }else if(typeof el == "string"){ // string
19489                 if(el == "separator" || el == "-"){
19490                     item = this.addSeparator();
19491                 }else{
19492                     item = this.addText(el);
19493                 }
19494             }else if(el.tagName || el.el){ // element
19495                 item = this.addElement(el);
19496             }else if(typeof el == "object"){ // must be menu item config?
19497                 item = this.addMenuItem(el);
19498             }
19499         }
19500         return item;
19501     },
19502
19503     /**
19504      * Returns this menu's underlying {@link Roo.Element} object
19505      * @return {Roo.Element} The element
19506      */
19507     getEl : function(){
19508         if(!this.el){
19509             this.render();
19510         }
19511         return this.el;
19512     },
19513
19514     /**
19515      * Adds a separator bar to the menu
19516      * @return {Roo.menu.Item} The menu item that was added
19517      */
19518     addSeparator : function(){
19519         return this.addItem(new Roo.menu.Separator());
19520     },
19521
19522     /**
19523      * Adds an {@link Roo.Element} object to the menu
19524      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19525      * @return {Roo.menu.Item} The menu item that was added
19526      */
19527     addElement : function(el){
19528         return this.addItem(new Roo.menu.BaseItem(el));
19529     },
19530
19531     /**
19532      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19533      * @param {Roo.menu.Item} item The menu item to add
19534      * @return {Roo.menu.Item} The menu item that was added
19535      */
19536     addItem : function(item){
19537         this.items.add(item);
19538         if(this.ul){
19539             var li = document.createElement("li");
19540             li.className = "x-menu-list-item";
19541             this.ul.dom.appendChild(li);
19542             item.render(li, this);
19543             this.delayAutoWidth();
19544         }
19545         return item;
19546     },
19547
19548     /**
19549      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19550      * @param {Object} config A MenuItem config object
19551      * @return {Roo.menu.Item} The menu item that was added
19552      */
19553     addMenuItem : function(config){
19554         if(!(config instanceof Roo.menu.Item)){
19555             if(typeof config.checked == "boolean"){ // must be check menu item config?
19556                 config = new Roo.menu.CheckItem(config);
19557             }else{
19558                 config = new Roo.menu.Item(config);
19559             }
19560         }
19561         return this.addItem(config);
19562     },
19563
19564     /**
19565      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19566      * @param {String} text The text to display in the menu item
19567      * @return {Roo.menu.Item} The menu item that was added
19568      */
19569     addText : function(text){
19570         return this.addItem(new Roo.menu.TextItem({ text : text }));
19571     },
19572
19573     /**
19574      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19575      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19576      * @param {Roo.menu.Item} item The menu item to add
19577      * @return {Roo.menu.Item} The menu item that was added
19578      */
19579     insert : function(index, item){
19580         this.items.insert(index, item);
19581         if(this.ul){
19582             var li = document.createElement("li");
19583             li.className = "x-menu-list-item";
19584             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19585             item.render(li, this);
19586             this.delayAutoWidth();
19587         }
19588         return item;
19589     },
19590
19591     /**
19592      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19593      * @param {Roo.menu.Item} item The menu item to remove
19594      */
19595     remove : function(item){
19596         this.items.removeKey(item.id);
19597         item.destroy();
19598     },
19599
19600     /**
19601      * Removes and destroys all items in the menu
19602      */
19603     removeAll : function(){
19604         var f;
19605         while(f = this.items.first()){
19606             this.remove(f);
19607         }
19608     }
19609 });
19610
19611 // MenuNav is a private utility class used internally by the Menu
19612 Roo.menu.MenuNav = function(menu){
19613     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19614     this.scope = this.menu = menu;
19615 };
19616
19617 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19618     doRelay : function(e, h){
19619         var k = e.getKey();
19620         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19621             this.menu.tryActivate(0, 1);
19622             return false;
19623         }
19624         return h.call(this.scope || this, e, this.menu);
19625     },
19626
19627     up : function(e, m){
19628         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19629             m.tryActivate(m.items.length-1, -1);
19630         }
19631     },
19632
19633     down : function(e, m){
19634         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19635             m.tryActivate(0, 1);
19636         }
19637     },
19638
19639     right : function(e, m){
19640         if(m.activeItem){
19641             m.activeItem.expandMenu(true);
19642         }
19643     },
19644
19645     left : function(e, m){
19646         m.hide();
19647         if(m.parentMenu && m.parentMenu.activeItem){
19648             m.parentMenu.activeItem.activate();
19649         }
19650     },
19651
19652     enter : function(e, m){
19653         if(m.activeItem){
19654             e.stopPropagation();
19655             m.activeItem.onClick(e);
19656             m.fireEvent("click", this, m.activeItem);
19657             return true;
19658         }
19659     }
19660 });/*
19661  * Based on:
19662  * Ext JS Library 1.1.1
19663  * Copyright(c) 2006-2007, Ext JS, LLC.
19664  *
19665  * Originally Released Under LGPL - original licence link has changed is not relivant.
19666  *
19667  * Fork - LGPL
19668  * <script type="text/javascript">
19669  */
19670  
19671 /**
19672  * @class Roo.menu.MenuMgr
19673  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19674  * @singleton
19675  */
19676 Roo.menu.MenuMgr = function(){
19677    var menus, active, groups = {}, attached = false, lastShow = new Date();
19678
19679    // private - called when first menu is created
19680    function init(){
19681        menus = {};
19682        active = new Roo.util.MixedCollection();
19683        Roo.get(document).addKeyListener(27, function(){
19684            if(active.length > 0){
19685                hideAll();
19686            }
19687        });
19688    }
19689
19690    // private
19691    function hideAll(){
19692        if(active && active.length > 0){
19693            var c = active.clone();
19694            c.each(function(m){
19695                m.hide();
19696            });
19697        }
19698    }
19699
19700    // private
19701    function onHide(m){
19702        active.remove(m);
19703        if(active.length < 1){
19704            Roo.get(document).un("mousedown", onMouseDown);
19705            attached = false;
19706        }
19707    }
19708
19709    // private
19710    function onShow(m){
19711        var last = active.last();
19712        lastShow = new Date();
19713        active.add(m);
19714        if(!attached){
19715            Roo.get(document).on("mousedown", onMouseDown);
19716            attached = true;
19717        }
19718        if(m.parentMenu){
19719           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19720           m.parentMenu.activeChild = m;
19721        }else if(last && last.isVisible()){
19722           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19723        }
19724    }
19725
19726    // private
19727    function onBeforeHide(m){
19728        if(m.activeChild){
19729            m.activeChild.hide();
19730        }
19731        if(m.autoHideTimer){
19732            clearTimeout(m.autoHideTimer);
19733            delete m.autoHideTimer;
19734        }
19735    }
19736
19737    // private
19738    function onBeforeShow(m){
19739        var pm = m.parentMenu;
19740        if(!pm && !m.allowOtherMenus){
19741            hideAll();
19742        }else if(pm && pm.activeChild && active != m){
19743            pm.activeChild.hide();
19744        }
19745    }
19746
19747    // private
19748    function onMouseDown(e){
19749        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19750            hideAll();
19751        }
19752    }
19753
19754    // private
19755    function onBeforeCheck(mi, state){
19756        if(state){
19757            var g = groups[mi.group];
19758            for(var i = 0, l = g.length; i < l; i++){
19759                if(g[i] != mi){
19760                    g[i].setChecked(false);
19761                }
19762            }
19763        }
19764    }
19765
19766    return {
19767
19768        /**
19769         * Hides all menus that are currently visible
19770         */
19771        hideAll : function(){
19772             hideAll();  
19773        },
19774
19775        // private
19776        register : function(menu){
19777            if(!menus){
19778                init();
19779            }
19780            menus[menu.id] = menu;
19781            menu.on("beforehide", onBeforeHide);
19782            menu.on("hide", onHide);
19783            menu.on("beforeshow", onBeforeShow);
19784            menu.on("show", onShow);
19785            var g = menu.group;
19786            if(g && menu.events["checkchange"]){
19787                if(!groups[g]){
19788                    groups[g] = [];
19789                }
19790                groups[g].push(menu);
19791                menu.on("checkchange", onCheck);
19792            }
19793        },
19794
19795         /**
19796          * Returns a {@link Roo.menu.Menu} object
19797          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19798          * be used to generate and return a new Menu instance.
19799          */
19800        get : function(menu){
19801            if(typeof menu == "string"){ // menu id
19802                return menus[menu];
19803            }else if(menu.events){  // menu instance
19804                return menu;
19805            }else if(typeof menu.length == 'number'){ // array of menu items?
19806                return new Roo.menu.Menu({items:menu});
19807            }else{ // otherwise, must be a config
19808                return new Roo.menu.Menu(menu);
19809            }
19810        },
19811
19812        // private
19813        unregister : function(menu){
19814            delete menus[menu.id];
19815            menu.un("beforehide", onBeforeHide);
19816            menu.un("hide", onHide);
19817            menu.un("beforeshow", onBeforeShow);
19818            menu.un("show", onShow);
19819            var g = menu.group;
19820            if(g && menu.events["checkchange"]){
19821                groups[g].remove(menu);
19822                menu.un("checkchange", onCheck);
19823            }
19824        },
19825
19826        // private
19827        registerCheckable : function(menuItem){
19828            var g = menuItem.group;
19829            if(g){
19830                if(!groups[g]){
19831                    groups[g] = [];
19832                }
19833                groups[g].push(menuItem);
19834                menuItem.on("beforecheckchange", onBeforeCheck);
19835            }
19836        },
19837
19838        // private
19839        unregisterCheckable : function(menuItem){
19840            var g = menuItem.group;
19841            if(g){
19842                groups[g].remove(menuItem);
19843                menuItem.un("beforecheckchange", onBeforeCheck);
19844            }
19845        }
19846    };
19847 }();/*
19848  * Based on:
19849  * Ext JS Library 1.1.1
19850  * Copyright(c) 2006-2007, Ext JS, LLC.
19851  *
19852  * Originally Released Under LGPL - original licence link has changed is not relivant.
19853  *
19854  * Fork - LGPL
19855  * <script type="text/javascript">
19856  */
19857  
19858
19859 /**
19860  * @class Roo.menu.BaseItem
19861  * @extends Roo.Component
19862  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19863  * management and base configuration options shared by all menu components.
19864  * @constructor
19865  * Creates a new BaseItem
19866  * @param {Object} config Configuration options
19867  */
19868 Roo.menu.BaseItem = function(config){
19869     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19870
19871     this.addEvents({
19872         /**
19873          * @event click
19874          * Fires when this item is clicked
19875          * @param {Roo.menu.BaseItem} this
19876          * @param {Roo.EventObject} e
19877          */
19878         click: true,
19879         /**
19880          * @event activate
19881          * Fires when this item is activated
19882          * @param {Roo.menu.BaseItem} this
19883          */
19884         activate : true,
19885         /**
19886          * @event deactivate
19887          * Fires when this item is deactivated
19888          * @param {Roo.menu.BaseItem} this
19889          */
19890         deactivate : true
19891     });
19892
19893     if(this.handler){
19894         this.on("click", this.handler, this.scope, true);
19895     }
19896 };
19897
19898 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19899     /**
19900      * @cfg {Function} handler
19901      * A function that will handle the click event of this menu item (defaults to undefined)
19902      */
19903     /**
19904      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19905      */
19906     canActivate : false,
19907     
19908      /**
19909      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
19910      */
19911     hidden: false,
19912     
19913     /**
19914      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19915      */
19916     activeClass : "x-menu-item-active",
19917     /**
19918      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19919      */
19920     hideOnClick : true,
19921     /**
19922      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19923      */
19924     hideDelay : 100,
19925
19926     // private
19927     ctype: "Roo.menu.BaseItem",
19928
19929     // private
19930     actionMode : "container",
19931
19932     // private
19933     render : function(container, parentMenu){
19934         this.parentMenu = parentMenu;
19935         Roo.menu.BaseItem.superclass.render.call(this, container);
19936         this.container.menuItemId = this.id;
19937     },
19938
19939     // private
19940     onRender : function(container, position){
19941         this.el = Roo.get(this.el);
19942         container.dom.appendChild(this.el.dom);
19943     },
19944
19945     // private
19946     onClick : function(e){
19947         if(!this.disabled && this.fireEvent("click", this, e) !== false
19948                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19949             this.handleClick(e);
19950         }else{
19951             e.stopEvent();
19952         }
19953     },
19954
19955     // private
19956     activate : function(){
19957         if(this.disabled){
19958             return false;
19959         }
19960         var li = this.container;
19961         li.addClass(this.activeClass);
19962         this.region = li.getRegion().adjust(2, 2, -2, -2);
19963         this.fireEvent("activate", this);
19964         return true;
19965     },
19966
19967     // private
19968     deactivate : function(){
19969         this.container.removeClass(this.activeClass);
19970         this.fireEvent("deactivate", this);
19971     },
19972
19973     // private
19974     shouldDeactivate : function(e){
19975         return !this.region || !this.region.contains(e.getPoint());
19976     },
19977
19978     // private
19979     handleClick : function(e){
19980         if(this.hideOnClick){
19981             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
19982         }
19983     },
19984
19985     // private
19986     expandMenu : function(autoActivate){
19987         // do nothing
19988     },
19989
19990     // private
19991     hideMenu : function(){
19992         // do nothing
19993     }
19994 });/*
19995  * Based on:
19996  * Ext JS Library 1.1.1
19997  * Copyright(c) 2006-2007, Ext JS, LLC.
19998  *
19999  * Originally Released Under LGPL - original licence link has changed is not relivant.
20000  *
20001  * Fork - LGPL
20002  * <script type="text/javascript">
20003  */
20004  
20005 /**
20006  * @class Roo.menu.Adapter
20007  * @extends Roo.menu.BaseItem
20008  * 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.
20009  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20010  * @constructor
20011  * Creates a new Adapter
20012  * @param {Object} config Configuration options
20013  */
20014 Roo.menu.Adapter = function(component, config){
20015     Roo.menu.Adapter.superclass.constructor.call(this, config);
20016     this.component = component;
20017 };
20018 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20019     // private
20020     canActivate : true,
20021
20022     // private
20023     onRender : function(container, position){
20024         this.component.render(container);
20025         this.el = this.component.getEl();
20026     },
20027
20028     // private
20029     activate : function(){
20030         if(this.disabled){
20031             return false;
20032         }
20033         this.component.focus();
20034         this.fireEvent("activate", this);
20035         return true;
20036     },
20037
20038     // private
20039     deactivate : function(){
20040         this.fireEvent("deactivate", this);
20041     },
20042
20043     // private
20044     disable : function(){
20045         this.component.disable();
20046         Roo.menu.Adapter.superclass.disable.call(this);
20047     },
20048
20049     // private
20050     enable : function(){
20051         this.component.enable();
20052         Roo.menu.Adapter.superclass.enable.call(this);
20053     }
20054 });/*
20055  * Based on:
20056  * Ext JS Library 1.1.1
20057  * Copyright(c) 2006-2007, Ext JS, LLC.
20058  *
20059  * Originally Released Under LGPL - original licence link has changed is not relivant.
20060  *
20061  * Fork - LGPL
20062  * <script type="text/javascript">
20063  */
20064
20065 /**
20066  * @class Roo.menu.TextItem
20067  * @extends Roo.menu.BaseItem
20068  * Adds a static text string to a menu, usually used as either a heading or group separator.
20069  * Note: old style constructor with text is still supported.
20070  * 
20071  * @constructor
20072  * Creates a new TextItem
20073  * @param {Object} cfg Configuration
20074  */
20075 Roo.menu.TextItem = function(cfg){
20076     if (typeof(cfg) == 'string') {
20077         this.text = cfg;
20078     } else {
20079         Roo.apply(this,cfg);
20080     }
20081     
20082     Roo.menu.TextItem.superclass.constructor.call(this);
20083 };
20084
20085 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20086     /**
20087      * @cfg {Boolean} text Text to show on item.
20088      */
20089     text : '',
20090     
20091     /**
20092      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20093      */
20094     hideOnClick : false,
20095     /**
20096      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20097      */
20098     itemCls : "x-menu-text",
20099
20100     // private
20101     onRender : function(){
20102         var s = document.createElement("span");
20103         s.className = this.itemCls;
20104         s.innerHTML = this.text;
20105         this.el = s;
20106         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20107     }
20108 });/*
20109  * Based on:
20110  * Ext JS Library 1.1.1
20111  * Copyright(c) 2006-2007, Ext JS, LLC.
20112  *
20113  * Originally Released Under LGPL - original licence link has changed is not relivant.
20114  *
20115  * Fork - LGPL
20116  * <script type="text/javascript">
20117  */
20118
20119 /**
20120  * @class Roo.menu.Separator
20121  * @extends Roo.menu.BaseItem
20122  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20123  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20124  * @constructor
20125  * @param {Object} config Configuration options
20126  */
20127 Roo.menu.Separator = function(config){
20128     Roo.menu.Separator.superclass.constructor.call(this, config);
20129 };
20130
20131 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20132     /**
20133      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20134      */
20135     itemCls : "x-menu-sep",
20136     /**
20137      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20138      */
20139     hideOnClick : false,
20140
20141     // private
20142     onRender : function(li){
20143         var s = document.createElement("span");
20144         s.className = this.itemCls;
20145         s.innerHTML = "&#160;";
20146         this.el = s;
20147         li.addClass("x-menu-sep-li");
20148         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20149     }
20150 });/*
20151  * Based on:
20152  * Ext JS Library 1.1.1
20153  * Copyright(c) 2006-2007, Ext JS, LLC.
20154  *
20155  * Originally Released Under LGPL - original licence link has changed is not relivant.
20156  *
20157  * Fork - LGPL
20158  * <script type="text/javascript">
20159  */
20160 /**
20161  * @class Roo.menu.Item
20162  * @extends Roo.menu.BaseItem
20163  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20164  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20165  * activation and click handling.
20166  * @constructor
20167  * Creates a new Item
20168  * @param {Object} config Configuration options
20169  */
20170 Roo.menu.Item = function(config){
20171     Roo.menu.Item.superclass.constructor.call(this, config);
20172     if(this.menu){
20173         this.menu = Roo.menu.MenuMgr.get(this.menu);
20174     }
20175 };
20176 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20177     
20178     /**
20179      * @cfg {String} text
20180      * The text to show on the menu item.
20181      */
20182     text: '',
20183      /**
20184      * @cfg {String} HTML to render in menu
20185      * The text to show on the menu item (HTML version).
20186      */
20187     html: '',
20188     /**
20189      * @cfg {String} icon
20190      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20191      */
20192     icon: undefined,
20193     /**
20194      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20195      */
20196     itemCls : "x-menu-item",
20197     /**
20198      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20199      */
20200     canActivate : true,
20201     /**
20202      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20203      */
20204     showDelay: 200,
20205     // doc'd in BaseItem
20206     hideDelay: 200,
20207
20208     // private
20209     ctype: "Roo.menu.Item",
20210     
20211     // private
20212     onRender : function(container, position){
20213         var el = document.createElement("a");
20214         el.hideFocus = true;
20215         el.unselectable = "on";
20216         el.href = this.href || "#";
20217         if(this.hrefTarget){
20218             el.target = this.hrefTarget;
20219         }
20220         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20221         
20222         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20223         
20224         el.innerHTML = String.format(
20225                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20226                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20227         this.el = el;
20228         Roo.menu.Item.superclass.onRender.call(this, container, position);
20229     },
20230
20231     /**
20232      * Sets the text to display in this menu item
20233      * @param {String} text The text to display
20234      * @param {Boolean} isHTML true to indicate text is pure html.
20235      */
20236     setText : function(text, isHTML){
20237         if (isHTML) {
20238             this.html = text;
20239         } else {
20240             this.text = text;
20241             this.html = '';
20242         }
20243         if(this.rendered){
20244             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20245      
20246             this.el.update(String.format(
20247                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20248                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20249             this.parentMenu.autoWidth();
20250         }
20251     },
20252
20253     // private
20254     handleClick : function(e){
20255         if(!this.href){ // if no link defined, stop the event automatically
20256             e.stopEvent();
20257         }
20258         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20259     },
20260
20261     // private
20262     activate : function(autoExpand){
20263         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20264             this.focus();
20265             if(autoExpand){
20266                 this.expandMenu();
20267             }
20268         }
20269         return true;
20270     },
20271
20272     // private
20273     shouldDeactivate : function(e){
20274         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20275             if(this.menu && this.menu.isVisible()){
20276                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20277             }
20278             return true;
20279         }
20280         return false;
20281     },
20282
20283     // private
20284     deactivate : function(){
20285         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20286         this.hideMenu();
20287     },
20288
20289     // private
20290     expandMenu : function(autoActivate){
20291         if(!this.disabled && this.menu){
20292             clearTimeout(this.hideTimer);
20293             delete this.hideTimer;
20294             if(!this.menu.isVisible() && !this.showTimer){
20295                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20296             }else if (this.menu.isVisible() && autoActivate){
20297                 this.menu.tryActivate(0, 1);
20298             }
20299         }
20300     },
20301
20302     // private
20303     deferExpand : function(autoActivate){
20304         delete this.showTimer;
20305         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20306         if(autoActivate){
20307             this.menu.tryActivate(0, 1);
20308         }
20309     },
20310
20311     // private
20312     hideMenu : function(){
20313         clearTimeout(this.showTimer);
20314         delete this.showTimer;
20315         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20316             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20317         }
20318     },
20319
20320     // private
20321     deferHide : function(){
20322         delete this.hideTimer;
20323         this.menu.hide();
20324     }
20325 });/*
20326  * Based on:
20327  * Ext JS Library 1.1.1
20328  * Copyright(c) 2006-2007, Ext JS, LLC.
20329  *
20330  * Originally Released Under LGPL - original licence link has changed is not relivant.
20331  *
20332  * Fork - LGPL
20333  * <script type="text/javascript">
20334  */
20335  
20336 /**
20337  * @class Roo.menu.CheckItem
20338  * @extends Roo.menu.Item
20339  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20340  * @constructor
20341  * Creates a new CheckItem
20342  * @param {Object} config Configuration options
20343  */
20344 Roo.menu.CheckItem = function(config){
20345     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20346     this.addEvents({
20347         /**
20348          * @event beforecheckchange
20349          * Fires before the checked value is set, providing an opportunity to cancel if needed
20350          * @param {Roo.menu.CheckItem} this
20351          * @param {Boolean} checked The new checked value that will be set
20352          */
20353         "beforecheckchange" : true,
20354         /**
20355          * @event checkchange
20356          * Fires after the checked value has been set
20357          * @param {Roo.menu.CheckItem} this
20358          * @param {Boolean} checked The checked value that was set
20359          */
20360         "checkchange" : true
20361     });
20362     if(this.checkHandler){
20363         this.on('checkchange', this.checkHandler, this.scope);
20364     }
20365 };
20366 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20367     /**
20368      * @cfg {String} group
20369      * All check items with the same group name will automatically be grouped into a single-select
20370      * radio button group (defaults to '')
20371      */
20372     /**
20373      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20374      */
20375     itemCls : "x-menu-item x-menu-check-item",
20376     /**
20377      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20378      */
20379     groupClass : "x-menu-group-item",
20380
20381     /**
20382      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20383      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20384      * initialized with checked = true will be rendered as checked.
20385      */
20386     checked: false,
20387
20388     // private
20389     ctype: "Roo.menu.CheckItem",
20390
20391     // private
20392     onRender : function(c){
20393         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20394         if(this.group){
20395             this.el.addClass(this.groupClass);
20396         }
20397         Roo.menu.MenuMgr.registerCheckable(this);
20398         if(this.checked){
20399             this.checked = false;
20400             this.setChecked(true, true);
20401         }
20402     },
20403
20404     // private
20405     destroy : function(){
20406         if(this.rendered){
20407             Roo.menu.MenuMgr.unregisterCheckable(this);
20408         }
20409         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20410     },
20411
20412     /**
20413      * Set the checked state of this item
20414      * @param {Boolean} checked The new checked value
20415      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20416      */
20417     setChecked : function(state, suppressEvent){
20418         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20419             if(this.container){
20420                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20421             }
20422             this.checked = state;
20423             if(suppressEvent !== true){
20424                 this.fireEvent("checkchange", this, state);
20425             }
20426         }
20427     },
20428
20429     // private
20430     handleClick : function(e){
20431        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20432            this.setChecked(!this.checked);
20433        }
20434        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20435     }
20436 });/*
20437  * Based on:
20438  * Ext JS Library 1.1.1
20439  * Copyright(c) 2006-2007, Ext JS, LLC.
20440  *
20441  * Originally Released Under LGPL - original licence link has changed is not relivant.
20442  *
20443  * Fork - LGPL
20444  * <script type="text/javascript">
20445  */
20446  
20447 /**
20448  * @class Roo.menu.DateItem
20449  * @extends Roo.menu.Adapter
20450  * A menu item that wraps the {@link Roo.DatPicker} component.
20451  * @constructor
20452  * Creates a new DateItem
20453  * @param {Object} config Configuration options
20454  */
20455 Roo.menu.DateItem = function(config){
20456     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20457     /** The Roo.DatePicker object @type Roo.DatePicker */
20458     this.picker = this.component;
20459     this.addEvents({select: true});
20460     
20461     this.picker.on("render", function(picker){
20462         picker.getEl().swallowEvent("click");
20463         picker.container.addClass("x-menu-date-item");
20464     });
20465
20466     this.picker.on("select", this.onSelect, this);
20467 };
20468
20469 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20470     // private
20471     onSelect : function(picker, date){
20472         this.fireEvent("select", this, date, picker);
20473         Roo.menu.DateItem.superclass.handleClick.call(this);
20474     }
20475 });/*
20476  * Based on:
20477  * Ext JS Library 1.1.1
20478  * Copyright(c) 2006-2007, Ext JS, LLC.
20479  *
20480  * Originally Released Under LGPL - original licence link has changed is not relivant.
20481  *
20482  * Fork - LGPL
20483  * <script type="text/javascript">
20484  */
20485  
20486 /**
20487  * @class Roo.menu.ColorItem
20488  * @extends Roo.menu.Adapter
20489  * A menu item that wraps the {@link Roo.ColorPalette} component.
20490  * @constructor
20491  * Creates a new ColorItem
20492  * @param {Object} config Configuration options
20493  */
20494 Roo.menu.ColorItem = function(config){
20495     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20496     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20497     this.palette = this.component;
20498     this.relayEvents(this.palette, ["select"]);
20499     if(this.selectHandler){
20500         this.on('select', this.selectHandler, this.scope);
20501     }
20502 };
20503 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20504  * Based on:
20505  * Ext JS Library 1.1.1
20506  * Copyright(c) 2006-2007, Ext JS, LLC.
20507  *
20508  * Originally Released Under LGPL - original licence link has changed is not relivant.
20509  *
20510  * Fork - LGPL
20511  * <script type="text/javascript">
20512  */
20513  
20514
20515 /**
20516  * @class Roo.menu.DateMenu
20517  * @extends Roo.menu.Menu
20518  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20519  * @constructor
20520  * Creates a new DateMenu
20521  * @param {Object} config Configuration options
20522  */
20523 Roo.menu.DateMenu = function(config){
20524     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20525     this.plain = true;
20526     var di = new Roo.menu.DateItem(config);
20527     this.add(di);
20528     /**
20529      * The {@link Roo.DatePicker} instance for this DateMenu
20530      * @type DatePicker
20531      */
20532     this.picker = di.picker;
20533     /**
20534      * @event select
20535      * @param {DatePicker} picker
20536      * @param {Date} date
20537      */
20538     this.relayEvents(di, ["select"]);
20539     this.on('beforeshow', function(){
20540         if(this.picker){
20541             this.picker.hideMonthPicker(false);
20542         }
20543     }, this);
20544 };
20545 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20546     cls:'x-date-menu'
20547 });/*
20548  * Based on:
20549  * Ext JS Library 1.1.1
20550  * Copyright(c) 2006-2007, Ext JS, LLC.
20551  *
20552  * Originally Released Under LGPL - original licence link has changed is not relivant.
20553  *
20554  * Fork - LGPL
20555  * <script type="text/javascript">
20556  */
20557  
20558
20559 /**
20560  * @class Roo.menu.ColorMenu
20561  * @extends Roo.menu.Menu
20562  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20563  * @constructor
20564  * Creates a new ColorMenu
20565  * @param {Object} config Configuration options
20566  */
20567 Roo.menu.ColorMenu = function(config){
20568     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20569     this.plain = true;
20570     var ci = new Roo.menu.ColorItem(config);
20571     this.add(ci);
20572     /**
20573      * The {@link Roo.ColorPalette} instance for this ColorMenu
20574      * @type ColorPalette
20575      */
20576     this.palette = ci.palette;
20577     /**
20578      * @event select
20579      * @param {ColorPalette} palette
20580      * @param {String} color
20581      */
20582     this.relayEvents(ci, ["select"]);
20583 };
20584 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20585  * Based on:
20586  * Ext JS Library 1.1.1
20587  * Copyright(c) 2006-2007, Ext JS, LLC.
20588  *
20589  * Originally Released Under LGPL - original licence link has changed is not relivant.
20590  *
20591  * Fork - LGPL
20592  * <script type="text/javascript">
20593  */
20594  
20595 /**
20596  * @class Roo.form.Field
20597  * @extends Roo.BoxComponent
20598  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20599  * @constructor
20600  * Creates a new Field
20601  * @param {Object} config Configuration options
20602  */
20603 Roo.form.Field = function(config){
20604     Roo.form.Field.superclass.constructor.call(this, config);
20605 };
20606
20607 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20608     /**
20609      * @cfg {String} fieldLabel Label to use when rendering a form.
20610      */
20611        /**
20612      * @cfg {String} qtip Mouse over tip
20613      */
20614      
20615     /**
20616      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20617      */
20618     invalidClass : "x-form-invalid",
20619     /**
20620      * @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")
20621      */
20622     invalidText : "The value in this field is invalid",
20623     /**
20624      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20625      */
20626     focusClass : "x-form-focus",
20627     /**
20628      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20629       automatic validation (defaults to "keyup").
20630      */
20631     validationEvent : "keyup",
20632     /**
20633      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20634      */
20635     validateOnBlur : true,
20636     /**
20637      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20638      */
20639     validationDelay : 250,
20640     /**
20641      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20642      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20643      */
20644     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
20645     /**
20646      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20647      */
20648     fieldClass : "x-form-field",
20649     /**
20650      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20651      *<pre>
20652 Value         Description
20653 -----------   ----------------------------------------------------------------------
20654 qtip          Display a quick tip when the user hovers over the field
20655 title         Display a default browser title attribute popup
20656 under         Add a block div beneath the field containing the error text
20657 side          Add an error icon to the right of the field with a popup on hover
20658 [element id]  Add the error text directly to the innerHTML of the specified element
20659 </pre>
20660      */
20661     msgTarget : 'qtip',
20662     /**
20663      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20664      */
20665     msgFx : 'normal',
20666
20667     /**
20668      * @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.
20669      */
20670     readOnly : false,
20671
20672     /**
20673      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20674      */
20675     disabled : false,
20676
20677     /**
20678      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20679      */
20680     inputType : undefined,
20681     
20682     /**
20683      * @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).
20684          */
20685         tabIndex : undefined,
20686         
20687     // private
20688     isFormField : true,
20689
20690     // private
20691     hasFocus : false,
20692     /**
20693      * @property {Roo.Element} fieldEl
20694      * Element Containing the rendered Field (with label etc.)
20695      */
20696     /**
20697      * @cfg {Mixed} value A value to initialize this field with.
20698      */
20699     value : undefined,
20700
20701     /**
20702      * @cfg {String} name The field's HTML name attribute.
20703      */
20704     /**
20705      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20706      */
20707
20708         // private ??
20709         initComponent : function(){
20710         Roo.form.Field.superclass.initComponent.call(this);
20711         this.addEvents({
20712             /**
20713              * @event focus
20714              * Fires when this field receives input focus.
20715              * @param {Roo.form.Field} this
20716              */
20717             focus : true,
20718             /**
20719              * @event blur
20720              * Fires when this field loses input focus.
20721              * @param {Roo.form.Field} this
20722              */
20723             blur : true,
20724             /**
20725              * @event specialkey
20726              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20727              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20728              * @param {Roo.form.Field} this
20729              * @param {Roo.EventObject} e The event object
20730              */
20731             specialkey : true,
20732             /**
20733              * @event change
20734              * Fires just before the field blurs if the field value has changed.
20735              * @param {Roo.form.Field} this
20736              * @param {Mixed} newValue The new value
20737              * @param {Mixed} oldValue The original value
20738              */
20739             change : true,
20740             /**
20741              * @event invalid
20742              * Fires after the field has been marked as invalid.
20743              * @param {Roo.form.Field} this
20744              * @param {String} msg The validation message
20745              */
20746             invalid : true,
20747             /**
20748              * @event valid
20749              * Fires after the field has been validated with no errors.
20750              * @param {Roo.form.Field} this
20751              */
20752             valid : true,
20753              /**
20754              * @event keyup
20755              * Fires after the key up
20756              * @param {Roo.form.Field} this
20757              * @param {Roo.EventObject}  e The event Object
20758              */
20759             keyup : true
20760         });
20761     },
20762
20763     /**
20764      * Returns the name attribute of the field if available
20765      * @return {String} name The field name
20766      */
20767     getName: function(){
20768          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20769     },
20770
20771     // private
20772     onRender : function(ct, position){
20773         Roo.form.Field.superclass.onRender.call(this, ct, position);
20774         if(!this.el){
20775             var cfg = this.getAutoCreate();
20776             if(!cfg.name){
20777                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
20778             }
20779             if (!cfg.name.length) {
20780                 delete cfg.name;
20781             }
20782             if(this.inputType){
20783                 cfg.type = this.inputType;
20784             }
20785             this.el = ct.createChild(cfg, position);
20786         }
20787         var type = this.el.dom.type;
20788         if(type){
20789             if(type == 'password'){
20790                 type = 'text';
20791             }
20792             this.el.addClass('x-form-'+type);
20793         }
20794         if(this.readOnly){
20795             this.el.dom.readOnly = true;
20796         }
20797         if(this.tabIndex !== undefined){
20798             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20799         }
20800
20801         this.el.addClass([this.fieldClass, this.cls]);
20802         this.initValue();
20803     },
20804
20805     /**
20806      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20807      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20808      * @return {Roo.form.Field} this
20809      */
20810     applyTo : function(target){
20811         this.allowDomMove = false;
20812         this.el = Roo.get(target);
20813         this.render(this.el.dom.parentNode);
20814         return this;
20815     },
20816
20817     // private
20818     initValue : function(){
20819         if(this.value !== undefined){
20820             this.setValue(this.value);
20821         }else if(this.el.dom.value.length > 0){
20822             this.setValue(this.el.dom.value);
20823         }
20824     },
20825
20826     /**
20827      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20828      */
20829     isDirty : function() {
20830         if(this.disabled) {
20831             return false;
20832         }
20833         return String(this.getValue()) !== String(this.originalValue);
20834     },
20835
20836     // private
20837     afterRender : function(){
20838         Roo.form.Field.superclass.afterRender.call(this);
20839         this.initEvents();
20840     },
20841
20842     // private
20843     fireKey : function(e){
20844         //Roo.log('field ' + e.getKey());
20845         if(e.isNavKeyPress()){
20846             this.fireEvent("specialkey", this, e);
20847         }
20848     },
20849
20850     /**
20851      * Resets the current field value to the originally loaded value and clears any validation messages
20852      */
20853     reset : function(){
20854         this.setValue(this.resetValue);
20855         this.clearInvalid();
20856     },
20857
20858     // private
20859     initEvents : function(){
20860         // safari killled keypress - so keydown is now used..
20861         this.el.on("keydown" , this.fireKey,  this);
20862         this.el.on("focus", this.onFocus,  this);
20863         this.el.on("blur", this.onBlur,  this);
20864         this.el.relayEvent('keyup', this);
20865
20866         // reference to original value for reset
20867         this.originalValue = this.getValue();
20868         this.resetValue =  this.getValue();
20869     },
20870
20871     // private
20872     onFocus : function(){
20873         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20874             this.el.addClass(this.focusClass);
20875         }
20876         if(!this.hasFocus){
20877             this.hasFocus = true;
20878             this.startValue = this.getValue();
20879             this.fireEvent("focus", this);
20880         }
20881     },
20882
20883     beforeBlur : Roo.emptyFn,
20884
20885     // private
20886     onBlur : function(){
20887         this.beforeBlur();
20888         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20889             this.el.removeClass(this.focusClass);
20890         }
20891         this.hasFocus = false;
20892         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20893             this.validate();
20894         }
20895         var v = this.getValue();
20896         if(String(v) !== String(this.startValue)){
20897             this.fireEvent('change', this, v, this.startValue);
20898         }
20899         this.fireEvent("blur", this);
20900     },
20901
20902     /**
20903      * Returns whether or not the field value is currently valid
20904      * @param {Boolean} preventMark True to disable marking the field invalid
20905      * @return {Boolean} True if the value is valid, else false
20906      */
20907     isValid : function(preventMark){
20908         if(this.disabled){
20909             return true;
20910         }
20911         var restore = this.preventMark;
20912         this.preventMark = preventMark === true;
20913         var v = this.validateValue(this.processValue(this.getRawValue()));
20914         this.preventMark = restore;
20915         return v;
20916     },
20917
20918     /**
20919      * Validates the field value
20920      * @return {Boolean} True if the value is valid, else false
20921      */
20922     validate : function(){
20923         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20924             this.clearInvalid();
20925             return true;
20926         }
20927         return false;
20928     },
20929
20930     processValue : function(value){
20931         return value;
20932     },
20933
20934     // private
20935     // Subclasses should provide the validation implementation by overriding this
20936     validateValue : function(value){
20937         return true;
20938     },
20939
20940     /**
20941      * Mark this field as invalid
20942      * @param {String} msg The validation message
20943      */
20944     markInvalid : function(msg){
20945         if(!this.rendered || this.preventMark){ // not rendered
20946             return;
20947         }
20948         
20949         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
20950         
20951         obj.el.addClass(this.invalidClass);
20952         msg = msg || this.invalidText;
20953         switch(this.msgTarget){
20954             case 'qtip':
20955                 obj.el.dom.qtip = msg;
20956                 obj.el.dom.qclass = 'x-form-invalid-tip';
20957                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20958                     Roo.QuickTips.enable();
20959                 }
20960                 break;
20961             case 'title':
20962                 this.el.dom.title = msg;
20963                 break;
20964             case 'under':
20965                 if(!this.errorEl){
20966                     var elp = this.el.findParent('.x-form-element', 5, true);
20967                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20968                     this.errorEl.setWidth(elp.getWidth(true)-20);
20969                 }
20970                 this.errorEl.update(msg);
20971                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20972                 break;
20973             case 'side':
20974                 if(!this.errorIcon){
20975                     var elp = this.el.findParent('.x-form-element', 5, true);
20976                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20977                 }
20978                 this.alignErrorIcon();
20979                 this.errorIcon.dom.qtip = msg;
20980                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20981                 this.errorIcon.show();
20982                 this.on('resize', this.alignErrorIcon, this);
20983                 break;
20984             default:
20985                 var t = Roo.getDom(this.msgTarget);
20986                 t.innerHTML = msg;
20987                 t.style.display = this.msgDisplay;
20988                 break;
20989         }
20990         this.fireEvent('invalid', this, msg);
20991     },
20992
20993     // private
20994     alignErrorIcon : function(){
20995         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
20996     },
20997
20998     /**
20999      * Clear any invalid styles/messages for this field
21000      */
21001     clearInvalid : function(){
21002         if(!this.rendered || this.preventMark){ // not rendered
21003             return;
21004         }
21005         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
21006         
21007         obj.el.removeClass(this.invalidClass);
21008         switch(this.msgTarget){
21009             case 'qtip':
21010                 obj.el.dom.qtip = '';
21011                 break;
21012             case 'title':
21013                 this.el.dom.title = '';
21014                 break;
21015             case 'under':
21016                 if(this.errorEl){
21017                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21018                 }
21019                 break;
21020             case 'side':
21021                 if(this.errorIcon){
21022                     this.errorIcon.dom.qtip = '';
21023                     this.errorIcon.hide();
21024                     this.un('resize', this.alignErrorIcon, this);
21025                 }
21026                 break;
21027             default:
21028                 var t = Roo.getDom(this.msgTarget);
21029                 t.innerHTML = '';
21030                 t.style.display = 'none';
21031                 break;
21032         }
21033         this.fireEvent('valid', this);
21034     },
21035
21036     /**
21037      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21038      * @return {Mixed} value The field value
21039      */
21040     getRawValue : function(){
21041         var v = this.el.getValue();
21042         
21043         return v;
21044     },
21045
21046     /**
21047      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21048      * @return {Mixed} value The field value
21049      */
21050     getValue : function(){
21051         var v = this.el.getValue();
21052          
21053         return v;
21054     },
21055
21056     /**
21057      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21058      * @param {Mixed} value The value to set
21059      */
21060     setRawValue : function(v){
21061         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21062     },
21063
21064     /**
21065      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21066      * @param {Mixed} value The value to set
21067      */
21068     setValue : function(v){
21069         this.value = v;
21070         if(this.rendered){
21071             this.el.dom.value = (v === null || v === undefined ? '' : v);
21072              this.validate();
21073         }
21074     },
21075
21076     adjustSize : function(w, h){
21077         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21078         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21079         return s;
21080     },
21081
21082     adjustWidth : function(tag, w){
21083         tag = tag.toLowerCase();
21084         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21085             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21086                 if(tag == 'input'){
21087                     return w + 2;
21088                 }
21089                 if(tag == 'textarea'){
21090                     return w-2;
21091                 }
21092             }else if(Roo.isOpera){
21093                 if(tag == 'input'){
21094                     return w + 2;
21095                 }
21096                 if(tag == 'textarea'){
21097                     return w-2;
21098                 }
21099             }
21100         }
21101         return w;
21102     }
21103 });
21104
21105
21106 // anything other than normal should be considered experimental
21107 Roo.form.Field.msgFx = {
21108     normal : {
21109         show: function(msgEl, f){
21110             msgEl.setDisplayed('block');
21111         },
21112
21113         hide : function(msgEl, f){
21114             msgEl.setDisplayed(false).update('');
21115         }
21116     },
21117
21118     slide : {
21119         show: function(msgEl, f){
21120             msgEl.slideIn('t', {stopFx:true});
21121         },
21122
21123         hide : function(msgEl, f){
21124             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21125         }
21126     },
21127
21128     slideRight : {
21129         show: function(msgEl, f){
21130             msgEl.fixDisplay();
21131             msgEl.alignTo(f.el, 'tl-tr');
21132             msgEl.slideIn('l', {stopFx:true});
21133         },
21134
21135         hide : function(msgEl, f){
21136             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21137         }
21138     }
21139 };/*
21140  * Based on:
21141  * Ext JS Library 1.1.1
21142  * Copyright(c) 2006-2007, Ext JS, LLC.
21143  *
21144  * Originally Released Under LGPL - original licence link has changed is not relivant.
21145  *
21146  * Fork - LGPL
21147  * <script type="text/javascript">
21148  */
21149  
21150
21151 /**
21152  * @class Roo.form.TextField
21153  * @extends Roo.form.Field
21154  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21155  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21156  * @constructor
21157  * Creates a new TextField
21158  * @param {Object} config Configuration options
21159  */
21160 Roo.form.TextField = function(config){
21161     Roo.form.TextField.superclass.constructor.call(this, config);
21162     this.addEvents({
21163         /**
21164          * @event autosize
21165          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21166          * according to the default logic, but this event provides a hook for the developer to apply additional
21167          * logic at runtime to resize the field if needed.
21168              * @param {Roo.form.Field} this This text field
21169              * @param {Number} width The new field width
21170              */
21171         autosize : true
21172     });
21173 };
21174
21175 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21176     /**
21177      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21178      */
21179     grow : false,
21180     /**
21181      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21182      */
21183     growMin : 30,
21184     /**
21185      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21186      */
21187     growMax : 800,
21188     /**
21189      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21190      */
21191     vtype : null,
21192     /**
21193      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21194      */
21195     maskRe : null,
21196     /**
21197      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21198      */
21199     disableKeyFilter : false,
21200     /**
21201      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21202      */
21203     allowBlank : true,
21204     /**
21205      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21206      */
21207     minLength : 0,
21208     /**
21209      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21210      */
21211     maxLength : Number.MAX_VALUE,
21212     /**
21213      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21214      */
21215     minLengthText : "The minimum length for this field is {0}",
21216     /**
21217      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21218      */
21219     maxLengthText : "The maximum length for this field is {0}",
21220     /**
21221      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21222      */
21223     selectOnFocus : false,
21224     /**
21225      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21226      */
21227     blankText : "This field is required",
21228     /**
21229      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21230      * If available, this function will be called only after the basic validators all return true, and will be passed the
21231      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21232      */
21233     validator : null,
21234     /**
21235      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21236      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21237      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21238      */
21239     regex : null,
21240     /**
21241      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21242      */
21243     regexText : "",
21244     /**
21245      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21246      */
21247     emptyText : null,
21248    
21249
21250     // private
21251     initEvents : function()
21252     {
21253         if (this.emptyText) {
21254             this.el.attr('placeholder', this.emptyText);
21255         }
21256         
21257         Roo.form.TextField.superclass.initEvents.call(this);
21258         if(this.validationEvent == 'keyup'){
21259             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21260             this.el.on('keyup', this.filterValidation, this);
21261         }
21262         else if(this.validationEvent !== false){
21263             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21264         }
21265         
21266         if(this.selectOnFocus){
21267             this.on("focus", this.preFocus, this);
21268             
21269         }
21270         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21271             this.el.on("keypress", this.filterKeys, this);
21272         }
21273         if(this.grow){
21274             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21275             this.el.on("click", this.autoSize,  this);
21276         }
21277         if(this.el.is('input[type=password]') && Roo.isSafari){
21278             this.el.on('keydown', this.SafariOnKeyDown, this);
21279         }
21280     },
21281
21282     processValue : function(value){
21283         if(this.stripCharsRe){
21284             var newValue = value.replace(this.stripCharsRe, '');
21285             if(newValue !== value){
21286                 this.setRawValue(newValue);
21287                 return newValue;
21288             }
21289         }
21290         return value;
21291     },
21292
21293     filterValidation : function(e){
21294         if(!e.isNavKeyPress()){
21295             this.validationTask.delay(this.validationDelay);
21296         }
21297     },
21298
21299     // private
21300     onKeyUp : function(e){
21301         if(!e.isNavKeyPress()){
21302             this.autoSize();
21303         }
21304     },
21305
21306     /**
21307      * Resets the current field value to the originally-loaded value and clears any validation messages.
21308      *  
21309      */
21310     reset : function(){
21311         Roo.form.TextField.superclass.reset.call(this);
21312        
21313     },
21314
21315     
21316     // private
21317     preFocus : function(){
21318         
21319         if(this.selectOnFocus){
21320             this.el.dom.select();
21321         }
21322     },
21323
21324     
21325     // private
21326     filterKeys : function(e){
21327         var k = e.getKey();
21328         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21329             return;
21330         }
21331         var c = e.getCharCode(), cc = String.fromCharCode(c);
21332         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21333             return;
21334         }
21335         if(!this.maskRe.test(cc)){
21336             e.stopEvent();
21337         }
21338     },
21339
21340     setValue : function(v){
21341         
21342         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21343         
21344         this.autoSize();
21345     },
21346
21347     /**
21348      * Validates a value according to the field's validation rules and marks the field as invalid
21349      * if the validation fails
21350      * @param {Mixed} value The value to validate
21351      * @return {Boolean} True if the value is valid, else false
21352      */
21353     validateValue : function(value){
21354         if(value.length < 1)  { // if it's blank
21355              if(this.allowBlank){
21356                 this.clearInvalid();
21357                 return true;
21358              }else{
21359                 this.markInvalid(this.blankText);
21360                 return false;
21361              }
21362         }
21363         if(value.length < this.minLength){
21364             this.markInvalid(String.format(this.minLengthText, this.minLength));
21365             return false;
21366         }
21367         if(value.length > this.maxLength){
21368             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21369             return false;
21370         }
21371         if(this.vtype){
21372             var vt = Roo.form.VTypes;
21373             if(!vt[this.vtype](value, this)){
21374                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21375                 return false;
21376             }
21377         }
21378         if(typeof this.validator == "function"){
21379             var msg = this.validator(value);
21380             if(msg !== true){
21381                 this.markInvalid(msg);
21382                 return false;
21383             }
21384         }
21385         if(this.regex && !this.regex.test(value)){
21386             this.markInvalid(this.regexText);
21387             return false;
21388         }
21389         return true;
21390     },
21391
21392     /**
21393      * Selects text in this field
21394      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21395      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21396      */
21397     selectText : function(start, end){
21398         var v = this.getRawValue();
21399         if(v.length > 0){
21400             start = start === undefined ? 0 : start;
21401             end = end === undefined ? v.length : end;
21402             var d = this.el.dom;
21403             if(d.setSelectionRange){
21404                 d.setSelectionRange(start, end);
21405             }else if(d.createTextRange){
21406                 var range = d.createTextRange();
21407                 range.moveStart("character", start);
21408                 range.moveEnd("character", v.length-end);
21409                 range.select();
21410             }
21411         }
21412     },
21413
21414     /**
21415      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21416      * This only takes effect if grow = true, and fires the autosize event.
21417      */
21418     autoSize : function(){
21419         if(!this.grow || !this.rendered){
21420             return;
21421         }
21422         if(!this.metrics){
21423             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21424         }
21425         var el = this.el;
21426         var v = el.dom.value;
21427         var d = document.createElement('div');
21428         d.appendChild(document.createTextNode(v));
21429         v = d.innerHTML;
21430         d = null;
21431         v += "&#160;";
21432         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21433         this.el.setWidth(w);
21434         this.fireEvent("autosize", this, w);
21435     },
21436     
21437     // private
21438     SafariOnKeyDown : function(event)
21439     {
21440         // this is a workaround for a password hang bug on chrome/ webkit.
21441         
21442         var isSelectAll = false;
21443         
21444         if(this.el.dom.selectionEnd > 0){
21445             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
21446         }
21447         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
21448             event.preventDefault();
21449             this.setValue('');
21450             return;
21451         }
21452         
21453         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
21454             
21455             event.preventDefault();
21456             // this is very hacky as keydown always get's upper case.
21457             
21458             var cc = String.fromCharCode(event.getCharCode());
21459             
21460             
21461             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
21462             
21463         }
21464         
21465         
21466     }
21467 });/*
21468  * Based on:
21469  * Ext JS Library 1.1.1
21470  * Copyright(c) 2006-2007, Ext JS, LLC.
21471  *
21472  * Originally Released Under LGPL - original licence link has changed is not relivant.
21473  *
21474  * Fork - LGPL
21475  * <script type="text/javascript">
21476  */
21477  
21478 /**
21479  * @class Roo.form.Hidden
21480  * @extends Roo.form.TextField
21481  * Simple Hidden element used on forms 
21482  * 
21483  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21484  * 
21485  * @constructor
21486  * Creates a new Hidden form element.
21487  * @param {Object} config Configuration options
21488  */
21489
21490
21491
21492 // easy hidden field...
21493 Roo.form.Hidden = function(config){
21494     Roo.form.Hidden.superclass.constructor.call(this, config);
21495 };
21496   
21497 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21498     fieldLabel:      '',
21499     inputType:      'hidden',
21500     width:          50,
21501     allowBlank:     true,
21502     labelSeparator: '',
21503     hidden:         true,
21504     itemCls :       'x-form-item-display-none'
21505
21506
21507 });
21508
21509
21510 /*
21511  * Based on:
21512  * Ext JS Library 1.1.1
21513  * Copyright(c) 2006-2007, Ext JS, LLC.
21514  *
21515  * Originally Released Under LGPL - original licence link has changed is not relivant.
21516  *
21517  * Fork - LGPL
21518  * <script type="text/javascript">
21519  */
21520  
21521 /**
21522  * @class Roo.form.TriggerField
21523  * @extends Roo.form.TextField
21524  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21525  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21526  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21527  * for which you can provide a custom implementation.  For example:
21528  * <pre><code>
21529 var trigger = new Roo.form.TriggerField();
21530 trigger.onTriggerClick = myTriggerFn;
21531 trigger.applyTo('my-field');
21532 </code></pre>
21533  *
21534  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21535  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21536  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21537  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21538  * @constructor
21539  * Create a new TriggerField.
21540  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21541  * to the base TextField)
21542  */
21543 Roo.form.TriggerField = function(config){
21544     this.mimicing = false;
21545     Roo.form.TriggerField.superclass.constructor.call(this, config);
21546 };
21547
21548 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21549     /**
21550      * @cfg {String} triggerClass A CSS class to apply to the trigger
21551      */
21552     /**
21553      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21554      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21555      */
21556     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
21557     /**
21558      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21559      */
21560     hideTrigger:false,
21561
21562     /** @cfg {Boolean} grow @hide */
21563     /** @cfg {Number} growMin @hide */
21564     /** @cfg {Number} growMax @hide */
21565
21566     /**
21567      * @hide 
21568      * @method
21569      */
21570     autoSize: Roo.emptyFn,
21571     // private
21572     monitorTab : true,
21573     // private
21574     deferHeight : true,
21575
21576     
21577     actionMode : 'wrap',
21578     // private
21579     onResize : function(w, h){
21580         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21581         if(typeof w == 'number'){
21582             var x = w - this.trigger.getWidth();
21583             this.el.setWidth(this.adjustWidth('input', x));
21584             this.trigger.setStyle('left', x+'px');
21585         }
21586     },
21587
21588     // private
21589     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21590
21591     // private
21592     getResizeEl : function(){
21593         return this.wrap;
21594     },
21595
21596     // private
21597     getPositionEl : function(){
21598         return this.wrap;
21599     },
21600
21601     // private
21602     alignErrorIcon : function(){
21603         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21604     },
21605
21606     // private
21607     onRender : function(ct, position){
21608         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21609         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21610         this.trigger = this.wrap.createChild(this.triggerConfig ||
21611                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21612         if(this.hideTrigger){
21613             this.trigger.setDisplayed(false);
21614         }
21615         this.initTrigger();
21616         if(!this.width){
21617             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21618         }
21619     },
21620
21621     // private
21622     initTrigger : function(){
21623         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21624         this.trigger.addClassOnOver('x-form-trigger-over');
21625         this.trigger.addClassOnClick('x-form-trigger-click');
21626     },
21627
21628     // private
21629     onDestroy : function(){
21630         if(this.trigger){
21631             this.trigger.removeAllListeners();
21632             this.trigger.remove();
21633         }
21634         if(this.wrap){
21635             this.wrap.remove();
21636         }
21637         Roo.form.TriggerField.superclass.onDestroy.call(this);
21638     },
21639
21640     // private
21641     onFocus : function(){
21642         Roo.form.TriggerField.superclass.onFocus.call(this);
21643         if(!this.mimicing){
21644             this.wrap.addClass('x-trigger-wrap-focus');
21645             this.mimicing = true;
21646             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21647             if(this.monitorTab){
21648                 this.el.on("keydown", this.checkTab, this);
21649             }
21650         }
21651     },
21652
21653     // private
21654     checkTab : function(e){
21655         if(e.getKey() == e.TAB){
21656             this.triggerBlur();
21657         }
21658     },
21659
21660     // private
21661     onBlur : function(){
21662         // do nothing
21663     },
21664
21665     // private
21666     mimicBlur : function(e, t){
21667         if(!this.wrap.contains(t) && this.validateBlur()){
21668             this.triggerBlur();
21669         }
21670     },
21671
21672     // private
21673     triggerBlur : function(){
21674         this.mimicing = false;
21675         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21676         if(this.monitorTab){
21677             this.el.un("keydown", this.checkTab, this);
21678         }
21679         this.wrap.removeClass('x-trigger-wrap-focus');
21680         Roo.form.TriggerField.superclass.onBlur.call(this);
21681     },
21682
21683     // private
21684     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21685     validateBlur : function(e, t){
21686         return true;
21687     },
21688
21689     // private
21690     onDisable : function(){
21691         Roo.form.TriggerField.superclass.onDisable.call(this);
21692         if(this.wrap){
21693             this.wrap.addClass('x-item-disabled');
21694         }
21695     },
21696
21697     // private
21698     onEnable : function(){
21699         Roo.form.TriggerField.superclass.onEnable.call(this);
21700         if(this.wrap){
21701             this.wrap.removeClass('x-item-disabled');
21702         }
21703     },
21704
21705     // private
21706     onShow : function(){
21707         var ae = this.getActionEl();
21708         
21709         if(ae){
21710             ae.dom.style.display = '';
21711             ae.dom.style.visibility = 'visible';
21712         }
21713     },
21714
21715     // private
21716     
21717     onHide : function(){
21718         var ae = this.getActionEl();
21719         ae.dom.style.display = 'none';
21720     },
21721
21722     /**
21723      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21724      * by an implementing function.
21725      * @method
21726      * @param {EventObject} e
21727      */
21728     onTriggerClick : Roo.emptyFn
21729 });
21730
21731 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21732 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21733 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21734 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21735     initComponent : function(){
21736         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21737
21738         this.triggerConfig = {
21739             tag:'span', cls:'x-form-twin-triggers', cn:[
21740             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21741             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21742         ]};
21743     },
21744
21745     getTrigger : function(index){
21746         return this.triggers[index];
21747     },
21748
21749     initTrigger : function(){
21750         var ts = this.trigger.select('.x-form-trigger', true);
21751         this.wrap.setStyle('overflow', 'hidden');
21752         var triggerField = this;
21753         ts.each(function(t, all, index){
21754             t.hide = function(){
21755                 var w = triggerField.wrap.getWidth();
21756                 this.dom.style.display = 'none';
21757                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21758             };
21759             t.show = function(){
21760                 var w = triggerField.wrap.getWidth();
21761                 this.dom.style.display = '';
21762                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21763             };
21764             var triggerIndex = 'Trigger'+(index+1);
21765
21766             if(this['hide'+triggerIndex]){
21767                 t.dom.style.display = 'none';
21768             }
21769             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21770             t.addClassOnOver('x-form-trigger-over');
21771             t.addClassOnClick('x-form-trigger-click');
21772         }, this);
21773         this.triggers = ts.elements;
21774     },
21775
21776     onTrigger1Click : Roo.emptyFn,
21777     onTrigger2Click : Roo.emptyFn
21778 });/*
21779  * Based on:
21780  * Ext JS Library 1.1.1
21781  * Copyright(c) 2006-2007, Ext JS, LLC.
21782  *
21783  * Originally Released Under LGPL - original licence link has changed is not relivant.
21784  *
21785  * Fork - LGPL
21786  * <script type="text/javascript">
21787  */
21788  
21789 /**
21790  * @class Roo.form.TextArea
21791  * @extends Roo.form.TextField
21792  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21793  * support for auto-sizing.
21794  * @constructor
21795  * Creates a new TextArea
21796  * @param {Object} config Configuration options
21797  */
21798 Roo.form.TextArea = function(config){
21799     Roo.form.TextArea.superclass.constructor.call(this, config);
21800     // these are provided exchanges for backwards compat
21801     // minHeight/maxHeight were replaced by growMin/growMax to be
21802     // compatible with TextField growing config values
21803     if(this.minHeight !== undefined){
21804         this.growMin = this.minHeight;
21805     }
21806     if(this.maxHeight !== undefined){
21807         this.growMax = this.maxHeight;
21808     }
21809 };
21810
21811 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21812     /**
21813      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21814      */
21815     growMin : 60,
21816     /**
21817      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21818      */
21819     growMax: 1000,
21820     /**
21821      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21822      * in the field (equivalent to setting overflow: hidden, defaults to false)
21823      */
21824     preventScrollbars: false,
21825     /**
21826      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21827      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21828      */
21829
21830     // private
21831     onRender : function(ct, position){
21832         if(!this.el){
21833             this.defaultAutoCreate = {
21834                 tag: "textarea",
21835                 style:"width:300px;height:60px;",
21836                 autocomplete: "new-password"
21837             };
21838         }
21839         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21840         if(this.grow){
21841             this.textSizeEl = Roo.DomHelper.append(document.body, {
21842                 tag: "pre", cls: "x-form-grow-sizer"
21843             });
21844             if(this.preventScrollbars){
21845                 this.el.setStyle("overflow", "hidden");
21846             }
21847             this.el.setHeight(this.growMin);
21848         }
21849     },
21850
21851     onDestroy : function(){
21852         if(this.textSizeEl){
21853             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21854         }
21855         Roo.form.TextArea.superclass.onDestroy.call(this);
21856     },
21857
21858     // private
21859     onKeyUp : function(e){
21860         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21861             this.autoSize();
21862         }
21863     },
21864
21865     /**
21866      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21867      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21868      */
21869     autoSize : function(){
21870         if(!this.grow || !this.textSizeEl){
21871             return;
21872         }
21873         var el = this.el;
21874         var v = el.dom.value;
21875         var ts = this.textSizeEl;
21876
21877         ts.innerHTML = '';
21878         ts.appendChild(document.createTextNode(v));
21879         v = ts.innerHTML;
21880
21881         Roo.fly(ts).setWidth(this.el.getWidth());
21882         if(v.length < 1){
21883             v = "&#160;&#160;";
21884         }else{
21885             if(Roo.isIE){
21886                 v = v.replace(/\n/g, '<p>&#160;</p>');
21887             }
21888             v += "&#160;\n&#160;";
21889         }
21890         ts.innerHTML = v;
21891         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21892         if(h != this.lastHeight){
21893             this.lastHeight = h;
21894             this.el.setHeight(h);
21895             this.fireEvent("autosize", this, h);
21896         }
21897     }
21898 });/*
21899  * Based on:
21900  * Ext JS Library 1.1.1
21901  * Copyright(c) 2006-2007, Ext JS, LLC.
21902  *
21903  * Originally Released Under LGPL - original licence link has changed is not relivant.
21904  *
21905  * Fork - LGPL
21906  * <script type="text/javascript">
21907  */
21908  
21909
21910 /**
21911  * @class Roo.form.NumberField
21912  * @extends Roo.form.TextField
21913  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21914  * @constructor
21915  * Creates a new NumberField
21916  * @param {Object} config Configuration options
21917  */
21918 Roo.form.NumberField = function(config){
21919     Roo.form.NumberField.superclass.constructor.call(this, config);
21920 };
21921
21922 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21923     /**
21924      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21925      */
21926     fieldClass: "x-form-field x-form-num-field",
21927     /**
21928      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21929      */
21930     allowDecimals : true,
21931     /**
21932      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21933      */
21934     decimalSeparator : ".",
21935     /**
21936      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21937      */
21938     decimalPrecision : 2,
21939     /**
21940      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21941      */
21942     allowNegative : true,
21943     /**
21944      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21945      */
21946     minValue : Number.NEGATIVE_INFINITY,
21947     /**
21948      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21949      */
21950     maxValue : Number.MAX_VALUE,
21951     /**
21952      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21953      */
21954     minText : "The minimum value for this field is {0}",
21955     /**
21956      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21957      */
21958     maxText : "The maximum value for this field is {0}",
21959     /**
21960      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21961      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21962      */
21963     nanText : "{0} is not a valid number",
21964
21965     // private
21966     initEvents : function(){
21967         Roo.form.NumberField.superclass.initEvents.call(this);
21968         var allowed = "0123456789";
21969         if(this.allowDecimals){
21970             allowed += this.decimalSeparator;
21971         }
21972         if(this.allowNegative){
21973             allowed += "-";
21974         }
21975         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21976         var keyPress = function(e){
21977             var k = e.getKey();
21978             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21979                 return;
21980             }
21981             var c = e.getCharCode();
21982             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21983                 e.stopEvent();
21984             }
21985         };
21986         this.el.on("keypress", keyPress, this);
21987     },
21988
21989     // private
21990     validateValue : function(value){
21991         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21992             return false;
21993         }
21994         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21995              return true;
21996         }
21997         var num = this.parseValue(value);
21998         if(isNaN(num)){
21999             this.markInvalid(String.format(this.nanText, value));
22000             return false;
22001         }
22002         if(num < this.minValue){
22003             this.markInvalid(String.format(this.minText, this.minValue));
22004             return false;
22005         }
22006         if(num > this.maxValue){
22007             this.markInvalid(String.format(this.maxText, this.maxValue));
22008             return false;
22009         }
22010         return true;
22011     },
22012
22013     getValue : function(){
22014         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22015     },
22016
22017     // private
22018     parseValue : function(value){
22019         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22020         return isNaN(value) ? '' : value;
22021     },
22022
22023     // private
22024     fixPrecision : function(value){
22025         var nan = isNaN(value);
22026         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22027             return nan ? '' : value;
22028         }
22029         return parseFloat(value).toFixed(this.decimalPrecision);
22030     },
22031
22032     setValue : function(v){
22033         v = this.fixPrecision(v);
22034         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22035     },
22036
22037     // private
22038     decimalPrecisionFcn : function(v){
22039         return Math.floor(v);
22040     },
22041
22042     beforeBlur : function(){
22043         var v = this.parseValue(this.getRawValue());
22044         if(v){
22045             this.setValue(v);
22046         }
22047     }
22048 });/*
22049  * Based on:
22050  * Ext JS Library 1.1.1
22051  * Copyright(c) 2006-2007, Ext JS, LLC.
22052  *
22053  * Originally Released Under LGPL - original licence link has changed is not relivant.
22054  *
22055  * Fork - LGPL
22056  * <script type="text/javascript">
22057  */
22058  
22059 /**
22060  * @class Roo.form.DateField
22061  * @extends Roo.form.TriggerField
22062  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22063 * @constructor
22064 * Create a new DateField
22065 * @param {Object} config
22066  */
22067 Roo.form.DateField = function(config){
22068     Roo.form.DateField.superclass.constructor.call(this, config);
22069     
22070       this.addEvents({
22071          
22072         /**
22073          * @event select
22074          * Fires when a date is selected
22075              * @param {Roo.form.DateField} combo This combo box
22076              * @param {Date} date The date selected
22077              */
22078         'select' : true
22079          
22080     });
22081     
22082     
22083     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22084     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22085     this.ddMatch = null;
22086     if(this.disabledDates){
22087         var dd = this.disabledDates;
22088         var re = "(?:";
22089         for(var i = 0; i < dd.length; i++){
22090             re += dd[i];
22091             if(i != dd.length-1) re += "|";
22092         }
22093         this.ddMatch = new RegExp(re + ")");
22094     }
22095 };
22096
22097 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22098     /**
22099      * @cfg {String} format
22100      * The default date format string which can be overriden for localization support.  The format must be
22101      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22102      */
22103     format : "m/d/y",
22104     /**
22105      * @cfg {String} altFormats
22106      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22107      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22108      */
22109     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22110     /**
22111      * @cfg {Array} disabledDays
22112      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22113      */
22114     disabledDays : null,
22115     /**
22116      * @cfg {String} disabledDaysText
22117      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22118      */
22119     disabledDaysText : "Disabled",
22120     /**
22121      * @cfg {Array} disabledDates
22122      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22123      * expression so they are very powerful. Some examples:
22124      * <ul>
22125      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22126      * <li>["03/08", "09/16"] would disable those days for every year</li>
22127      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22128      * <li>["03/../2006"] would disable every day in March 2006</li>
22129      * <li>["^03"] would disable every day in every March</li>
22130      * </ul>
22131      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22132      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22133      */
22134     disabledDates : null,
22135     /**
22136      * @cfg {String} disabledDatesText
22137      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22138      */
22139     disabledDatesText : "Disabled",
22140     /**
22141      * @cfg {Date/String} minValue
22142      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22143      * valid format (defaults to null).
22144      */
22145     minValue : null,
22146     /**
22147      * @cfg {Date/String} maxValue
22148      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22149      * valid format (defaults to null).
22150      */
22151     maxValue : null,
22152     /**
22153      * @cfg {String} minText
22154      * The error text to display when the date in the cell is before minValue (defaults to
22155      * 'The date in this field must be after {minValue}').
22156      */
22157     minText : "The date in this field must be equal to or after {0}",
22158     /**
22159      * @cfg {String} maxText
22160      * The error text to display when the date in the cell is after maxValue (defaults to
22161      * 'The date in this field must be before {maxValue}').
22162      */
22163     maxText : "The date in this field must be equal to or before {0}",
22164     /**
22165      * @cfg {String} invalidText
22166      * The error text to display when the date in the field is invalid (defaults to
22167      * '{value} is not a valid date - it must be in the format {format}').
22168      */
22169     invalidText : "{0} is not a valid date - it must be in the format {1}",
22170     /**
22171      * @cfg {String} triggerClass
22172      * An additional CSS class used to style the trigger button.  The trigger will always get the
22173      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22174      * which displays a calendar icon).
22175      */
22176     triggerClass : 'x-form-date-trigger',
22177     
22178
22179     /**
22180      * @cfg {Boolean} useIso
22181      * if enabled, then the date field will use a hidden field to store the 
22182      * real value as iso formated date. default (false)
22183      */ 
22184     useIso : false,
22185     /**
22186      * @cfg {String/Object} autoCreate
22187      * A DomHelper element spec, or true for a default element spec (defaults to
22188      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22189      */ 
22190     // private
22191     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22192     
22193     // private
22194     hiddenField: false,
22195     
22196     onRender : function(ct, position)
22197     {
22198         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22199         if (this.useIso) {
22200             //this.el.dom.removeAttribute('name'); 
22201             Roo.log("Changing name?");
22202             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22203             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22204                     'before', true);
22205             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22206             // prevent input submission
22207             this.hiddenName = this.name;
22208         }
22209             
22210             
22211     },
22212     
22213     // private
22214     validateValue : function(value)
22215     {
22216         value = this.formatDate(value);
22217         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22218             Roo.log('super failed');
22219             return false;
22220         }
22221         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22222              return true;
22223         }
22224         var svalue = value;
22225         value = this.parseDate(value);
22226         if(!value){
22227             Roo.log('parse date failed' + svalue);
22228             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22229             return false;
22230         }
22231         var time = value.getTime();
22232         if(this.minValue && time < this.minValue.getTime()){
22233             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22234             return false;
22235         }
22236         if(this.maxValue && time > this.maxValue.getTime()){
22237             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22238             return false;
22239         }
22240         if(this.disabledDays){
22241             var day = value.getDay();
22242             for(var i = 0; i < this.disabledDays.length; i++) {
22243                 if(day === this.disabledDays[i]){
22244                     this.markInvalid(this.disabledDaysText);
22245                     return false;
22246                 }
22247             }
22248         }
22249         var fvalue = this.formatDate(value);
22250         if(this.ddMatch && this.ddMatch.test(fvalue)){
22251             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22252             return false;
22253         }
22254         return true;
22255     },
22256
22257     // private
22258     // Provides logic to override the default TriggerField.validateBlur which just returns true
22259     validateBlur : function(){
22260         return !this.menu || !this.menu.isVisible();
22261     },
22262     
22263     getName: function()
22264     {
22265         // returns hidden if it's set..
22266         if (!this.rendered) {return ''};
22267         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22268         
22269     },
22270
22271     /**
22272      * Returns the current date value of the date field.
22273      * @return {Date} The date value
22274      */
22275     getValue : function(){
22276         
22277         return  this.hiddenField ?
22278                 this.hiddenField.value :
22279                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22280     },
22281
22282     /**
22283      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22284      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22285      * (the default format used is "m/d/y").
22286      * <br />Usage:
22287      * <pre><code>
22288 //All of these calls set the same date value (May 4, 2006)
22289
22290 //Pass a date object:
22291 var dt = new Date('5/4/06');
22292 dateField.setValue(dt);
22293
22294 //Pass a date string (default format):
22295 dateField.setValue('5/4/06');
22296
22297 //Pass a date string (custom format):
22298 dateField.format = 'Y-m-d';
22299 dateField.setValue('2006-5-4');
22300 </code></pre>
22301      * @param {String/Date} date The date or valid date string
22302      */
22303     setValue : function(date){
22304         if (this.hiddenField) {
22305             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22306         }
22307         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22308         // make sure the value field is always stored as a date..
22309         this.value = this.parseDate(date);
22310         
22311         
22312     },
22313
22314     // private
22315     parseDate : function(value){
22316         if(!value || value instanceof Date){
22317             return value;
22318         }
22319         var v = Date.parseDate(value, this.format);
22320          if (!v && this.useIso) {
22321             v = Date.parseDate(value, 'Y-m-d');
22322         }
22323         if(!v && this.altFormats){
22324             if(!this.altFormatsArray){
22325                 this.altFormatsArray = this.altFormats.split("|");
22326             }
22327             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22328                 v = Date.parseDate(value, this.altFormatsArray[i]);
22329             }
22330         }
22331         return v;
22332     },
22333
22334     // private
22335     formatDate : function(date, fmt){
22336         return (!date || !(date instanceof Date)) ?
22337                date : date.dateFormat(fmt || this.format);
22338     },
22339
22340     // private
22341     menuListeners : {
22342         select: function(m, d){
22343             
22344             this.setValue(d);
22345             this.fireEvent('select', this, d);
22346         },
22347         show : function(){ // retain focus styling
22348             this.onFocus();
22349         },
22350         hide : function(){
22351             this.focus.defer(10, this);
22352             var ml = this.menuListeners;
22353             this.menu.un("select", ml.select,  this);
22354             this.menu.un("show", ml.show,  this);
22355             this.menu.un("hide", ml.hide,  this);
22356         }
22357     },
22358
22359     // private
22360     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22361     onTriggerClick : function(){
22362         if(this.disabled){
22363             return;
22364         }
22365         if(this.menu == null){
22366             this.menu = new Roo.menu.DateMenu();
22367         }
22368         Roo.apply(this.menu.picker,  {
22369             showClear: this.allowBlank,
22370             minDate : this.minValue,
22371             maxDate : this.maxValue,
22372             disabledDatesRE : this.ddMatch,
22373             disabledDatesText : this.disabledDatesText,
22374             disabledDays : this.disabledDays,
22375             disabledDaysText : this.disabledDaysText,
22376             format : this.useIso ? 'Y-m-d' : this.format,
22377             minText : String.format(this.minText, this.formatDate(this.minValue)),
22378             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22379         });
22380         this.menu.on(Roo.apply({}, this.menuListeners, {
22381             scope:this
22382         }));
22383         this.menu.picker.setValue(this.getValue() || new Date());
22384         this.menu.show(this.el, "tl-bl?");
22385     },
22386
22387     beforeBlur : function(){
22388         var v = this.parseDate(this.getRawValue());
22389         if(v){
22390             this.setValue(v);
22391         }
22392     },
22393
22394     /*@
22395      * overide
22396      * 
22397      */
22398     isDirty : function() {
22399         if(this.disabled) {
22400             return false;
22401         }
22402         
22403         if(typeof(this.startValue) === 'undefined'){
22404             return false;
22405         }
22406         
22407         return String(this.getValue()) !== String(this.startValue);
22408         
22409     }
22410 });/*
22411  * Based on:
22412  * Ext JS Library 1.1.1
22413  * Copyright(c) 2006-2007, Ext JS, LLC.
22414  *
22415  * Originally Released Under LGPL - original licence link has changed is not relivant.
22416  *
22417  * Fork - LGPL
22418  * <script type="text/javascript">
22419  */
22420  
22421 /**
22422  * @class Roo.form.MonthField
22423  * @extends Roo.form.TriggerField
22424  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22425 * @constructor
22426 * Create a new MonthField
22427 * @param {Object} config
22428  */
22429 Roo.form.MonthField = function(config){
22430     
22431     Roo.form.MonthField.superclass.constructor.call(this, config);
22432     
22433       this.addEvents({
22434          
22435         /**
22436          * @event select
22437          * Fires when a date is selected
22438              * @param {Roo.form.MonthFieeld} combo This combo box
22439              * @param {Date} date The date selected
22440              */
22441         'select' : true
22442          
22443     });
22444     
22445     
22446     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22447     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22448     this.ddMatch = null;
22449     if(this.disabledDates){
22450         var dd = this.disabledDates;
22451         var re = "(?:";
22452         for(var i = 0; i < dd.length; i++){
22453             re += dd[i];
22454             if(i != dd.length-1) re += "|";
22455         }
22456         this.ddMatch = new RegExp(re + ")");
22457     }
22458 };
22459
22460 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22461     /**
22462      * @cfg {String} format
22463      * The default date format string which can be overriden for localization support.  The format must be
22464      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22465      */
22466     format : "M Y",
22467     /**
22468      * @cfg {String} altFormats
22469      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22470      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22471      */
22472     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22473     /**
22474      * @cfg {Array} disabledDays
22475      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22476      */
22477     disabledDays : [0,1,2,3,4,5,6],
22478     /**
22479      * @cfg {String} disabledDaysText
22480      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22481      */
22482     disabledDaysText : "Disabled",
22483     /**
22484      * @cfg {Array} disabledDates
22485      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22486      * expression so they are very powerful. Some examples:
22487      * <ul>
22488      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22489      * <li>["03/08", "09/16"] would disable those days for every year</li>
22490      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22491      * <li>["03/../2006"] would disable every day in March 2006</li>
22492      * <li>["^03"] would disable every day in every March</li>
22493      * </ul>
22494      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22495      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22496      */
22497     disabledDates : null,
22498     /**
22499      * @cfg {String} disabledDatesText
22500      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22501      */
22502     disabledDatesText : "Disabled",
22503     /**
22504      * @cfg {Date/String} minValue
22505      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22506      * valid format (defaults to null).
22507      */
22508     minValue : null,
22509     /**
22510      * @cfg {Date/String} maxValue
22511      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22512      * valid format (defaults to null).
22513      */
22514     maxValue : null,
22515     /**
22516      * @cfg {String} minText
22517      * The error text to display when the date in the cell is before minValue (defaults to
22518      * 'The date in this field must be after {minValue}').
22519      */
22520     minText : "The date in this field must be equal to or after {0}",
22521     /**
22522      * @cfg {String} maxTextf
22523      * The error text to display when the date in the cell is after maxValue (defaults to
22524      * 'The date in this field must be before {maxValue}').
22525      */
22526     maxText : "The date in this field must be equal to or before {0}",
22527     /**
22528      * @cfg {String} invalidText
22529      * The error text to display when the date in the field is invalid (defaults to
22530      * '{value} is not a valid date - it must be in the format {format}').
22531      */
22532     invalidText : "{0} is not a valid date - it must be in the format {1}",
22533     /**
22534      * @cfg {String} triggerClass
22535      * An additional CSS class used to style the trigger button.  The trigger will always get the
22536      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22537      * which displays a calendar icon).
22538      */
22539     triggerClass : 'x-form-date-trigger',
22540     
22541
22542     /**
22543      * @cfg {Boolean} useIso
22544      * if enabled, then the date field will use a hidden field to store the 
22545      * real value as iso formated date. default (true)
22546      */ 
22547     useIso : true,
22548     /**
22549      * @cfg {String/Object} autoCreate
22550      * A DomHelper element spec, or true for a default element spec (defaults to
22551      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22552      */ 
22553     // private
22554     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
22555     
22556     // private
22557     hiddenField: false,
22558     
22559     hideMonthPicker : false,
22560     
22561     onRender : function(ct, position)
22562     {
22563         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
22564         if (this.useIso) {
22565             this.el.dom.removeAttribute('name'); 
22566             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22567                     'before', true);
22568             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22569             // prevent input submission
22570             this.hiddenName = this.name;
22571         }
22572             
22573             
22574     },
22575     
22576     // private
22577     validateValue : function(value)
22578     {
22579         value = this.formatDate(value);
22580         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
22581             return false;
22582         }
22583         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22584              return true;
22585         }
22586         var svalue = value;
22587         value = this.parseDate(value);
22588         if(!value){
22589             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22590             return false;
22591         }
22592         var time = value.getTime();
22593         if(this.minValue && time < this.minValue.getTime()){
22594             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22595             return false;
22596         }
22597         if(this.maxValue && time > this.maxValue.getTime()){
22598             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22599             return false;
22600         }
22601         /*if(this.disabledDays){
22602             var day = value.getDay();
22603             for(var i = 0; i < this.disabledDays.length; i++) {
22604                 if(day === this.disabledDays[i]){
22605                     this.markInvalid(this.disabledDaysText);
22606                     return false;
22607                 }
22608             }
22609         }
22610         */
22611         var fvalue = this.formatDate(value);
22612         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
22613             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22614             return false;
22615         }
22616         */
22617         return true;
22618     },
22619
22620     // private
22621     // Provides logic to override the default TriggerField.validateBlur which just returns true
22622     validateBlur : function(){
22623         return !this.menu || !this.menu.isVisible();
22624     },
22625
22626     /**
22627      * Returns the current date value of the date field.
22628      * @return {Date} The date value
22629      */
22630     getValue : function(){
22631         
22632         
22633         
22634         return  this.hiddenField ?
22635                 this.hiddenField.value :
22636                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
22637     },
22638
22639     /**
22640      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22641      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
22642      * (the default format used is "m/d/y").
22643      * <br />Usage:
22644      * <pre><code>
22645 //All of these calls set the same date value (May 4, 2006)
22646
22647 //Pass a date object:
22648 var dt = new Date('5/4/06');
22649 monthField.setValue(dt);
22650
22651 //Pass a date string (default format):
22652 monthField.setValue('5/4/06');
22653
22654 //Pass a date string (custom format):
22655 monthField.format = 'Y-m-d';
22656 monthField.setValue('2006-5-4');
22657 </code></pre>
22658      * @param {String/Date} date The date or valid date string
22659      */
22660     setValue : function(date){
22661         Roo.log('month setValue' + date);
22662         // can only be first of month..
22663         
22664         var val = this.parseDate(date);
22665         
22666         if (this.hiddenField) {
22667             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22668         }
22669         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22670         this.value = this.parseDate(date);
22671     },
22672
22673     // private
22674     parseDate : function(value){
22675         if(!value || value instanceof Date){
22676             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
22677             return value;
22678         }
22679         var v = Date.parseDate(value, this.format);
22680         if (!v && this.useIso) {
22681             v = Date.parseDate(value, 'Y-m-d');
22682         }
22683         if (v) {
22684             // 
22685             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
22686         }
22687         
22688         
22689         if(!v && this.altFormats){
22690             if(!this.altFormatsArray){
22691                 this.altFormatsArray = this.altFormats.split("|");
22692             }
22693             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22694                 v = Date.parseDate(value, this.altFormatsArray[i]);
22695             }
22696         }
22697         return v;
22698     },
22699
22700     // private
22701     formatDate : function(date, fmt){
22702         return (!date || !(date instanceof Date)) ?
22703                date : date.dateFormat(fmt || this.format);
22704     },
22705
22706     // private
22707     menuListeners : {
22708         select: function(m, d){
22709             this.setValue(d);
22710             this.fireEvent('select', this, d);
22711         },
22712         show : function(){ // retain focus styling
22713             this.onFocus();
22714         },
22715         hide : function(){
22716             this.focus.defer(10, this);
22717             var ml = this.menuListeners;
22718             this.menu.un("select", ml.select,  this);
22719             this.menu.un("show", ml.show,  this);
22720             this.menu.un("hide", ml.hide,  this);
22721         }
22722     },
22723     // private
22724     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22725     onTriggerClick : function(){
22726         if(this.disabled){
22727             return;
22728         }
22729         if(this.menu == null){
22730             this.menu = new Roo.menu.DateMenu();
22731            
22732         }
22733         
22734         Roo.apply(this.menu.picker,  {
22735             
22736             showClear: this.allowBlank,
22737             minDate : this.minValue,
22738             maxDate : this.maxValue,
22739             disabledDatesRE : this.ddMatch,
22740             disabledDatesText : this.disabledDatesText,
22741             
22742             format : this.useIso ? 'Y-m-d' : this.format,
22743             minText : String.format(this.minText, this.formatDate(this.minValue)),
22744             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22745             
22746         });
22747          this.menu.on(Roo.apply({}, this.menuListeners, {
22748             scope:this
22749         }));
22750        
22751         
22752         var m = this.menu;
22753         var p = m.picker;
22754         
22755         // hide month picker get's called when we called by 'before hide';
22756         
22757         var ignorehide = true;
22758         p.hideMonthPicker  = function(disableAnim){
22759             if (ignorehide) {
22760                 return;
22761             }
22762              if(this.monthPicker){
22763                 Roo.log("hideMonthPicker called");
22764                 if(disableAnim === true){
22765                     this.monthPicker.hide();
22766                 }else{
22767                     this.monthPicker.slideOut('t', {duration:.2});
22768                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
22769                     p.fireEvent("select", this, this.value);
22770                     m.hide();
22771                 }
22772             }
22773         }
22774         
22775         Roo.log('picker set value');
22776         Roo.log(this.getValue());
22777         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
22778         m.show(this.el, 'tl-bl?');
22779         ignorehide  = false;
22780         // this will trigger hideMonthPicker..
22781         
22782         
22783         // hidden the day picker
22784         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
22785         
22786         
22787         
22788       
22789         
22790         p.showMonthPicker.defer(100, p);
22791     
22792         
22793        
22794     },
22795
22796     beforeBlur : function(){
22797         var v = this.parseDate(this.getRawValue());
22798         if(v){
22799             this.setValue(v);
22800         }
22801     }
22802
22803     /** @cfg {Boolean} grow @hide */
22804     /** @cfg {Number} growMin @hide */
22805     /** @cfg {Number} growMax @hide */
22806     /**
22807      * @hide
22808      * @method autoSize
22809      */
22810 });/*
22811  * Based on:
22812  * Ext JS Library 1.1.1
22813  * Copyright(c) 2006-2007, Ext JS, LLC.
22814  *
22815  * Originally Released Under LGPL - original licence link has changed is not relivant.
22816  *
22817  * Fork - LGPL
22818  * <script type="text/javascript">
22819  */
22820  
22821
22822 /**
22823  * @class Roo.form.ComboBox
22824  * @extends Roo.form.TriggerField
22825  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22826  * @constructor
22827  * Create a new ComboBox.
22828  * @param {Object} config Configuration options
22829  */
22830 Roo.form.ComboBox = function(config){
22831     Roo.form.ComboBox.superclass.constructor.call(this, config);
22832     this.addEvents({
22833         /**
22834          * @event expand
22835          * Fires when the dropdown list is expanded
22836              * @param {Roo.form.ComboBox} combo This combo box
22837              */
22838         'expand' : true,
22839         /**
22840          * @event collapse
22841          * Fires when the dropdown list is collapsed
22842              * @param {Roo.form.ComboBox} combo This combo box
22843              */
22844         'collapse' : true,
22845         /**
22846          * @event beforeselect
22847          * Fires before a list item is selected. Return false to cancel the selection.
22848              * @param {Roo.form.ComboBox} combo This combo box
22849              * @param {Roo.data.Record} record The data record returned from the underlying store
22850              * @param {Number} index The index of the selected item in the dropdown list
22851              */
22852         'beforeselect' : true,
22853         /**
22854          * @event select
22855          * Fires when a list item is selected
22856              * @param {Roo.form.ComboBox} combo This combo box
22857              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22858              * @param {Number} index The index of the selected item in the dropdown list
22859              */
22860         'select' : true,
22861         /**
22862          * @event beforequery
22863          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22864          * The event object passed has these properties:
22865              * @param {Roo.form.ComboBox} combo This combo box
22866              * @param {String} query The query
22867              * @param {Boolean} forceAll true to force "all" query
22868              * @param {Boolean} cancel true to cancel the query
22869              * @param {Object} e The query event object
22870              */
22871         'beforequery': true,
22872          /**
22873          * @event add
22874          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22875              * @param {Roo.form.ComboBox} combo This combo box
22876              */
22877         'add' : true,
22878         /**
22879          * @event edit
22880          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22881              * @param {Roo.form.ComboBox} combo This combo box
22882              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22883              */
22884         'edit' : true
22885         
22886         
22887     });
22888     if(this.transform){
22889         this.allowDomMove = false;
22890         var s = Roo.getDom(this.transform);
22891         if(!this.hiddenName){
22892             this.hiddenName = s.name;
22893         }
22894         if(!this.store){
22895             this.mode = 'local';
22896             var d = [], opts = s.options;
22897             for(var i = 0, len = opts.length;i < len; i++){
22898                 var o = opts[i];
22899                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22900                 if(o.selected) {
22901                     this.value = value;
22902                 }
22903                 d.push([value, o.text]);
22904             }
22905             this.store = new Roo.data.SimpleStore({
22906                 'id': 0,
22907                 fields: ['value', 'text'],
22908                 data : d
22909             });
22910             this.valueField = 'value';
22911             this.displayField = 'text';
22912         }
22913         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22914         if(!this.lazyRender){
22915             this.target = true;
22916             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22917             s.parentNode.removeChild(s); // remove it
22918             this.render(this.el.parentNode);
22919         }else{
22920             s.parentNode.removeChild(s); // remove it
22921         }
22922
22923     }
22924     if (this.store) {
22925         this.store = Roo.factory(this.store, Roo.data);
22926     }
22927     
22928     this.selectedIndex = -1;
22929     if(this.mode == 'local'){
22930         if(config.queryDelay === undefined){
22931             this.queryDelay = 10;
22932         }
22933         if(config.minChars === undefined){
22934             this.minChars = 0;
22935         }
22936     }
22937 };
22938
22939 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22940     /**
22941      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22942      */
22943     /**
22944      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22945      * rendering into an Roo.Editor, defaults to false)
22946      */
22947     /**
22948      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22949      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22950      */
22951     /**
22952      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22953      */
22954     /**
22955      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22956      * the dropdown list (defaults to undefined, with no header element)
22957      */
22958
22959      /**
22960      * @cfg {String/Roo.Template} tpl The template to use to render the output
22961      */
22962      
22963     // private
22964     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22965     /**
22966      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22967      */
22968     listWidth: undefined,
22969     /**
22970      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22971      * mode = 'remote' or 'text' if mode = 'local')
22972      */
22973     displayField: undefined,
22974     /**
22975      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22976      * mode = 'remote' or 'value' if mode = 'local'). 
22977      * Note: use of a valueField requires the user make a selection
22978      * in order for a value to be mapped.
22979      */
22980     valueField: undefined,
22981     
22982     
22983     /**
22984      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22985      * field's data value (defaults to the underlying DOM element's name)
22986      */
22987     hiddenName: undefined,
22988     /**
22989      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22990      */
22991     listClass: '',
22992     /**
22993      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22994      */
22995     selectedClass: 'x-combo-selected',
22996     /**
22997      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22998      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22999      * which displays a downward arrow icon).
23000      */
23001     triggerClass : 'x-form-arrow-trigger',
23002     /**
23003      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23004      */
23005     shadow:'sides',
23006     /**
23007      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23008      * anchor positions (defaults to 'tl-bl')
23009      */
23010     listAlign: 'tl-bl?',
23011     /**
23012      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23013      */
23014     maxHeight: 300,
23015     /**
23016      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23017      * query specified by the allQuery config option (defaults to 'query')
23018      */
23019     triggerAction: 'query',
23020     /**
23021      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23022      * (defaults to 4, does not apply if editable = false)
23023      */
23024     minChars : 4,
23025     /**
23026      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23027      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23028      */
23029     typeAhead: false,
23030     /**
23031      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23032      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23033      */
23034     queryDelay: 500,
23035     /**
23036      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23037      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23038      */
23039     pageSize: 0,
23040     /**
23041      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23042      * when editable = true (defaults to false)
23043      */
23044     selectOnFocus:false,
23045     /**
23046      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23047      */
23048     queryParam: 'query',
23049     /**
23050      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23051      * when mode = 'remote' (defaults to 'Loading...')
23052      */
23053     loadingText: 'Loading...',
23054     /**
23055      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23056      */
23057     resizable: false,
23058     /**
23059      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23060      */
23061     handleHeight : 8,
23062     /**
23063      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23064      * traditional select (defaults to true)
23065      */
23066     editable: true,
23067     /**
23068      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23069      */
23070     allQuery: '',
23071     /**
23072      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23073      */
23074     mode: 'remote',
23075     /**
23076      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23077      * listWidth has a higher value)
23078      */
23079     minListWidth : 70,
23080     /**
23081      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23082      * allow the user to set arbitrary text into the field (defaults to false)
23083      */
23084     forceSelection:false,
23085     /**
23086      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23087      * if typeAhead = true (defaults to 250)
23088      */
23089     typeAheadDelay : 250,
23090     /**
23091      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23092      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23093      */
23094     valueNotFoundText : undefined,
23095     /**
23096      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23097      */
23098     blockFocus : false,
23099     
23100     /**
23101      * @cfg {Boolean} disableClear Disable showing of clear button.
23102      */
23103     disableClear : false,
23104     /**
23105      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23106      */
23107     alwaysQuery : false,
23108     
23109     //private
23110     addicon : false,
23111     editicon: false,
23112     
23113     // element that contains real text value.. (when hidden is used..)
23114      
23115     // private
23116     onRender : function(ct, position){
23117         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23118         if(this.hiddenName){
23119             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23120                     'before', true);
23121             this.hiddenField.value =
23122                 this.hiddenValue !== undefined ? this.hiddenValue :
23123                 this.value !== undefined ? this.value : '';
23124
23125             // prevent input submission
23126             this.el.dom.removeAttribute('name');
23127              
23128              
23129         }
23130         if(Roo.isGecko){
23131             this.el.dom.setAttribute('autocomplete', 'off');
23132         }
23133
23134         var cls = 'x-combo-list';
23135
23136         this.list = new Roo.Layer({
23137             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23138         });
23139
23140         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23141         this.list.setWidth(lw);
23142         this.list.swallowEvent('mousewheel');
23143         this.assetHeight = 0;
23144
23145         if(this.title){
23146             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23147             this.assetHeight += this.header.getHeight();
23148         }
23149
23150         this.innerList = this.list.createChild({cls:cls+'-inner'});
23151         this.innerList.on('mouseover', this.onViewOver, this);
23152         this.innerList.on('mousemove', this.onViewMove, this);
23153         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23154         
23155         if(this.allowBlank && !this.pageSize && !this.disableClear){
23156             this.footer = this.list.createChild({cls:cls+'-ft'});
23157             this.pageTb = new Roo.Toolbar(this.footer);
23158            
23159         }
23160         if(this.pageSize){
23161             this.footer = this.list.createChild({cls:cls+'-ft'});
23162             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23163                     {pageSize: this.pageSize});
23164             
23165         }
23166         
23167         if (this.pageTb && this.allowBlank && !this.disableClear) {
23168             var _this = this;
23169             this.pageTb.add(new Roo.Toolbar.Fill(), {
23170                 cls: 'x-btn-icon x-btn-clear',
23171                 text: '&#160;',
23172                 handler: function()
23173                 {
23174                     _this.collapse();
23175                     _this.clearValue();
23176                     _this.onSelect(false, -1);
23177                 }
23178             });
23179         }
23180         if (this.footer) {
23181             this.assetHeight += this.footer.getHeight();
23182         }
23183         
23184
23185         if(!this.tpl){
23186             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23187         }
23188
23189         this.view = new Roo.View(this.innerList, this.tpl, {
23190             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23191         });
23192
23193         this.view.on('click', this.onViewClick, this);
23194
23195         this.store.on('beforeload', this.onBeforeLoad, this);
23196         this.store.on('load', this.onLoad, this);
23197         this.store.on('loadexception', this.onLoadException, this);
23198
23199         if(this.resizable){
23200             this.resizer = new Roo.Resizable(this.list,  {
23201                pinned:true, handles:'se'
23202             });
23203             this.resizer.on('resize', function(r, w, h){
23204                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23205                 this.listWidth = w;
23206                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23207                 this.restrictHeight();
23208             }, this);
23209             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23210         }
23211         if(!this.editable){
23212             this.editable = true;
23213             this.setEditable(false);
23214         }  
23215         
23216         
23217         if (typeof(this.events.add.listeners) != 'undefined') {
23218             
23219             this.addicon = this.wrap.createChild(
23220                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23221        
23222             this.addicon.on('click', function(e) {
23223                 this.fireEvent('add', this);
23224             }, this);
23225         }
23226         if (typeof(this.events.edit.listeners) != 'undefined') {
23227             
23228             this.editicon = this.wrap.createChild(
23229                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23230             if (this.addicon) {
23231                 this.editicon.setStyle('margin-left', '40px');
23232             }
23233             this.editicon.on('click', function(e) {
23234                 
23235                 // we fire even  if inothing is selected..
23236                 this.fireEvent('edit', this, this.lastData );
23237                 
23238             }, this);
23239         }
23240         
23241         
23242         
23243     },
23244
23245     // private
23246     initEvents : function(){
23247         Roo.form.ComboBox.superclass.initEvents.call(this);
23248
23249         this.keyNav = new Roo.KeyNav(this.el, {
23250             "up" : function(e){
23251                 this.inKeyMode = true;
23252                 this.selectPrev();
23253             },
23254
23255             "down" : function(e){
23256                 if(!this.isExpanded()){
23257                     this.onTriggerClick();
23258                 }else{
23259                     this.inKeyMode = true;
23260                     this.selectNext();
23261                 }
23262             },
23263
23264             "enter" : function(e){
23265                 this.onViewClick();
23266                 //return true;
23267             },
23268
23269             "esc" : function(e){
23270                 this.collapse();
23271             },
23272
23273             "tab" : function(e){
23274                 this.onViewClick(false);
23275                 this.fireEvent("specialkey", this, e);
23276                 return true;
23277             },
23278
23279             scope : this,
23280
23281             doRelay : function(foo, bar, hname){
23282                 if(hname == 'down' || this.scope.isExpanded()){
23283                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23284                 }
23285                 return true;
23286             },
23287
23288             forceKeyDown: true
23289         });
23290         this.queryDelay = Math.max(this.queryDelay || 10,
23291                 this.mode == 'local' ? 10 : 250);
23292         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23293         if(this.typeAhead){
23294             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23295         }
23296         if(this.editable !== false){
23297             this.el.on("keyup", this.onKeyUp, this);
23298         }
23299         if(this.forceSelection){
23300             this.on('blur', this.doForce, this);
23301         }
23302     },
23303
23304     onDestroy : function(){
23305         if(this.view){
23306             this.view.setStore(null);
23307             this.view.el.removeAllListeners();
23308             this.view.el.remove();
23309             this.view.purgeListeners();
23310         }
23311         if(this.list){
23312             this.list.destroy();
23313         }
23314         if(this.store){
23315             this.store.un('beforeload', this.onBeforeLoad, this);
23316             this.store.un('load', this.onLoad, this);
23317             this.store.un('loadexception', this.onLoadException, this);
23318         }
23319         Roo.form.ComboBox.superclass.onDestroy.call(this);
23320     },
23321
23322     // private
23323     fireKey : function(e){
23324         if(e.isNavKeyPress() && !this.list.isVisible()){
23325             this.fireEvent("specialkey", this, e);
23326         }
23327     },
23328
23329     // private
23330     onResize: function(w, h){
23331         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23332         
23333         if(typeof w != 'number'){
23334             // we do not handle it!?!?
23335             return;
23336         }
23337         var tw = this.trigger.getWidth();
23338         tw += this.addicon ? this.addicon.getWidth() : 0;
23339         tw += this.editicon ? this.editicon.getWidth() : 0;
23340         var x = w - tw;
23341         this.el.setWidth( this.adjustWidth('input', x));
23342             
23343         this.trigger.setStyle('left', x+'px');
23344         
23345         if(this.list && this.listWidth === undefined){
23346             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23347             this.list.setWidth(lw);
23348             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23349         }
23350         
23351     
23352         
23353     },
23354
23355     /**
23356      * Allow or prevent the user from directly editing the field text.  If false is passed,
23357      * the user will only be able to select from the items defined in the dropdown list.  This method
23358      * is the runtime equivalent of setting the 'editable' config option at config time.
23359      * @param {Boolean} value True to allow the user to directly edit the field text
23360      */
23361     setEditable : function(value){
23362         if(value == this.editable){
23363             return;
23364         }
23365         this.editable = value;
23366         if(!value){
23367             this.el.dom.setAttribute('readOnly', true);
23368             this.el.on('mousedown', this.onTriggerClick,  this);
23369             this.el.addClass('x-combo-noedit');
23370         }else{
23371             this.el.dom.setAttribute('readOnly', false);
23372             this.el.un('mousedown', this.onTriggerClick,  this);
23373             this.el.removeClass('x-combo-noedit');
23374         }
23375     },
23376
23377     // private
23378     onBeforeLoad : function(){
23379         if(!this.hasFocus){
23380             return;
23381         }
23382         this.innerList.update(this.loadingText ?
23383                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23384         this.restrictHeight();
23385         this.selectedIndex = -1;
23386     },
23387
23388     // private
23389     onLoad : function(){
23390         if(!this.hasFocus){
23391             return;
23392         }
23393         if(this.store.getCount() > 0){
23394             this.expand();
23395             this.restrictHeight();
23396             if(this.lastQuery == this.allQuery){
23397                 if(this.editable){
23398                     this.el.dom.select();
23399                 }
23400                 if(!this.selectByValue(this.value, true)){
23401                     this.select(0, true);
23402                 }
23403             }else{
23404                 this.selectNext();
23405                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23406                     this.taTask.delay(this.typeAheadDelay);
23407                 }
23408             }
23409         }else{
23410             this.onEmptyResults();
23411         }
23412         //this.el.focus();
23413     },
23414     // private
23415     onLoadException : function()
23416     {
23417         this.collapse();
23418         Roo.log(this.store.reader.jsonData);
23419         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23420             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23421         }
23422         
23423         
23424     },
23425     // private
23426     onTypeAhead : function(){
23427         if(this.store.getCount() > 0){
23428             var r = this.store.getAt(0);
23429             var newValue = r.data[this.displayField];
23430             var len = newValue.length;
23431             var selStart = this.getRawValue().length;
23432             if(selStart != len){
23433                 this.setRawValue(newValue);
23434                 this.selectText(selStart, newValue.length);
23435             }
23436         }
23437     },
23438
23439     // private
23440     onSelect : function(record, index){
23441         if(this.fireEvent('beforeselect', this, record, index) !== false){
23442             this.setFromData(index > -1 ? record.data : false);
23443             this.collapse();
23444             this.fireEvent('select', this, record, index);
23445         }
23446     },
23447
23448     /**
23449      * Returns the currently selected field value or empty string if no value is set.
23450      * @return {String} value The selected value
23451      */
23452     getValue : function(){
23453         if(this.valueField){
23454             return typeof this.value != 'undefined' ? this.value : '';
23455         }
23456         return Roo.form.ComboBox.superclass.getValue.call(this);
23457     },
23458
23459     /**
23460      * Clears any text/value currently set in the field
23461      */
23462     clearValue : function(){
23463         if(this.hiddenField){
23464             this.hiddenField.value = '';
23465         }
23466         this.value = '';
23467         this.setRawValue('');
23468         this.lastSelectionText = '';
23469         
23470     },
23471
23472     /**
23473      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23474      * will be displayed in the field.  If the value does not match the data value of an existing item,
23475      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23476      * Otherwise the field will be blank (although the value will still be set).
23477      * @param {String} value The value to match
23478      */
23479     setValue : function(v){
23480         var text = v;
23481         if(this.valueField){
23482             var r = this.findRecord(this.valueField, v);
23483             if(r){
23484                 text = r.data[this.displayField];
23485             }else if(this.valueNotFoundText !== undefined){
23486                 text = this.valueNotFoundText;
23487             }
23488         }
23489         this.lastSelectionText = text;
23490         if(this.hiddenField){
23491             this.hiddenField.value = v;
23492         }
23493         Roo.form.ComboBox.superclass.setValue.call(this, text);
23494         this.value = v;
23495     },
23496     /**
23497      * @property {Object} the last set data for the element
23498      */
23499     
23500     lastData : false,
23501     /**
23502      * Sets the value of the field based on a object which is related to the record format for the store.
23503      * @param {Object} value the value to set as. or false on reset?
23504      */
23505     setFromData : function(o){
23506         var dv = ''; // display value
23507         var vv = ''; // value value..
23508         this.lastData = o;
23509         if (this.displayField) {
23510             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23511         } else {
23512             // this is an error condition!!!
23513             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23514         }
23515         
23516         if(this.valueField){
23517             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23518         }
23519         if(this.hiddenField){
23520             this.hiddenField.value = vv;
23521             
23522             this.lastSelectionText = dv;
23523             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23524             this.value = vv;
23525             return;
23526         }
23527         // no hidden field.. - we store the value in 'value', but still display
23528         // display field!!!!
23529         this.lastSelectionText = dv;
23530         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23531         this.value = vv;
23532         
23533         
23534     },
23535     // private
23536     reset : function(){
23537         // overridden so that last data is reset..
23538         this.setValue(this.resetValue);
23539         this.clearInvalid();
23540         this.lastData = false;
23541         if (this.view) {
23542             this.view.clearSelections();
23543         }
23544     },
23545     // private
23546     findRecord : function(prop, value){
23547         var record;
23548         if(this.store.getCount() > 0){
23549             this.store.each(function(r){
23550                 if(r.data[prop] == value){
23551                     record = r;
23552                     return false;
23553                 }
23554                 return true;
23555             });
23556         }
23557         return record;
23558     },
23559     
23560     getName: function()
23561     {
23562         // returns hidden if it's set..
23563         if (!this.rendered) {return ''};
23564         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23565         
23566     },
23567     // private
23568     onViewMove : function(e, t){
23569         this.inKeyMode = false;
23570     },
23571
23572     // private
23573     onViewOver : function(e, t){
23574         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23575             return;
23576         }
23577         var item = this.view.findItemFromChild(t);
23578         if(item){
23579             var index = this.view.indexOf(item);
23580             this.select(index, false);
23581         }
23582     },
23583
23584     // private
23585     onViewClick : function(doFocus)
23586     {
23587         var index = this.view.getSelectedIndexes()[0];
23588         var r = this.store.getAt(index);
23589         if(r){
23590             this.onSelect(r, index);
23591         }
23592         if(doFocus !== false && !this.blockFocus){
23593             this.el.focus();
23594         }
23595     },
23596
23597     // private
23598     restrictHeight : function(){
23599         this.innerList.dom.style.height = '';
23600         var inner = this.innerList.dom;
23601         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23602         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23603         this.list.beginUpdate();
23604         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23605         this.list.alignTo(this.el, this.listAlign);
23606         this.list.endUpdate();
23607     },
23608
23609     // private
23610     onEmptyResults : function(){
23611         this.collapse();
23612     },
23613
23614     /**
23615      * Returns true if the dropdown list is expanded, else false.
23616      */
23617     isExpanded : function(){
23618         return this.list.isVisible();
23619     },
23620
23621     /**
23622      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23623      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23624      * @param {String} value The data value of the item to select
23625      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23626      * selected item if it is not currently in view (defaults to true)
23627      * @return {Boolean} True if the value matched an item in the list, else false
23628      */
23629     selectByValue : function(v, scrollIntoView){
23630         if(v !== undefined && v !== null){
23631             var r = this.findRecord(this.valueField || this.displayField, v);
23632             if(r){
23633                 this.select(this.store.indexOf(r), scrollIntoView);
23634                 return true;
23635             }
23636         }
23637         return false;
23638     },
23639
23640     /**
23641      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23642      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23643      * @param {Number} index The zero-based index of the list item to select
23644      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23645      * selected item if it is not currently in view (defaults to true)
23646      */
23647     select : function(index, scrollIntoView){
23648         this.selectedIndex = index;
23649         this.view.select(index);
23650         if(scrollIntoView !== false){
23651             var el = this.view.getNode(index);
23652             if(el){
23653                 this.innerList.scrollChildIntoView(el, false);
23654             }
23655         }
23656     },
23657
23658     // private
23659     selectNext : function(){
23660         var ct = this.store.getCount();
23661         if(ct > 0){
23662             if(this.selectedIndex == -1){
23663                 this.select(0);
23664             }else if(this.selectedIndex < ct-1){
23665                 this.select(this.selectedIndex+1);
23666             }
23667         }
23668     },
23669
23670     // private
23671     selectPrev : function(){
23672         var ct = this.store.getCount();
23673         if(ct > 0){
23674             if(this.selectedIndex == -1){
23675                 this.select(0);
23676             }else if(this.selectedIndex != 0){
23677                 this.select(this.selectedIndex-1);
23678             }
23679         }
23680     },
23681
23682     // private
23683     onKeyUp : function(e){
23684         if(this.editable !== false && !e.isSpecialKey()){
23685             this.lastKey = e.getKey();
23686             this.dqTask.delay(this.queryDelay);
23687         }
23688     },
23689
23690     // private
23691     validateBlur : function(){
23692         return !this.list || !this.list.isVisible();   
23693     },
23694
23695     // private
23696     initQuery : function(){
23697         this.doQuery(this.getRawValue());
23698     },
23699
23700     // private
23701     doForce : function(){
23702         if(this.el.dom.value.length > 0){
23703             this.el.dom.value =
23704                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23705              
23706         }
23707     },
23708
23709     /**
23710      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23711      * query allowing the query action to be canceled if needed.
23712      * @param {String} query The SQL query to execute
23713      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23714      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23715      * saved in the current store (defaults to false)
23716      */
23717     doQuery : function(q, forceAll){
23718         if(q === undefined || q === null){
23719             q = '';
23720         }
23721         var qe = {
23722             query: q,
23723             forceAll: forceAll,
23724             combo: this,
23725             cancel:false
23726         };
23727         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23728             return false;
23729         }
23730         q = qe.query;
23731         forceAll = qe.forceAll;
23732         if(forceAll === true || (q.length >= this.minChars)){
23733             if(this.lastQuery != q || this.alwaysQuery){
23734                 this.lastQuery = q;
23735                 if(this.mode == 'local'){
23736                     this.selectedIndex = -1;
23737                     if(forceAll){
23738                         this.store.clearFilter();
23739                     }else{
23740                         this.store.filter(this.displayField, q);
23741                     }
23742                     this.onLoad();
23743                 }else{
23744                     this.store.baseParams[this.queryParam] = q;
23745                     this.store.load({
23746                         params: this.getParams(q)
23747                     });
23748                     this.expand();
23749                 }
23750             }else{
23751                 this.selectedIndex = -1;
23752                 this.onLoad();   
23753             }
23754         }
23755     },
23756
23757     // private
23758     getParams : function(q){
23759         var p = {};
23760         //p[this.queryParam] = q;
23761         if(this.pageSize){
23762             p.start = 0;
23763             p.limit = this.pageSize;
23764         }
23765         return p;
23766     },
23767
23768     /**
23769      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23770      */
23771     collapse : function(){
23772         if(!this.isExpanded()){
23773             return;
23774         }
23775         this.list.hide();
23776         Roo.get(document).un('mousedown', this.collapseIf, this);
23777         Roo.get(document).un('mousewheel', this.collapseIf, this);
23778         if (!this.editable) {
23779             Roo.get(document).un('keydown', this.listKeyPress, this);
23780         }
23781         this.fireEvent('collapse', this);
23782     },
23783
23784     // private
23785     collapseIf : function(e){
23786         if(!e.within(this.wrap) && !e.within(this.list)){
23787             this.collapse();
23788         }
23789     },
23790
23791     /**
23792      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23793      */
23794     expand : function(){
23795         if(this.isExpanded() || !this.hasFocus){
23796             return;
23797         }
23798         this.list.alignTo(this.el, this.listAlign);
23799         this.list.show();
23800         Roo.get(document).on('mousedown', this.collapseIf, this);
23801         Roo.get(document).on('mousewheel', this.collapseIf, this);
23802         if (!this.editable) {
23803             Roo.get(document).on('keydown', this.listKeyPress, this);
23804         }
23805         
23806         this.fireEvent('expand', this);
23807     },
23808
23809     // private
23810     // Implements the default empty TriggerField.onTriggerClick function
23811     onTriggerClick : function(){
23812         if(this.disabled){
23813             return;
23814         }
23815         if(this.isExpanded()){
23816             this.collapse();
23817             if (!this.blockFocus) {
23818                 this.el.focus();
23819             }
23820             
23821         }else {
23822             this.hasFocus = true;
23823             if(this.triggerAction == 'all') {
23824                 this.doQuery(this.allQuery, true);
23825             } else {
23826                 this.doQuery(this.getRawValue());
23827             }
23828             if (!this.blockFocus) {
23829                 this.el.focus();
23830             }
23831         }
23832     },
23833     listKeyPress : function(e)
23834     {
23835         //Roo.log('listkeypress');
23836         // scroll to first matching element based on key pres..
23837         if (e.isSpecialKey()) {
23838             return false;
23839         }
23840         var k = String.fromCharCode(e.getKey()).toUpperCase();
23841         //Roo.log(k);
23842         var match  = false;
23843         var csel = this.view.getSelectedNodes();
23844         var cselitem = false;
23845         if (csel.length) {
23846             var ix = this.view.indexOf(csel[0]);
23847             cselitem  = this.store.getAt(ix);
23848             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23849                 cselitem = false;
23850             }
23851             
23852         }
23853         
23854         this.store.each(function(v) { 
23855             if (cselitem) {
23856                 // start at existing selection.
23857                 if (cselitem.id == v.id) {
23858                     cselitem = false;
23859                 }
23860                 return;
23861             }
23862                 
23863             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23864                 match = this.store.indexOf(v);
23865                 return false;
23866             }
23867         }, this);
23868         
23869         if (match === false) {
23870             return true; // no more action?
23871         }
23872         // scroll to?
23873         this.view.select(match);
23874         var sn = Roo.get(this.view.getSelectedNodes()[0])
23875         sn.scrollIntoView(sn.dom.parentNode, false);
23876     }
23877
23878     /** 
23879     * @cfg {Boolean} grow 
23880     * @hide 
23881     */
23882     /** 
23883     * @cfg {Number} growMin 
23884     * @hide 
23885     */
23886     /** 
23887     * @cfg {Number} growMax 
23888     * @hide 
23889     */
23890     /**
23891      * @hide
23892      * @method autoSize
23893      */
23894 });/*
23895  * Copyright(c) 2010-2012, Roo J Solutions Limited
23896  *
23897  * Licence LGPL
23898  *
23899  */
23900
23901 /**
23902  * @class Roo.form.ComboBoxArray
23903  * @extends Roo.form.TextField
23904  * A facebook style adder... for lists of email / people / countries  etc...
23905  * pick multiple items from a combo box, and shows each one.
23906  *
23907  *  Fred [x]  Brian [x]  [Pick another |v]
23908  *
23909  *
23910  *  For this to work: it needs various extra information
23911  *    - normal combo problay has
23912  *      name, hiddenName
23913  *    + displayField, valueField
23914  *
23915  *    For our purpose...
23916  *
23917  *
23918  *   If we change from 'extends' to wrapping...
23919  *   
23920  *  
23921  *
23922  
23923  
23924  * @constructor
23925  * Create a new ComboBoxArray.
23926  * @param {Object} config Configuration options
23927  */
23928  
23929
23930 Roo.form.ComboBoxArray = function(config)
23931 {
23932     this.addEvents({
23933         /**
23934          * @event beforeremove
23935          * Fires before remove the value from the list
23936              * @param {Roo.form.ComboBoxArray} _self This combo box array
23937              * @param {Roo.form.ComboBoxArray.Item} item removed item
23938              */
23939         'beforeremove' : true,
23940         /**
23941          * @event remove
23942          * Fires when remove the value from the list
23943              * @param {Roo.form.ComboBoxArray} _self This combo box array
23944              * @param {Roo.form.ComboBoxArray.Item} item removed item
23945              */
23946         'remove' : true
23947         
23948         
23949     });
23950     
23951     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23952     
23953     this.items = new Roo.util.MixedCollection(false);
23954     
23955     // construct the child combo...
23956     
23957     
23958     
23959     
23960    
23961     
23962 }
23963
23964  
23965 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
23966
23967     /**
23968      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
23969      */
23970     
23971     lastData : false,
23972     
23973     // behavies liek a hiddne field
23974     inputType:      'hidden',
23975     /**
23976      * @cfg {Number} width The width of the box that displays the selected element
23977      */ 
23978     width:          300,
23979
23980     
23981     
23982     /**
23983      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
23984      */
23985     name : false,
23986     /**
23987      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
23988      */
23989     hiddenName : false,
23990     
23991     
23992     // private the array of items that are displayed..
23993     items  : false,
23994     // private - the hidden field el.
23995     hiddenEl : false,
23996     // private - the filed el..
23997     el : false,
23998     
23999     //validateValue : function() { return true; }, // all values are ok!
24000     //onAddClick: function() { },
24001     
24002     onRender : function(ct, position) 
24003     {
24004         
24005         // create the standard hidden element
24006         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24007         
24008         
24009         // give fake names to child combo;
24010         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24011         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24012         
24013         this.combo = Roo.factory(this.combo, Roo.form);
24014         this.combo.onRender(ct, position);
24015         if (typeof(this.combo.width) != 'undefined') {
24016             this.combo.onResize(this.combo.width,0);
24017         }
24018         
24019         this.combo.initEvents();
24020         
24021         // assigned so form know we need to do this..
24022         this.store          = this.combo.store;
24023         this.valueField     = this.combo.valueField;
24024         this.displayField   = this.combo.displayField ;
24025         
24026         
24027         this.combo.wrap.addClass('x-cbarray-grp');
24028         
24029         var cbwrap = this.combo.wrap.createChild(
24030             {tag: 'div', cls: 'x-cbarray-cb'},
24031             this.combo.el.dom
24032         );
24033         
24034              
24035         this.hiddenEl = this.combo.wrap.createChild({
24036             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24037         });
24038         this.el = this.combo.wrap.createChild({
24039             tag: 'input',  type:'hidden' , name: this.name, value : ''
24040         });
24041          //   this.el.dom.removeAttribute("name");
24042         
24043         
24044         this.outerWrap = this.combo.wrap;
24045         this.wrap = cbwrap;
24046         
24047         this.outerWrap.setWidth(this.width);
24048         this.outerWrap.dom.removeChild(this.el.dom);
24049         
24050         this.wrap.dom.appendChild(this.el.dom);
24051         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24052         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24053         
24054         this.combo.trigger.setStyle('position','relative');
24055         this.combo.trigger.setStyle('left', '0px');
24056         this.combo.trigger.setStyle('top', '2px');
24057         
24058         this.combo.el.setStyle('vertical-align', 'text-bottom');
24059         
24060         //this.trigger.setStyle('vertical-align', 'top');
24061         
24062         // this should use the code from combo really... on('add' ....)
24063         if (this.adder) {
24064             
24065         
24066             this.adder = this.outerWrap.createChild(
24067                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24068             var _t = this;
24069             this.adder.on('click', function(e) {
24070                 _t.fireEvent('adderclick', this, e);
24071             }, _t);
24072         }
24073         //var _t = this;
24074         //this.adder.on('click', this.onAddClick, _t);
24075         
24076         
24077         this.combo.on('select', function(cb, rec, ix) {
24078             this.addItem(rec.data);
24079             
24080             cb.setValue('');
24081             cb.el.dom.value = '';
24082             //cb.lastData = rec.data;
24083             // add to list
24084             
24085         }, this);
24086         
24087         
24088     },
24089     
24090     
24091     getName: function()
24092     {
24093         // returns hidden if it's set..
24094         if (!this.rendered) {return ''};
24095         return  this.hiddenName ? this.hiddenName : this.name;
24096         
24097     },
24098     
24099     
24100     onResize: function(w, h){
24101         
24102         return;
24103         // not sure if this is needed..
24104         //this.combo.onResize(w,h);
24105         
24106         if(typeof w != 'number'){
24107             // we do not handle it!?!?
24108             return;
24109         }
24110         var tw = this.combo.trigger.getWidth();
24111         tw += this.addicon ? this.addicon.getWidth() : 0;
24112         tw += this.editicon ? this.editicon.getWidth() : 0;
24113         var x = w - tw;
24114         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24115             
24116         this.combo.trigger.setStyle('left', '0px');
24117         
24118         if(this.list && this.listWidth === undefined){
24119             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24120             this.list.setWidth(lw);
24121             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24122         }
24123         
24124     
24125         
24126     },
24127     
24128     addItem: function(rec)
24129     {
24130         var valueField = this.combo.valueField;
24131         var displayField = this.combo.displayField;
24132         if (this.items.indexOfKey(rec[valueField]) > -1) {
24133             //console.log("GOT " + rec.data.id);
24134             return;
24135         }
24136         
24137         var x = new Roo.form.ComboBoxArray.Item({
24138             //id : rec[this.idField],
24139             data : rec,
24140             displayField : displayField ,
24141             tipField : displayField ,
24142             cb : this
24143         });
24144         // use the 
24145         this.items.add(rec[valueField],x);
24146         // add it before the element..
24147         this.updateHiddenEl();
24148         x.render(this.outerWrap, this.wrap.dom);
24149         // add the image handler..
24150     },
24151     
24152     updateHiddenEl : function()
24153     {
24154         this.validate();
24155         if (!this.hiddenEl) {
24156             return;
24157         }
24158         var ar = [];
24159         var idField = this.combo.valueField;
24160         
24161         this.items.each(function(f) {
24162             ar.push(f.data[idField]);
24163            
24164         });
24165         this.hiddenEl.dom.value = ar.join(',');
24166         this.validate();
24167     },
24168     
24169     reset : function()
24170     {
24171         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24172         this.items.each(function(f) {
24173            f.remove(); 
24174         });
24175         this.el.dom.value = '';
24176         if (this.hiddenEl) {
24177             this.hiddenEl.dom.value = '';
24178         }
24179         
24180     },
24181     getValue: function()
24182     {
24183         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24184     },
24185     setValue: function(v) // not a valid action - must use addItems..
24186     {
24187          
24188         this.reset();
24189         
24190         
24191         
24192         if (this.store.isLocal && (typeof(v) == 'string')) {
24193             // then we can use the store to find the values..
24194             // comma seperated at present.. this needs to allow JSON based encoding..
24195             this.hiddenEl.value  = v;
24196             var v_ar = [];
24197             Roo.each(v.split(','), function(k) {
24198                 Roo.log("CHECK " + this.valueField + ',' + k);
24199                 var li = this.store.query(this.valueField, k);
24200                 if (!li.length) {
24201                     return;
24202                 }
24203                 var add = {};
24204                 add[this.valueField] = k;
24205                 add[this.displayField] = li.item(0).data[this.displayField];
24206                 
24207                 this.addItem(add);
24208             }, this) 
24209              
24210         }
24211         if (typeof(v) == 'object' ) {
24212             // then let's assume it's an array of objects..
24213             Roo.each(v, function(l) {
24214                 this.addItem(l);
24215             }, this);
24216              
24217         }
24218         
24219         
24220     },
24221     setFromData: function(v)
24222     {
24223         // this recieves an object, if setValues is called.
24224         this.reset();
24225         this.el.dom.value = v[this.displayField];
24226         this.hiddenEl.dom.value = v[this.valueField];
24227         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24228             return;
24229         }
24230         var kv = v[this.valueField];
24231         var dv = v[this.displayField];
24232         kv = typeof(kv) != 'string' ? '' : kv;
24233         dv = typeof(dv) != 'string' ? '' : dv;
24234         
24235         
24236         var keys = kv.split(',');
24237         var display = dv.split(',');
24238         for (var i = 0 ; i < keys.length; i++) {
24239             
24240             add = {};
24241             add[this.valueField] = keys[i];
24242             add[this.displayField] = display[i];
24243             this.addItem(add);
24244         }
24245       
24246         
24247     },
24248     
24249     /**
24250      * Validates the combox array value
24251      * @return {Boolean} True if the value is valid, else false
24252      */
24253     validate : function(){
24254         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
24255             this.clearInvalid();
24256             return true;
24257         }
24258         return false;
24259     },
24260     
24261     validateValue : function(value){
24262         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24263         
24264     },
24265     
24266     /*@
24267      * overide
24268      * 
24269      */
24270     isDirty : function() {
24271         if(this.disabled) {
24272             return false;
24273         }
24274         
24275         try {
24276             var d = Roo.decode(String(this.originalValue));
24277         } catch (e) {
24278             return String(this.getValue()) !== String(this.originalValue);
24279         }
24280         
24281         var originalValue = [];
24282         
24283         for (var i = 0; i < d.length; i++){
24284             originalValue.push(d[i][this.valueField]);
24285         }
24286         
24287         return String(this.getValue()) !== String(originalValue.join(','));
24288         
24289     }
24290     
24291 });
24292
24293
24294
24295 /**
24296  * @class Roo.form.ComboBoxArray.Item
24297  * @extends Roo.BoxComponent
24298  * A selected item in the list
24299  *  Fred [x]  Brian [x]  [Pick another |v]
24300  * 
24301  * @constructor
24302  * Create a new item.
24303  * @param {Object} config Configuration options
24304  */
24305  
24306 Roo.form.ComboBoxArray.Item = function(config) {
24307     config.id = Roo.id();
24308     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24309 }
24310
24311 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24312     data : {},
24313     cb: false,
24314     displayField : false,
24315     tipField : false,
24316     
24317     
24318     defaultAutoCreate : {
24319         tag: 'div',
24320         cls: 'x-cbarray-item',
24321         cn : [ 
24322             { tag: 'div' },
24323             {
24324                 tag: 'img',
24325                 width:16,
24326                 height : 16,
24327                 src : Roo.BLANK_IMAGE_URL ,
24328                 align: 'center'
24329             }
24330         ]
24331         
24332     },
24333     
24334  
24335     onRender : function(ct, position)
24336     {
24337         Roo.form.Field.superclass.onRender.call(this, ct, position);
24338         
24339         if(!this.el){
24340             var cfg = this.getAutoCreate();
24341             this.el = ct.createChild(cfg, position);
24342         }
24343         
24344         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24345         
24346         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24347             this.cb.renderer(this.data) :
24348             String.format('{0}',this.data[this.displayField]);
24349         
24350             
24351         this.el.child('div').dom.setAttribute('qtip',
24352                         String.format('{0}',this.data[this.tipField])
24353         );
24354         
24355         this.el.child('img').on('click', this.remove, this);
24356         
24357     },
24358    
24359     remove : function()
24360     {
24361         if(this.cb.disabled){
24362             return;
24363         }
24364         
24365         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
24366             this.cb.items.remove(this);
24367             this.el.child('img').un('click', this.remove, this);
24368             this.el.remove();
24369             this.cb.updateHiddenEl();
24370
24371             this.cb.fireEvent('remove', this.cb, this);
24372         }
24373         
24374     }
24375 });/*
24376  * Based on:
24377  * Ext JS Library 1.1.1
24378  * Copyright(c) 2006-2007, Ext JS, LLC.
24379  *
24380  * Originally Released Under LGPL - original licence link has changed is not relivant.
24381  *
24382  * Fork - LGPL
24383  * <script type="text/javascript">
24384  */
24385 /**
24386  * @class Roo.form.Checkbox
24387  * @extends Roo.form.Field
24388  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24389  * @constructor
24390  * Creates a new Checkbox
24391  * @param {Object} config Configuration options
24392  */
24393 Roo.form.Checkbox = function(config){
24394     Roo.form.Checkbox.superclass.constructor.call(this, config);
24395     this.addEvents({
24396         /**
24397          * @event check
24398          * Fires when the checkbox is checked or unchecked.
24399              * @param {Roo.form.Checkbox} this This checkbox
24400              * @param {Boolean} checked The new checked value
24401              */
24402         check : true
24403     });
24404 };
24405
24406 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24407     /**
24408      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24409      */
24410     focusClass : undefined,
24411     /**
24412      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24413      */
24414     fieldClass: "x-form-field",
24415     /**
24416      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24417      */
24418     checked: false,
24419     /**
24420      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24421      * {tag: "input", type: "checkbox", autocomplete: "off"})
24422      */
24423     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24424     /**
24425      * @cfg {String} boxLabel The text that appears beside the checkbox
24426      */
24427     boxLabel : "",
24428     /**
24429      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24430      */  
24431     inputValue : '1',
24432     /**
24433      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24434      */
24435      valueOff: '0', // value when not checked..
24436
24437     actionMode : 'viewEl', 
24438     //
24439     // private
24440     itemCls : 'x-menu-check-item x-form-item',
24441     groupClass : 'x-menu-group-item',
24442     inputType : 'hidden',
24443     
24444     
24445     inSetChecked: false, // check that we are not calling self...
24446     
24447     inputElement: false, // real input element?
24448     basedOn: false, // ????
24449     
24450     isFormField: true, // not sure where this is needed!!!!
24451
24452     onResize : function(){
24453         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24454         if(!this.boxLabel){
24455             this.el.alignTo(this.wrap, 'c-c');
24456         }
24457     },
24458
24459     initEvents : function(){
24460         Roo.form.Checkbox.superclass.initEvents.call(this);
24461         this.el.on("click", this.onClick,  this);
24462         this.el.on("change", this.onClick,  this);
24463     },
24464
24465
24466     getResizeEl : function(){
24467         return this.wrap;
24468     },
24469
24470     getPositionEl : function(){
24471         return this.wrap;
24472     },
24473
24474     // private
24475     onRender : function(ct, position){
24476         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24477         /*
24478         if(this.inputValue !== undefined){
24479             this.el.dom.value = this.inputValue;
24480         }
24481         */
24482         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24483         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24484         var viewEl = this.wrap.createChild({ 
24485             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24486         this.viewEl = viewEl;   
24487         this.wrap.on('click', this.onClick,  this); 
24488         
24489         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24490         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24491         
24492         
24493         
24494         if(this.boxLabel){
24495             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24496         //    viewEl.on('click', this.onClick,  this); 
24497         }
24498         //if(this.checked){
24499             this.setChecked(this.checked);
24500         //}else{
24501             //this.checked = this.el.dom;
24502         //}
24503
24504     },
24505
24506     // private
24507     initValue : Roo.emptyFn,
24508
24509     /**
24510      * Returns the checked state of the checkbox.
24511      * @return {Boolean} True if checked, else false
24512      */
24513     getValue : function(){
24514         if(this.el){
24515             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24516         }
24517         return this.valueOff;
24518         
24519     },
24520
24521         // private
24522     onClick : function(){ 
24523         if (this.disabled) {
24524             return;
24525         }
24526         this.setChecked(!this.checked);
24527
24528         //if(this.el.dom.checked != this.checked){
24529         //    this.setValue(this.el.dom.checked);
24530        // }
24531     },
24532
24533     /**
24534      * Sets the checked state of the checkbox.
24535      * On is always based on a string comparison between inputValue and the param.
24536      * @param {Boolean/String} value - the value to set 
24537      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24538      */
24539     setValue : function(v,suppressEvent){
24540         
24541         
24542         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24543         //if(this.el && this.el.dom){
24544         //    this.el.dom.checked = this.checked;
24545         //    this.el.dom.defaultChecked = this.checked;
24546         //}
24547         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24548         //this.fireEvent("check", this, this.checked);
24549     },
24550     // private..
24551     setChecked : function(state,suppressEvent)
24552     {
24553         if (this.inSetChecked) {
24554             this.checked = state;
24555             return;
24556         }
24557         
24558     
24559         if(this.wrap){
24560             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24561         }
24562         this.checked = state;
24563         if(suppressEvent !== true){
24564             this.fireEvent('check', this, state);
24565         }
24566         this.inSetChecked = true;
24567         this.el.dom.value = state ? this.inputValue : this.valueOff;
24568         this.inSetChecked = false;
24569         
24570     },
24571     // handle setting of hidden value by some other method!!?!?
24572     setFromHidden: function()
24573     {
24574         if(!this.el){
24575             return;
24576         }
24577         //console.log("SET FROM HIDDEN");
24578         //alert('setFrom hidden');
24579         this.setValue(this.el.dom.value);
24580     },
24581     
24582     onDestroy : function()
24583     {
24584         if(this.viewEl){
24585             Roo.get(this.viewEl).remove();
24586         }
24587          
24588         Roo.form.Checkbox.superclass.onDestroy.call(this);
24589     }
24590
24591 });/*
24592  * Based on:
24593  * Ext JS Library 1.1.1
24594  * Copyright(c) 2006-2007, Ext JS, LLC.
24595  *
24596  * Originally Released Under LGPL - original licence link has changed is not relivant.
24597  *
24598  * Fork - LGPL
24599  * <script type="text/javascript">
24600  */
24601  
24602 /**
24603  * @class Roo.form.Radio
24604  * @extends Roo.form.Checkbox
24605  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24606  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24607  * @constructor
24608  * Creates a new Radio
24609  * @param {Object} config Configuration options
24610  */
24611 Roo.form.Radio = function(){
24612     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24613 };
24614 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24615     inputType: 'radio',
24616
24617     /**
24618      * If this radio is part of a group, it will return the selected value
24619      * @return {String}
24620      */
24621     getGroupValue : function(){
24622         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24623     },
24624     
24625     
24626     onRender : function(ct, position){
24627         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24628         
24629         if(this.inputValue !== undefined){
24630             this.el.dom.value = this.inputValue;
24631         }
24632          
24633         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24634         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24635         //var viewEl = this.wrap.createChild({ 
24636         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24637         //this.viewEl = viewEl;   
24638         //this.wrap.on('click', this.onClick,  this); 
24639         
24640         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24641         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
24642         
24643         
24644         
24645         if(this.boxLabel){
24646             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24647         //    viewEl.on('click', this.onClick,  this); 
24648         }
24649          if(this.checked){
24650             this.el.dom.checked =   'checked' ;
24651         }
24652          
24653     } 
24654     
24655     
24656 });//<script type="text/javascript">
24657
24658 /*
24659  * Based  Ext JS Library 1.1.1
24660  * Copyright(c) 2006-2007, Ext JS, LLC.
24661  * LGPL
24662  *
24663  */
24664  
24665 /**
24666  * @class Roo.HtmlEditorCore
24667  * @extends Roo.Component
24668  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24669  *
24670  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24671  */
24672
24673 Roo.HtmlEditorCore = function(config){
24674     
24675     
24676     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24677     
24678     
24679     this.addEvents({
24680         /**
24681          * @event initialize
24682          * Fires when the editor is fully initialized (including the iframe)
24683          * @param {Roo.HtmlEditorCore} this
24684          */
24685         initialize: true,
24686         /**
24687          * @event activate
24688          * Fires when the editor is first receives the focus. Any insertion must wait
24689          * until after this event.
24690          * @param {Roo.HtmlEditorCore} this
24691          */
24692         activate: true,
24693          /**
24694          * @event beforesync
24695          * Fires before the textarea is updated with content from the editor iframe. Return false
24696          * to cancel the sync.
24697          * @param {Roo.HtmlEditorCore} this
24698          * @param {String} html
24699          */
24700         beforesync: true,
24701          /**
24702          * @event beforepush
24703          * Fires before the iframe editor is updated with content from the textarea. Return false
24704          * to cancel the push.
24705          * @param {Roo.HtmlEditorCore} this
24706          * @param {String} html
24707          */
24708         beforepush: true,
24709          /**
24710          * @event sync
24711          * Fires when the textarea is updated with content from the editor iframe.
24712          * @param {Roo.HtmlEditorCore} this
24713          * @param {String} html
24714          */
24715         sync: true,
24716          /**
24717          * @event push
24718          * Fires when the iframe editor is updated with content from the textarea.
24719          * @param {Roo.HtmlEditorCore} this
24720          * @param {String} html
24721          */
24722         push: true,
24723         
24724         /**
24725          * @event editorevent
24726          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24727          * @param {Roo.HtmlEditorCore} this
24728          */
24729         editorevent: true
24730         
24731     });
24732     
24733     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24734     
24735     // defaults : white / black...
24736     this.applyBlacklists();
24737     
24738     
24739     
24740 };
24741
24742
24743 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24744
24745
24746      /**
24747      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24748      */
24749     
24750     owner : false,
24751     
24752      /**
24753      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24754      *                        Roo.resizable.
24755      */
24756     resizable : false,
24757      /**
24758      * @cfg {Number} height (in pixels)
24759      */   
24760     height: 300,
24761    /**
24762      * @cfg {Number} width (in pixels)
24763      */   
24764     width: 500,
24765     
24766     /**
24767      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24768      * 
24769      */
24770     stylesheets: false,
24771     
24772     // id of frame..
24773     frameId: false,
24774     
24775     // private properties
24776     validationEvent : false,
24777     deferHeight: true,
24778     initialized : false,
24779     activated : false,
24780     sourceEditMode : false,
24781     onFocus : Roo.emptyFn,
24782     iframePad:3,
24783     hideMode:'offsets',
24784     
24785     clearUp: true,
24786     
24787     // blacklist + whitelisted elements..
24788     black: false,
24789     white: false,
24790      
24791     
24792
24793     /**
24794      * Protected method that will not generally be called directly. It
24795      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24796      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24797      */
24798     getDocMarkup : function(){
24799         // body styles..
24800         var st = '';
24801         
24802         // inherit styels from page...?? 
24803         if (this.stylesheets === false) {
24804             
24805             Roo.get(document.head).select('style').each(function(node) {
24806                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24807             });
24808             
24809             Roo.get(document.head).select('link').each(function(node) { 
24810                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24811             });
24812             
24813         } else if (!this.stylesheets.length) {
24814                 // simple..
24815                 st = '<style type="text/css">' +
24816                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24817                    '</style>';
24818         } else { 
24819             
24820         }
24821         
24822         st +=  '<style type="text/css">' +
24823             'IMG { cursor: pointer } ' +
24824         '</style>';
24825
24826         
24827         return '<html><head>' + st  +
24828             //<style type="text/css">' +
24829             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24830             //'</style>' +
24831             ' </head><body class="roo-htmleditor-body"></body></html>';
24832     },
24833
24834     // private
24835     onRender : function(ct, position)
24836     {
24837         var _t = this;
24838         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24839         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24840         
24841         
24842         this.el.dom.style.border = '0 none';
24843         this.el.dom.setAttribute('tabIndex', -1);
24844         this.el.addClass('x-hidden hide');
24845         
24846         
24847         
24848         if(Roo.isIE){ // fix IE 1px bogus margin
24849             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24850         }
24851        
24852         
24853         this.frameId = Roo.id();
24854         
24855          
24856         
24857         var iframe = this.owner.wrap.createChild({
24858             tag: 'iframe',
24859             cls: 'form-control', // bootstrap..
24860             id: this.frameId,
24861             name: this.frameId,
24862             frameBorder : 'no',
24863             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24864         }, this.el
24865         );
24866         
24867         
24868         this.iframe = iframe.dom;
24869
24870          this.assignDocWin();
24871         
24872         this.doc.designMode = 'on';
24873        
24874         this.doc.open();
24875         this.doc.write(this.getDocMarkup());
24876         this.doc.close();
24877
24878         
24879         var task = { // must defer to wait for browser to be ready
24880             run : function(){
24881                 //console.log("run task?" + this.doc.readyState);
24882                 this.assignDocWin();
24883                 if(this.doc.body || this.doc.readyState == 'complete'){
24884                     try {
24885                         this.doc.designMode="on";
24886                     } catch (e) {
24887                         return;
24888                     }
24889                     Roo.TaskMgr.stop(task);
24890                     this.initEditor.defer(10, this);
24891                 }
24892             },
24893             interval : 10,
24894             duration: 10000,
24895             scope: this
24896         };
24897         Roo.TaskMgr.start(task);
24898
24899     },
24900
24901     // private
24902     onResize : function(w, h)
24903     {
24904          Roo.log('resize: ' +w + ',' + h );
24905         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24906         if(!this.iframe){
24907             return;
24908         }
24909         if(typeof w == 'number'){
24910             
24911             this.iframe.style.width = w + 'px';
24912         }
24913         if(typeof h == 'number'){
24914             
24915             this.iframe.style.height = h + 'px';
24916             if(this.doc){
24917                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24918             }
24919         }
24920         
24921     },
24922
24923     /**
24924      * Toggles the editor between standard and source edit mode.
24925      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24926      */
24927     toggleSourceEdit : function(sourceEditMode){
24928         
24929         this.sourceEditMode = sourceEditMode === true;
24930         
24931         if(this.sourceEditMode){
24932  
24933             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24934             
24935         }else{
24936             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24937             //this.iframe.className = '';
24938             this.deferFocus();
24939         }
24940         //this.setSize(this.owner.wrap.getSize());
24941         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24942     },
24943
24944     
24945   
24946
24947     /**
24948      * Protected method that will not generally be called directly. If you need/want
24949      * custom HTML cleanup, this is the method you should override.
24950      * @param {String} html The HTML to be cleaned
24951      * return {String} The cleaned HTML
24952      */
24953     cleanHtml : function(html){
24954         html = String(html);
24955         if(html.length > 5){
24956             if(Roo.isSafari){ // strip safari nonsense
24957                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24958             }
24959         }
24960         if(html == '&nbsp;'){
24961             html = '';
24962         }
24963         return html;
24964     },
24965
24966     /**
24967      * HTML Editor -> Textarea
24968      * Protected method that will not generally be called directly. Syncs the contents
24969      * of the editor iframe with the textarea.
24970      */
24971     syncValue : function(){
24972         if(this.initialized){
24973             var bd = (this.doc.body || this.doc.documentElement);
24974             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24975             var html = bd.innerHTML;
24976             if(Roo.isSafari){
24977                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24978                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24979                 if(m && m[1]){
24980                     html = '<div style="'+m[0]+'">' + html + '</div>';
24981                 }
24982             }
24983             html = this.cleanHtml(html);
24984             // fix up the special chars.. normaly like back quotes in word...
24985             // however we do not want to do this with chinese..
24986             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
24987                 var cc = b.charCodeAt();
24988                 if (
24989                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24990                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24991                     (cc >= 0xf900 && cc < 0xfb00 )
24992                 ) {
24993                         return b;
24994                 }
24995                 return "&#"+cc+";" 
24996             });
24997             if(this.owner.fireEvent('beforesync', this, html) !== false){
24998                 this.el.dom.value = html;
24999                 this.owner.fireEvent('sync', this, html);
25000             }
25001         }
25002     },
25003
25004     /**
25005      * Protected method that will not generally be called directly. Pushes the value of the textarea
25006      * into the iframe editor.
25007      */
25008     pushValue : function(){
25009         if(this.initialized){
25010             var v = this.el.dom.value.trim();
25011             
25012 //            if(v.length < 1){
25013 //                v = '&#160;';
25014 //            }
25015             
25016             if(this.owner.fireEvent('beforepush', this, v) !== false){
25017                 var d = (this.doc.body || this.doc.documentElement);
25018                 d.innerHTML = v;
25019                 this.cleanUpPaste();
25020                 this.el.dom.value = d.innerHTML;
25021                 this.owner.fireEvent('push', this, v);
25022             }
25023         }
25024     },
25025
25026     // private
25027     deferFocus : function(){
25028         this.focus.defer(10, this);
25029     },
25030
25031     // doc'ed in Field
25032     focus : function(){
25033         if(this.win && !this.sourceEditMode){
25034             this.win.focus();
25035         }else{
25036             this.el.focus();
25037         }
25038     },
25039     
25040     assignDocWin: function()
25041     {
25042         var iframe = this.iframe;
25043         
25044          if(Roo.isIE){
25045             this.doc = iframe.contentWindow.document;
25046             this.win = iframe.contentWindow;
25047         } else {
25048 //            if (!Roo.get(this.frameId)) {
25049 //                return;
25050 //            }
25051 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25052 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25053             
25054             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25055                 return;
25056             }
25057             
25058             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25059             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25060         }
25061     },
25062     
25063     // private
25064     initEditor : function(){
25065         //console.log("INIT EDITOR");
25066         this.assignDocWin();
25067         
25068         
25069         
25070         this.doc.designMode="on";
25071         this.doc.open();
25072         this.doc.write(this.getDocMarkup());
25073         this.doc.close();
25074         
25075         var dbody = (this.doc.body || this.doc.documentElement);
25076         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25077         // this copies styles from the containing element into thsi one..
25078         // not sure why we need all of this..
25079         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25080         
25081         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25082         //ss['background-attachment'] = 'fixed'; // w3c
25083         dbody.bgProperties = 'fixed'; // ie
25084         //Roo.DomHelper.applyStyles(dbody, ss);
25085         Roo.EventManager.on(this.doc, {
25086             //'mousedown': this.onEditorEvent,
25087             'mouseup': this.onEditorEvent,
25088             'dblclick': this.onEditorEvent,
25089             'click': this.onEditorEvent,
25090             'keyup': this.onEditorEvent,
25091             buffer:100,
25092             scope: this
25093         });
25094         if(Roo.isGecko){
25095             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25096         }
25097         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25098             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25099         }
25100         this.initialized = true;
25101
25102         this.owner.fireEvent('initialize', this);
25103         this.pushValue();
25104     },
25105
25106     // private
25107     onDestroy : function(){
25108         
25109         
25110         
25111         if(this.rendered){
25112             
25113             //for (var i =0; i < this.toolbars.length;i++) {
25114             //    // fixme - ask toolbars for heights?
25115             //    this.toolbars[i].onDestroy();
25116            // }
25117             
25118             //this.wrap.dom.innerHTML = '';
25119             //this.wrap.remove();
25120         }
25121     },
25122
25123     // private
25124     onFirstFocus : function(){
25125         
25126         this.assignDocWin();
25127         
25128         
25129         this.activated = true;
25130          
25131     
25132         if(Roo.isGecko){ // prevent silly gecko errors
25133             this.win.focus();
25134             var s = this.win.getSelection();
25135             if(!s.focusNode || s.focusNode.nodeType != 3){
25136                 var r = s.getRangeAt(0);
25137                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25138                 r.collapse(true);
25139                 this.deferFocus();
25140             }
25141             try{
25142                 this.execCmd('useCSS', true);
25143                 this.execCmd('styleWithCSS', false);
25144             }catch(e){}
25145         }
25146         this.owner.fireEvent('activate', this);
25147     },
25148
25149     // private
25150     adjustFont: function(btn){
25151         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25152         //if(Roo.isSafari){ // safari
25153         //    adjust *= 2;
25154        // }
25155         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25156         if(Roo.isSafari){ // safari
25157             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25158             v =  (v < 10) ? 10 : v;
25159             v =  (v > 48) ? 48 : v;
25160             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25161             
25162         }
25163         
25164         
25165         v = Math.max(1, v+adjust);
25166         
25167         this.execCmd('FontSize', v  );
25168     },
25169
25170     onEditorEvent : function(e)
25171     {
25172         this.owner.fireEvent('editorevent', this, e);
25173       //  this.updateToolbar();
25174         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25175     },
25176
25177     insertTag : function(tg)
25178     {
25179         // could be a bit smarter... -> wrap the current selected tRoo..
25180         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25181             
25182             range = this.createRange(this.getSelection());
25183             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25184             wrappingNode.appendChild(range.extractContents());
25185             range.insertNode(wrappingNode);
25186
25187             return;
25188             
25189             
25190             
25191         }
25192         this.execCmd("formatblock",   tg);
25193         
25194     },
25195     
25196     insertText : function(txt)
25197     {
25198         
25199         
25200         var range = this.createRange();
25201         range.deleteContents();
25202                //alert(Sender.getAttribute('label'));
25203                
25204         range.insertNode(this.doc.createTextNode(txt));
25205     } ,
25206     
25207      
25208
25209     /**
25210      * Executes a Midas editor command on the editor document and performs necessary focus and
25211      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25212      * @param {String} cmd The Midas command
25213      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25214      */
25215     relayCmd : function(cmd, value){
25216         this.win.focus();
25217         this.execCmd(cmd, value);
25218         this.owner.fireEvent('editorevent', this);
25219         //this.updateToolbar();
25220         this.owner.deferFocus();
25221     },
25222
25223     /**
25224      * Executes a Midas editor command directly on the editor document.
25225      * For visual commands, you should use {@link #relayCmd} instead.
25226      * <b>This should only be called after the editor is initialized.</b>
25227      * @param {String} cmd The Midas command
25228      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25229      */
25230     execCmd : function(cmd, value){
25231         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25232         this.syncValue();
25233     },
25234  
25235  
25236    
25237     /**
25238      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25239      * to insert tRoo.
25240      * @param {String} text | dom node.. 
25241      */
25242     insertAtCursor : function(text)
25243     {
25244         
25245         
25246         
25247         if(!this.activated){
25248             return;
25249         }
25250         /*
25251         if(Roo.isIE){
25252             this.win.focus();
25253             var r = this.doc.selection.createRange();
25254             if(r){
25255                 r.collapse(true);
25256                 r.pasteHTML(text);
25257                 this.syncValue();
25258                 this.deferFocus();
25259             
25260             }
25261             return;
25262         }
25263         */
25264         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25265             this.win.focus();
25266             
25267             
25268             // from jquery ui (MIT licenced)
25269             var range, node;
25270             var win = this.win;
25271             
25272             if (win.getSelection && win.getSelection().getRangeAt) {
25273                 range = win.getSelection().getRangeAt(0);
25274                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25275                 range.insertNode(node);
25276             } else if (win.document.selection && win.document.selection.createRange) {
25277                 // no firefox support
25278                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25279                 win.document.selection.createRange().pasteHTML(txt);
25280             } else {
25281                 // no firefox support
25282                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25283                 this.execCmd('InsertHTML', txt);
25284             } 
25285             
25286             this.syncValue();
25287             
25288             this.deferFocus();
25289         }
25290     },
25291  // private
25292     mozKeyPress : function(e){
25293         if(e.ctrlKey){
25294             var c = e.getCharCode(), cmd;
25295           
25296             if(c > 0){
25297                 c = String.fromCharCode(c).toLowerCase();
25298                 switch(c){
25299                     case 'b':
25300                         cmd = 'bold';
25301                         break;
25302                     case 'i':
25303                         cmd = 'italic';
25304                         break;
25305                     
25306                     case 'u':
25307                         cmd = 'underline';
25308                         break;
25309                     
25310                     case 'v':
25311                         this.cleanUpPaste.defer(100, this);
25312                         return;
25313                         
25314                 }
25315                 if(cmd){
25316                     this.win.focus();
25317                     this.execCmd(cmd);
25318                     this.deferFocus();
25319                     e.preventDefault();
25320                 }
25321                 
25322             }
25323         }
25324     },
25325
25326     // private
25327     fixKeys : function(){ // load time branching for fastest keydown performance
25328         if(Roo.isIE){
25329             return function(e){
25330                 var k = e.getKey(), r;
25331                 if(k == e.TAB){
25332                     e.stopEvent();
25333                     r = this.doc.selection.createRange();
25334                     if(r){
25335                         r.collapse(true);
25336                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25337                         this.deferFocus();
25338                     }
25339                     return;
25340                 }
25341                 
25342                 if(k == e.ENTER){
25343                     r = this.doc.selection.createRange();
25344                     if(r){
25345                         var target = r.parentElement();
25346                         if(!target || target.tagName.toLowerCase() != 'li'){
25347                             e.stopEvent();
25348                             r.pasteHTML('<br />');
25349                             r.collapse(false);
25350                             r.select();
25351                         }
25352                     }
25353                 }
25354                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25355                     this.cleanUpPaste.defer(100, this);
25356                     return;
25357                 }
25358                 
25359                 
25360             };
25361         }else if(Roo.isOpera){
25362             return function(e){
25363                 var k = e.getKey();
25364                 if(k == e.TAB){
25365                     e.stopEvent();
25366                     this.win.focus();
25367                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25368                     this.deferFocus();
25369                 }
25370                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25371                     this.cleanUpPaste.defer(100, this);
25372                     return;
25373                 }
25374                 
25375             };
25376         }else if(Roo.isSafari){
25377             return function(e){
25378                 var k = e.getKey();
25379                 
25380                 if(k == e.TAB){
25381                     e.stopEvent();
25382                     this.execCmd('InsertText','\t');
25383                     this.deferFocus();
25384                     return;
25385                 }
25386                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25387                     this.cleanUpPaste.defer(100, this);
25388                     return;
25389                 }
25390                 
25391              };
25392         }
25393     }(),
25394     
25395     getAllAncestors: function()
25396     {
25397         var p = this.getSelectedNode();
25398         var a = [];
25399         if (!p) {
25400             a.push(p); // push blank onto stack..
25401             p = this.getParentElement();
25402         }
25403         
25404         
25405         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25406             a.push(p);
25407             p = p.parentNode;
25408         }
25409         a.push(this.doc.body);
25410         return a;
25411     },
25412     lastSel : false,
25413     lastSelNode : false,
25414     
25415     
25416     getSelection : function() 
25417     {
25418         this.assignDocWin();
25419         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25420     },
25421     
25422     getSelectedNode: function() 
25423     {
25424         // this may only work on Gecko!!!
25425         
25426         // should we cache this!!!!
25427         
25428         
25429         
25430          
25431         var range = this.createRange(this.getSelection()).cloneRange();
25432         
25433         if (Roo.isIE) {
25434             var parent = range.parentElement();
25435             while (true) {
25436                 var testRange = range.duplicate();
25437                 testRange.moveToElementText(parent);
25438                 if (testRange.inRange(range)) {
25439                     break;
25440                 }
25441                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25442                     break;
25443                 }
25444                 parent = parent.parentElement;
25445             }
25446             return parent;
25447         }
25448         
25449         // is ancestor a text element.
25450         var ac =  range.commonAncestorContainer;
25451         if (ac.nodeType == 3) {
25452             ac = ac.parentNode;
25453         }
25454         
25455         var ar = ac.childNodes;
25456          
25457         var nodes = [];
25458         var other_nodes = [];
25459         var has_other_nodes = false;
25460         for (var i=0;i<ar.length;i++) {
25461             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25462                 continue;
25463             }
25464             // fullly contained node.
25465             
25466             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25467                 nodes.push(ar[i]);
25468                 continue;
25469             }
25470             
25471             // probably selected..
25472             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25473                 other_nodes.push(ar[i]);
25474                 continue;
25475             }
25476             // outer..
25477             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25478                 continue;
25479             }
25480             
25481             
25482             has_other_nodes = true;
25483         }
25484         if (!nodes.length && other_nodes.length) {
25485             nodes= other_nodes;
25486         }
25487         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25488             return false;
25489         }
25490         
25491         return nodes[0];
25492     },
25493     createRange: function(sel)
25494     {
25495         // this has strange effects when using with 
25496         // top toolbar - not sure if it's a great idea.
25497         //this.editor.contentWindow.focus();
25498         if (typeof sel != "undefined") {
25499             try {
25500                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25501             } catch(e) {
25502                 return this.doc.createRange();
25503             }
25504         } else {
25505             return this.doc.createRange();
25506         }
25507     },
25508     getParentElement: function()
25509     {
25510         
25511         this.assignDocWin();
25512         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25513         
25514         var range = this.createRange(sel);
25515          
25516         try {
25517             var p = range.commonAncestorContainer;
25518             while (p.nodeType == 3) { // text node
25519                 p = p.parentNode;
25520             }
25521             return p;
25522         } catch (e) {
25523             return null;
25524         }
25525     
25526     },
25527     /***
25528      *
25529      * Range intersection.. the hard stuff...
25530      *  '-1' = before
25531      *  '0' = hits..
25532      *  '1' = after.
25533      *         [ -- selected range --- ]
25534      *   [fail]                        [fail]
25535      *
25536      *    basically..
25537      *      if end is before start or  hits it. fail.
25538      *      if start is after end or hits it fail.
25539      *
25540      *   if either hits (but other is outside. - then it's not 
25541      *   
25542      *    
25543      **/
25544     
25545     
25546     // @see http://www.thismuchiknow.co.uk/?p=64.
25547     rangeIntersectsNode : function(range, node)
25548     {
25549         var nodeRange = node.ownerDocument.createRange();
25550         try {
25551             nodeRange.selectNode(node);
25552         } catch (e) {
25553             nodeRange.selectNodeContents(node);
25554         }
25555     
25556         var rangeStartRange = range.cloneRange();
25557         rangeStartRange.collapse(true);
25558     
25559         var rangeEndRange = range.cloneRange();
25560         rangeEndRange.collapse(false);
25561     
25562         var nodeStartRange = nodeRange.cloneRange();
25563         nodeStartRange.collapse(true);
25564     
25565         var nodeEndRange = nodeRange.cloneRange();
25566         nodeEndRange.collapse(false);
25567     
25568         return rangeStartRange.compareBoundaryPoints(
25569                  Range.START_TO_START, nodeEndRange) == -1 &&
25570                rangeEndRange.compareBoundaryPoints(
25571                  Range.START_TO_START, nodeStartRange) == 1;
25572         
25573          
25574     },
25575     rangeCompareNode : function(range, node)
25576     {
25577         var nodeRange = node.ownerDocument.createRange();
25578         try {
25579             nodeRange.selectNode(node);
25580         } catch (e) {
25581             nodeRange.selectNodeContents(node);
25582         }
25583         
25584         
25585         range.collapse(true);
25586     
25587         nodeRange.collapse(true);
25588      
25589         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25590         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25591          
25592         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25593         
25594         var nodeIsBefore   =  ss == 1;
25595         var nodeIsAfter    = ee == -1;
25596         
25597         if (nodeIsBefore && nodeIsAfter)
25598             return 0; // outer
25599         if (!nodeIsBefore && nodeIsAfter)
25600             return 1; //right trailed.
25601         
25602         if (nodeIsBefore && !nodeIsAfter)
25603             return 2;  // left trailed.
25604         // fully contined.
25605         return 3;
25606     },
25607
25608     // private? - in a new class?
25609     cleanUpPaste :  function()
25610     {
25611         // cleans up the whole document..
25612         Roo.log('cleanuppaste');
25613         
25614         this.cleanUpChildren(this.doc.body);
25615         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25616         if (clean != this.doc.body.innerHTML) {
25617             this.doc.body.innerHTML = clean;
25618         }
25619         
25620     },
25621     
25622     cleanWordChars : function(input) {// change the chars to hex code
25623         var he = Roo.HtmlEditorCore;
25624         
25625         var output = input;
25626         Roo.each(he.swapCodes, function(sw) { 
25627             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25628             
25629             output = output.replace(swapper, sw[1]);
25630         });
25631         
25632         return output;
25633     },
25634     
25635     
25636     cleanUpChildren : function (n)
25637     {
25638         if (!n.childNodes.length) {
25639             return;
25640         }
25641         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25642            this.cleanUpChild(n.childNodes[i]);
25643         }
25644     },
25645     
25646     
25647         
25648     
25649     cleanUpChild : function (node)
25650     {
25651         var ed = this;
25652         //console.log(node);
25653         if (node.nodeName == "#text") {
25654             // clean up silly Windows -- stuff?
25655             return; 
25656         }
25657         if (node.nodeName == "#comment") {
25658             node.parentNode.removeChild(node);
25659             // clean up silly Windows -- stuff?
25660             return; 
25661         }
25662         var lcname = node.tagName.toLowerCase();
25663         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25664         // whitelist of tags..
25665         
25666         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25667             // remove node.
25668             node.parentNode.removeChild(node);
25669             return;
25670             
25671         }
25672         
25673         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25674         
25675         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25676         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25677         
25678         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25679         //    remove_keep_children = true;
25680         //}
25681         
25682         if (remove_keep_children) {
25683             this.cleanUpChildren(node);
25684             // inserts everything just before this node...
25685             while (node.childNodes.length) {
25686                 var cn = node.childNodes[0];
25687                 node.removeChild(cn);
25688                 node.parentNode.insertBefore(cn, node);
25689             }
25690             node.parentNode.removeChild(node);
25691             return;
25692         }
25693         
25694         if (!node.attributes || !node.attributes.length) {
25695             this.cleanUpChildren(node);
25696             return;
25697         }
25698         
25699         function cleanAttr(n,v)
25700         {
25701             
25702             if (v.match(/^\./) || v.match(/^\//)) {
25703                 return;
25704             }
25705             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25706                 return;
25707             }
25708             if (v.match(/^#/)) {
25709                 return;
25710             }
25711 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25712             node.removeAttribute(n);
25713             
25714         }
25715         
25716         var cwhite = this.cwhite;
25717         var cblack = this.cblack;
25718             
25719         function cleanStyle(n,v)
25720         {
25721             if (v.match(/expression/)) { //XSS?? should we even bother..
25722                 node.removeAttribute(n);
25723                 return;
25724             }
25725             
25726             var parts = v.split(/;/);
25727             var clean = [];
25728             
25729             Roo.each(parts, function(p) {
25730                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25731                 if (!p.length) {
25732                     return true;
25733                 }
25734                 var l = p.split(':').shift().replace(/\s+/g,'');
25735                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25736                 
25737                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25738 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25739                     //node.removeAttribute(n);
25740                     return true;
25741                 }
25742                 //Roo.log()
25743                 // only allow 'c whitelisted system attributes'
25744                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25745 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25746                     //node.removeAttribute(n);
25747                     return true;
25748                 }
25749                 
25750                 
25751                  
25752                 
25753                 clean.push(p);
25754                 return true;
25755             });
25756             if (clean.length) { 
25757                 node.setAttribute(n, clean.join(';'));
25758             } else {
25759                 node.removeAttribute(n);
25760             }
25761             
25762         }
25763         
25764         
25765         for (var i = node.attributes.length-1; i > -1 ; i--) {
25766             var a = node.attributes[i];
25767             //console.log(a);
25768             
25769             if (a.name.toLowerCase().substr(0,2)=='on')  {
25770                 node.removeAttribute(a.name);
25771                 continue;
25772             }
25773             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25774                 node.removeAttribute(a.name);
25775                 continue;
25776             }
25777             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25778                 cleanAttr(a.name,a.value); // fixme..
25779                 continue;
25780             }
25781             if (a.name == 'style') {
25782                 cleanStyle(a.name,a.value);
25783                 continue;
25784             }
25785             /// clean up MS crap..
25786             // tecnically this should be a list of valid class'es..
25787             
25788             
25789             if (a.name == 'class') {
25790                 if (a.value.match(/^Mso/)) {
25791                     node.className = '';
25792                 }
25793                 
25794                 if (a.value.match(/body/)) {
25795                     node.className = '';
25796                 }
25797                 continue;
25798             }
25799             
25800             // style cleanup!?
25801             // class cleanup?
25802             
25803         }
25804         
25805         
25806         this.cleanUpChildren(node);
25807         
25808         
25809     },
25810     
25811     /**
25812      * Clean up MS wordisms...
25813      */
25814     cleanWord : function(node)
25815     {
25816         
25817         
25818         if (!node) {
25819             this.cleanWord(this.doc.body);
25820             return;
25821         }
25822         if (node.nodeName == "#text") {
25823             // clean up silly Windows -- stuff?
25824             return; 
25825         }
25826         if (node.nodeName == "#comment") {
25827             node.parentNode.removeChild(node);
25828             // clean up silly Windows -- stuff?
25829             return; 
25830         }
25831         
25832         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25833             node.parentNode.removeChild(node);
25834             return;
25835         }
25836         
25837         // remove - but keep children..
25838         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
25839             while (node.childNodes.length) {
25840                 var cn = node.childNodes[0];
25841                 node.removeChild(cn);
25842                 node.parentNode.insertBefore(cn, node);
25843             }
25844             node.parentNode.removeChild(node);
25845             this.iterateChildren(node, this.cleanWord);
25846             return;
25847         }
25848         // clean styles
25849         if (node.className.length) {
25850             
25851             var cn = node.className.split(/\W+/);
25852             var cna = [];
25853             Roo.each(cn, function(cls) {
25854                 if (cls.match(/Mso[a-zA-Z]+/)) {
25855                     return;
25856                 }
25857                 cna.push(cls);
25858             });
25859             node.className = cna.length ? cna.join(' ') : '';
25860             if (!cna.length) {
25861                 node.removeAttribute("class");
25862             }
25863         }
25864         
25865         if (node.hasAttribute("lang")) {
25866             node.removeAttribute("lang");
25867         }
25868         
25869         if (node.hasAttribute("style")) {
25870             
25871             var styles = node.getAttribute("style").split(";");
25872             var nstyle = [];
25873             Roo.each(styles, function(s) {
25874                 if (!s.match(/:/)) {
25875                     return;
25876                 }
25877                 var kv = s.split(":");
25878                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25879                     return;
25880                 }
25881                 // what ever is left... we allow.
25882                 nstyle.push(s);
25883             });
25884             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25885             if (!nstyle.length) {
25886                 node.removeAttribute('style');
25887             }
25888         }
25889         this.iterateChildren(node, this.cleanWord);
25890         
25891         
25892         
25893     },
25894     /**
25895      * iterateChildren of a Node, calling fn each time, using this as the scole..
25896      * @param {DomNode} node node to iterate children of.
25897      * @param {Function} fn method of this class to call on each item.
25898      */
25899     iterateChildren : function(node, fn)
25900     {
25901         if (!node.childNodes.length) {
25902                 return;
25903         }
25904         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25905            fn.call(this, node.childNodes[i])
25906         }
25907     },
25908     
25909     
25910     /**
25911      * cleanTableWidths.
25912      *
25913      * Quite often pasting from word etc.. results in tables with column and widths.
25914      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25915      *
25916      */
25917     cleanTableWidths : function(node)
25918     {
25919          
25920          
25921         if (!node) {
25922             this.cleanTableWidths(this.doc.body);
25923             return;
25924         }
25925         
25926         // ignore list...
25927         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25928             return; 
25929         }
25930         Roo.log(node.tagName);
25931         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25932             this.iterateChildren(node, this.cleanTableWidths);
25933             return;
25934         }
25935         if (node.hasAttribute('width')) {
25936             node.removeAttribute('width');
25937         }
25938         
25939          
25940         if (node.hasAttribute("style")) {
25941             // pretty basic...
25942             
25943             var styles = node.getAttribute("style").split(";");
25944             var nstyle = [];
25945             Roo.each(styles, function(s) {
25946                 if (!s.match(/:/)) {
25947                     return;
25948                 }
25949                 var kv = s.split(":");
25950                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25951                     return;
25952                 }
25953                 // what ever is left... we allow.
25954                 nstyle.push(s);
25955             });
25956             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25957             if (!nstyle.length) {
25958                 node.removeAttribute('style');
25959             }
25960         }
25961         
25962         this.iterateChildren(node, this.cleanTableWidths);
25963         
25964         
25965     },
25966     
25967     
25968     
25969     
25970     domToHTML : function(currentElement, depth, nopadtext) {
25971         
25972         depth = depth || 0;
25973         nopadtext = nopadtext || false;
25974     
25975         if (!currentElement) {
25976             return this.domToHTML(this.doc.body);
25977         }
25978         
25979         //Roo.log(currentElement);
25980         var j;
25981         var allText = false;
25982         var nodeName = currentElement.nodeName;
25983         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25984         
25985         if  (nodeName == '#text') {
25986             
25987             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25988         }
25989         
25990         
25991         var ret = '';
25992         if (nodeName != 'BODY') {
25993              
25994             var i = 0;
25995             // Prints the node tagName, such as <A>, <IMG>, etc
25996             if (tagName) {
25997                 var attr = [];
25998                 for(i = 0; i < currentElement.attributes.length;i++) {
25999                     // quoting?
26000                     var aname = currentElement.attributes.item(i).name;
26001                     if (!currentElement.attributes.item(i).value.length) {
26002                         continue;
26003                     }
26004                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26005                 }
26006                 
26007                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26008             } 
26009             else {
26010                 
26011                 // eack
26012             }
26013         } else {
26014             tagName = false;
26015         }
26016         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26017             return ret;
26018         }
26019         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26020             nopadtext = true;
26021         }
26022         
26023         
26024         // Traverse the tree
26025         i = 0;
26026         var currentElementChild = currentElement.childNodes.item(i);
26027         var allText = true;
26028         var innerHTML  = '';
26029         lastnode = '';
26030         while (currentElementChild) {
26031             // Formatting code (indent the tree so it looks nice on the screen)
26032             var nopad = nopadtext;
26033             if (lastnode == 'SPAN') {
26034                 nopad  = true;
26035             }
26036             // text
26037             if  (currentElementChild.nodeName == '#text') {
26038                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26039                 toadd = nopadtext ? toadd : toadd.trim();
26040                 if (!nopad && toadd.length > 80) {
26041                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26042                 }
26043                 innerHTML  += toadd;
26044                 
26045                 i++;
26046                 currentElementChild = currentElement.childNodes.item(i);
26047                 lastNode = '';
26048                 continue;
26049             }
26050             allText = false;
26051             
26052             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26053                 
26054             // Recursively traverse the tree structure of the child node
26055             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26056             lastnode = currentElementChild.nodeName;
26057             i++;
26058             currentElementChild=currentElement.childNodes.item(i);
26059         }
26060         
26061         ret += innerHTML;
26062         
26063         if (!allText) {
26064                 // The remaining code is mostly for formatting the tree
26065             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26066         }
26067         
26068         
26069         if (tagName) {
26070             ret+= "</"+tagName+">";
26071         }
26072         return ret;
26073         
26074     },
26075         
26076     applyBlacklists : function()
26077     {
26078         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26079         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26080         
26081         this.white = [];
26082         this.black = [];
26083         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26084             if (b.indexOf(tag) > -1) {
26085                 return;
26086             }
26087             this.white.push(tag);
26088             
26089         }, this);
26090         
26091         Roo.each(w, function(tag) {
26092             if (b.indexOf(tag) > -1) {
26093                 return;
26094             }
26095             if (this.white.indexOf(tag) > -1) {
26096                 return;
26097             }
26098             this.white.push(tag);
26099             
26100         }, this);
26101         
26102         
26103         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26104             if (w.indexOf(tag) > -1) {
26105                 return;
26106             }
26107             this.black.push(tag);
26108             
26109         }, this);
26110         
26111         Roo.each(b, function(tag) {
26112             if (w.indexOf(tag) > -1) {
26113                 return;
26114             }
26115             if (this.black.indexOf(tag) > -1) {
26116                 return;
26117             }
26118             this.black.push(tag);
26119             
26120         }, this);
26121         
26122         
26123         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26124         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26125         
26126         this.cwhite = [];
26127         this.cblack = [];
26128         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26129             if (b.indexOf(tag) > -1) {
26130                 return;
26131             }
26132             this.cwhite.push(tag);
26133             
26134         }, this);
26135         
26136         Roo.each(w, function(tag) {
26137             if (b.indexOf(tag) > -1) {
26138                 return;
26139             }
26140             if (this.cwhite.indexOf(tag) > -1) {
26141                 return;
26142             }
26143             this.cwhite.push(tag);
26144             
26145         }, this);
26146         
26147         
26148         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26149             if (w.indexOf(tag) > -1) {
26150                 return;
26151             }
26152             this.cblack.push(tag);
26153             
26154         }, this);
26155         
26156         Roo.each(b, function(tag) {
26157             if (w.indexOf(tag) > -1) {
26158                 return;
26159             }
26160             if (this.cblack.indexOf(tag) > -1) {
26161                 return;
26162             }
26163             this.cblack.push(tag);
26164             
26165         }, this);
26166     },
26167     
26168     setStylesheets : function(stylesheets)
26169     {
26170         if(typeof(stylesheets) == 'string'){
26171             Roo.get(this.iframe.contentDocument.head).createChild({
26172                 tag : 'link',
26173                 rel : 'stylesheet',
26174                 type : 'text/css',
26175                 href : stylesheets
26176             });
26177             
26178             return;
26179         }
26180         var _this = this;
26181      
26182         Roo.each(stylesheets, function(s) {
26183             if(!s.length){
26184                 return;
26185             }
26186             
26187             Roo.get(_this.iframe.contentDocument.head).createChild({
26188                 tag : 'link',
26189                 rel : 'stylesheet',
26190                 type : 'text/css',
26191                 href : s
26192             });
26193         });
26194
26195         
26196     },
26197     
26198     removeStylesheets : function()
26199     {
26200         var _this = this;
26201         
26202         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26203             s.remove();
26204         });
26205     }
26206     
26207     // hide stuff that is not compatible
26208     /**
26209      * @event blur
26210      * @hide
26211      */
26212     /**
26213      * @event change
26214      * @hide
26215      */
26216     /**
26217      * @event focus
26218      * @hide
26219      */
26220     /**
26221      * @event specialkey
26222      * @hide
26223      */
26224     /**
26225      * @cfg {String} fieldClass @hide
26226      */
26227     /**
26228      * @cfg {String} focusClass @hide
26229      */
26230     /**
26231      * @cfg {String} autoCreate @hide
26232      */
26233     /**
26234      * @cfg {String} inputType @hide
26235      */
26236     /**
26237      * @cfg {String} invalidClass @hide
26238      */
26239     /**
26240      * @cfg {String} invalidText @hide
26241      */
26242     /**
26243      * @cfg {String} msgFx @hide
26244      */
26245     /**
26246      * @cfg {String} validateOnBlur @hide
26247      */
26248 });
26249
26250 Roo.HtmlEditorCore.white = [
26251         'area', 'br', 'img', 'input', 'hr', 'wbr',
26252         
26253        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26254        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26255        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26256        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26257        'table',   'ul',         'xmp', 
26258        
26259        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26260       'thead',   'tr', 
26261      
26262       'dir', 'menu', 'ol', 'ul', 'dl',
26263        
26264       'embed',  'object'
26265 ];
26266
26267
26268 Roo.HtmlEditorCore.black = [
26269     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26270         'applet', // 
26271         'base',   'basefont', 'bgsound', 'blink',  'body', 
26272         'frame',  'frameset', 'head',    'html',   'ilayer', 
26273         'iframe', 'layer',  'link',     'meta',    'object',   
26274         'script', 'style' ,'title',  'xml' // clean later..
26275 ];
26276 Roo.HtmlEditorCore.clean = [
26277     'script', 'style', 'title', 'xml'
26278 ];
26279 Roo.HtmlEditorCore.remove = [
26280     'font'
26281 ];
26282 // attributes..
26283
26284 Roo.HtmlEditorCore.ablack = [
26285     'on'
26286 ];
26287     
26288 Roo.HtmlEditorCore.aclean = [ 
26289     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26290 ];
26291
26292 // protocols..
26293 Roo.HtmlEditorCore.pwhite= [
26294         'http',  'https',  'mailto'
26295 ];
26296
26297 // white listed style attributes.
26298 Roo.HtmlEditorCore.cwhite= [
26299       //  'text-align', /// default is to allow most things..
26300       
26301          
26302 //        'font-size'//??
26303 ];
26304
26305 // black listed style attributes.
26306 Roo.HtmlEditorCore.cblack= [
26307       //  'font-size' -- this can be set by the project 
26308 ];
26309
26310
26311 Roo.HtmlEditorCore.swapCodes   =[ 
26312     [    8211, "--" ], 
26313     [    8212, "--" ], 
26314     [    8216,  "'" ],  
26315     [    8217, "'" ],  
26316     [    8220, '"' ],  
26317     [    8221, '"' ],  
26318     [    8226, "*" ],  
26319     [    8230, "..." ]
26320 ]; 
26321
26322     //<script type="text/javascript">
26323
26324 /*
26325  * Ext JS Library 1.1.1
26326  * Copyright(c) 2006-2007, Ext JS, LLC.
26327  * Licence LGPL
26328  * 
26329  */
26330  
26331  
26332 Roo.form.HtmlEditor = function(config){
26333     
26334     
26335     
26336     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26337     
26338     if (!this.toolbars) {
26339         this.toolbars = [];
26340     }
26341     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26342     
26343     
26344 };
26345
26346 /**
26347  * @class Roo.form.HtmlEditor
26348  * @extends Roo.form.Field
26349  * Provides a lightweight HTML Editor component.
26350  *
26351  * This has been tested on Fireforx / Chrome.. IE may not be so great..
26352  * 
26353  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26354  * supported by this editor.</b><br/><br/>
26355  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26356  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26357  */
26358 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26359     /**
26360      * @cfg {Boolean} clearUp
26361      */
26362     clearUp : true,
26363       /**
26364      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26365      */
26366     toolbars : false,
26367    
26368      /**
26369      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26370      *                        Roo.resizable.
26371      */
26372     resizable : false,
26373      /**
26374      * @cfg {Number} height (in pixels)
26375      */   
26376     height: 300,
26377    /**
26378      * @cfg {Number} width (in pixels)
26379      */   
26380     width: 500,
26381     
26382     /**
26383      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26384      * 
26385      */
26386     stylesheets: false,
26387     
26388     
26389      /**
26390      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26391      * 
26392      */
26393     cblack: false,
26394     /**
26395      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26396      * 
26397      */
26398     cwhite: false,
26399     
26400      /**
26401      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26402      * 
26403      */
26404     black: false,
26405     /**
26406      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26407      * 
26408      */
26409     white: false,
26410     
26411     // id of frame..
26412     frameId: false,
26413     
26414     // private properties
26415     validationEvent : false,
26416     deferHeight: true,
26417     initialized : false,
26418     activated : false,
26419     
26420     onFocus : Roo.emptyFn,
26421     iframePad:3,
26422     hideMode:'offsets',
26423     
26424     actionMode : 'container', // defaults to hiding it...
26425     
26426     defaultAutoCreate : { // modified by initCompnoent..
26427         tag: "textarea",
26428         style:"width:500px;height:300px;",
26429         autocomplete: "new-password"
26430     },
26431
26432     // private
26433     initComponent : function(){
26434         this.addEvents({
26435             /**
26436              * @event initialize
26437              * Fires when the editor is fully initialized (including the iframe)
26438              * @param {HtmlEditor} this
26439              */
26440             initialize: true,
26441             /**
26442              * @event activate
26443              * Fires when the editor is first receives the focus. Any insertion must wait
26444              * until after this event.
26445              * @param {HtmlEditor} this
26446              */
26447             activate: true,
26448              /**
26449              * @event beforesync
26450              * Fires before the textarea is updated with content from the editor iframe. Return false
26451              * to cancel the sync.
26452              * @param {HtmlEditor} this
26453              * @param {String} html
26454              */
26455             beforesync: true,
26456              /**
26457              * @event beforepush
26458              * Fires before the iframe editor is updated with content from the textarea. Return false
26459              * to cancel the push.
26460              * @param {HtmlEditor} this
26461              * @param {String} html
26462              */
26463             beforepush: true,
26464              /**
26465              * @event sync
26466              * Fires when the textarea is updated with content from the editor iframe.
26467              * @param {HtmlEditor} this
26468              * @param {String} html
26469              */
26470             sync: true,
26471              /**
26472              * @event push
26473              * Fires when the iframe editor is updated with content from the textarea.
26474              * @param {HtmlEditor} this
26475              * @param {String} html
26476              */
26477             push: true,
26478              /**
26479              * @event editmodechange
26480              * Fires when the editor switches edit modes
26481              * @param {HtmlEditor} this
26482              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26483              */
26484             editmodechange: true,
26485             /**
26486              * @event editorevent
26487              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26488              * @param {HtmlEditor} this
26489              */
26490             editorevent: true,
26491             /**
26492              * @event firstfocus
26493              * Fires when on first focus - needed by toolbars..
26494              * @param {HtmlEditor} this
26495              */
26496             firstfocus: true,
26497             /**
26498              * @event autosave
26499              * Auto save the htmlEditor value as a file into Events
26500              * @param {HtmlEditor} this
26501              */
26502             autosave: true,
26503             /**
26504              * @event savedpreview
26505              * preview the saved version of htmlEditor
26506              * @param {HtmlEditor} this
26507              */
26508             savedpreview: true,
26509             
26510             /**
26511             * @event stylesheetsclick
26512             * Fires when press the Sytlesheets button
26513             * @param {Roo.HtmlEditorCore} this
26514             */
26515             stylesheetsclick: true
26516         });
26517         this.defaultAutoCreate =  {
26518             tag: "textarea",
26519             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26520             autocomplete: "new-password"
26521         };
26522     },
26523
26524     /**
26525      * Protected method that will not generally be called directly. It
26526      * is called when the editor creates its toolbar. Override this method if you need to
26527      * add custom toolbar buttons.
26528      * @param {HtmlEditor} editor
26529      */
26530     createToolbar : function(editor){
26531         Roo.log("create toolbars");
26532         if (!editor.toolbars || !editor.toolbars.length) {
26533             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26534         }
26535         
26536         for (var i =0 ; i < editor.toolbars.length;i++) {
26537             editor.toolbars[i] = Roo.factory(
26538                     typeof(editor.toolbars[i]) == 'string' ?
26539                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
26540                 Roo.form.HtmlEditor);
26541             editor.toolbars[i].init(editor);
26542         }
26543          
26544         
26545     },
26546
26547      
26548     // private
26549     onRender : function(ct, position)
26550     {
26551         var _t = this;
26552         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
26553         
26554         this.wrap = this.el.wrap({
26555             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26556         });
26557         
26558         this.editorcore.onRender(ct, position);
26559          
26560         if (this.resizable) {
26561             this.resizeEl = new Roo.Resizable(this.wrap, {
26562                 pinned : true,
26563                 wrap: true,
26564                 dynamic : true,
26565                 minHeight : this.height,
26566                 height: this.height,
26567                 handles : this.resizable,
26568                 width: this.width,
26569                 listeners : {
26570                     resize : function(r, w, h) {
26571                         _t.onResize(w,h); // -something
26572                     }
26573                 }
26574             });
26575             
26576         }
26577         this.createToolbar(this);
26578        
26579         
26580         if(!this.width){
26581             this.setSize(this.wrap.getSize());
26582         }
26583         if (this.resizeEl) {
26584             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26585             // should trigger onReize..
26586         }
26587         
26588         this.keyNav = new Roo.KeyNav(this.el, {
26589             
26590             "tab" : function(e){
26591                 e.preventDefault();
26592                 
26593                 var value = this.getValue();
26594                 
26595                 var start = this.el.dom.selectionStart;
26596                 var end = this.el.dom.selectionEnd;
26597                 
26598                 if(!e.shiftKey){
26599                     
26600                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
26601                     this.el.dom.setSelectionRange(end + 1, end + 1);
26602                     return;
26603                 }
26604                 
26605                 var f = value.substring(0, start).split("\t");
26606                 
26607                 if(f.pop().length != 0){
26608                     return;
26609                 }
26610                 
26611                 this.setValue(f.join("\t") + value.substring(end));
26612                 this.el.dom.setSelectionRange(start - 1, start - 1);
26613                 
26614             },
26615             
26616             "home" : function(e){
26617                 e.preventDefault();
26618                 
26619                 var curr = this.el.dom.selectionStart;
26620                 var lines = this.getValue().split("\n");
26621                 
26622                 if(!lines.length){
26623                     return;
26624                 }
26625                 
26626                 if(e.ctrlKey){
26627                     this.el.dom.setSelectionRange(0, 0);
26628                     return;
26629                 }
26630                 
26631                 var pos = 0;
26632                 
26633                 for (var i = 0; i < lines.length;i++) {
26634                     pos += lines[i].length;
26635                     
26636                     if(i != 0){
26637                         pos += 1;
26638                     }
26639                     
26640                     if(pos < curr){
26641                         continue;
26642                     }
26643                     
26644                     pos -= lines[i].length;
26645                     
26646                     break;
26647                 }
26648                 
26649                 if(!e.shiftKey){
26650                     this.el.dom.setSelectionRange(pos, pos);
26651                     return;
26652                 }
26653                 
26654                 this.el.dom.selectionStart = pos;
26655                 this.el.dom.selectionEnd = curr;
26656             },
26657             
26658             "end" : function(e){
26659                 e.preventDefault();
26660                 
26661                 var curr = this.el.dom.selectionStart;
26662                 var lines = this.getValue().split("\n");
26663                 
26664                 if(!lines.length){
26665                     return;
26666                 }
26667                 
26668                 if(e.ctrlKey){
26669                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
26670                     return;
26671                 }
26672                 
26673                 var pos = 0;
26674                 
26675                 for (var i = 0; i < lines.length;i++) {
26676                     
26677                     pos += lines[i].length;
26678                     
26679                     if(i != 0){
26680                         pos += 1;
26681                     }
26682                     
26683                     if(pos < curr){
26684                         continue;
26685                     }
26686                     
26687                     break;
26688                 }
26689                 
26690                 if(!e.shiftKey){
26691                     this.el.dom.setSelectionRange(pos, pos);
26692                     return;
26693                 }
26694                 
26695                 this.el.dom.selectionStart = curr;
26696                 this.el.dom.selectionEnd = pos;
26697             },
26698
26699             scope : this,
26700
26701             doRelay : function(foo, bar, hname){
26702                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
26703             },
26704
26705             forceKeyDown: true
26706         });
26707         
26708 //        if(this.autosave && this.w){
26709 //            this.autoSaveFn = setInterval(this.autosave, 1000);
26710 //        }
26711     },
26712
26713     // private
26714     onResize : function(w, h)
26715     {
26716         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
26717         var ew = false;
26718         var eh = false;
26719         
26720         if(this.el ){
26721             if(typeof w == 'number'){
26722                 var aw = w - this.wrap.getFrameWidth('lr');
26723                 this.el.setWidth(this.adjustWidth('textarea', aw));
26724                 ew = aw;
26725             }
26726             if(typeof h == 'number'){
26727                 var tbh = 0;
26728                 for (var i =0; i < this.toolbars.length;i++) {
26729                     // fixme - ask toolbars for heights?
26730                     tbh += this.toolbars[i].tb.el.getHeight();
26731                     if (this.toolbars[i].footer) {
26732                         tbh += this.toolbars[i].footer.el.getHeight();
26733                     }
26734                 }
26735                 
26736                 
26737                 
26738                 
26739                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26740                 ah -= 5; // knock a few pixes off for look..
26741 //                Roo.log(ah);
26742                 this.el.setHeight(this.adjustWidth('textarea', ah));
26743                 var eh = ah;
26744             }
26745         }
26746         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26747         this.editorcore.onResize(ew,eh);
26748         
26749     },
26750
26751     /**
26752      * Toggles the editor between standard and source edit mode.
26753      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26754      */
26755     toggleSourceEdit : function(sourceEditMode)
26756     {
26757         this.editorcore.toggleSourceEdit(sourceEditMode);
26758         
26759         if(this.editorcore.sourceEditMode){
26760             Roo.log('editor - showing textarea');
26761             
26762 //            Roo.log('in');
26763 //            Roo.log(this.syncValue());
26764             this.editorcore.syncValue();
26765             this.el.removeClass('x-hidden');
26766             this.el.dom.removeAttribute('tabIndex');
26767             this.el.focus();
26768             
26769             for (var i = 0; i < this.toolbars.length; i++) {
26770                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26771                     this.toolbars[i].tb.hide();
26772                     this.toolbars[i].footer.hide();
26773                 }
26774             }
26775             
26776         }else{
26777             Roo.log('editor - hiding textarea');
26778 //            Roo.log('out')
26779 //            Roo.log(this.pushValue()); 
26780             this.editorcore.pushValue();
26781             
26782             this.el.addClass('x-hidden');
26783             this.el.dom.setAttribute('tabIndex', -1);
26784             
26785             for (var i = 0; i < this.toolbars.length; i++) {
26786                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
26787                     this.toolbars[i].tb.show();
26788                     this.toolbars[i].footer.show();
26789                 }
26790             }
26791             
26792             //this.deferFocus();
26793         }
26794         
26795         this.setSize(this.wrap.getSize());
26796         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
26797         
26798         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26799     },
26800  
26801     // private (for BoxComponent)
26802     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26803
26804     // private (for BoxComponent)
26805     getResizeEl : function(){
26806         return this.wrap;
26807     },
26808
26809     // private (for BoxComponent)
26810     getPositionEl : function(){
26811         return this.wrap;
26812     },
26813
26814     // private
26815     initEvents : function(){
26816         this.originalValue = this.getValue();
26817     },
26818
26819     /**
26820      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26821      * @method
26822      */
26823     markInvalid : Roo.emptyFn,
26824     /**
26825      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26826      * @method
26827      */
26828     clearInvalid : Roo.emptyFn,
26829
26830     setValue : function(v){
26831         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
26832         this.editorcore.pushValue();
26833     },
26834
26835      
26836     // private
26837     deferFocus : function(){
26838         this.focus.defer(10, this);
26839     },
26840
26841     // doc'ed in Field
26842     focus : function(){
26843         this.editorcore.focus();
26844         
26845     },
26846       
26847
26848     // private
26849     onDestroy : function(){
26850         
26851         
26852         
26853         if(this.rendered){
26854             
26855             for (var i =0; i < this.toolbars.length;i++) {
26856                 // fixme - ask toolbars for heights?
26857                 this.toolbars[i].onDestroy();
26858             }
26859             
26860             this.wrap.dom.innerHTML = '';
26861             this.wrap.remove();
26862         }
26863     },
26864
26865     // private
26866     onFirstFocus : function(){
26867         //Roo.log("onFirstFocus");
26868         this.editorcore.onFirstFocus();
26869          for (var i =0; i < this.toolbars.length;i++) {
26870             this.toolbars[i].onFirstFocus();
26871         }
26872         
26873     },
26874     
26875     // private
26876     syncValue : function()
26877     {
26878         this.editorcore.syncValue();
26879     },
26880     
26881     pushValue : function()
26882     {
26883         this.editorcore.pushValue();
26884     },
26885     
26886     setStylesheets : function(stylesheets)
26887     {
26888         this.editorcore.setStylesheets(stylesheets);
26889     },
26890     
26891     removeStylesheets : function()
26892     {
26893         this.editorcore.removeStylesheets();
26894     }
26895      
26896     
26897     // hide stuff that is not compatible
26898     /**
26899      * @event blur
26900      * @hide
26901      */
26902     /**
26903      * @event change
26904      * @hide
26905      */
26906     /**
26907      * @event focus
26908      * @hide
26909      */
26910     /**
26911      * @event specialkey
26912      * @hide
26913      */
26914     /**
26915      * @cfg {String} fieldClass @hide
26916      */
26917     /**
26918      * @cfg {String} focusClass @hide
26919      */
26920     /**
26921      * @cfg {String} autoCreate @hide
26922      */
26923     /**
26924      * @cfg {String} inputType @hide
26925      */
26926     /**
26927      * @cfg {String} invalidClass @hide
26928      */
26929     /**
26930      * @cfg {String} invalidText @hide
26931      */
26932     /**
26933      * @cfg {String} msgFx @hide
26934      */
26935     /**
26936      * @cfg {String} validateOnBlur @hide
26937      */
26938 });
26939  
26940     // <script type="text/javascript">
26941 /*
26942  * Based on
26943  * Ext JS Library 1.1.1
26944  * Copyright(c) 2006-2007, Ext JS, LLC.
26945  *  
26946  
26947  */
26948
26949 /**
26950  * @class Roo.form.HtmlEditorToolbar1
26951  * Basic Toolbar
26952  * 
26953  * Usage:
26954  *
26955  new Roo.form.HtmlEditor({
26956     ....
26957     toolbars : [
26958         new Roo.form.HtmlEditorToolbar1({
26959             disable : { fonts: 1 , format: 1, ..., ... , ...],
26960             btns : [ .... ]
26961         })
26962     }
26963      
26964  * 
26965  * @cfg {Object} disable List of elements to disable..
26966  * @cfg {Array} btns List of additional buttons.
26967  * 
26968  * 
26969  * NEEDS Extra CSS? 
26970  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26971  */
26972  
26973 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26974 {
26975     
26976     Roo.apply(this, config);
26977     
26978     // default disabled, based on 'good practice'..
26979     this.disable = this.disable || {};
26980     Roo.applyIf(this.disable, {
26981         fontSize : true,
26982         colors : true,
26983         specialElements : true
26984     });
26985     
26986     
26987     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26988     // dont call parent... till later.
26989 }
26990
26991 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26992     
26993     tb: false,
26994     
26995     rendered: false,
26996     
26997     editor : false,
26998     editorcore : false,
26999     /**
27000      * @cfg {Object} disable  List of toolbar elements to disable
27001          
27002      */
27003     disable : false,
27004     
27005     
27006      /**
27007      * @cfg {String} createLinkText The default text for the create link prompt
27008      */
27009     createLinkText : 'Please enter the URL for the link:',
27010     /**
27011      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27012      */
27013     defaultLinkValue : 'http:/'+'/',
27014    
27015     
27016       /**
27017      * @cfg {Array} fontFamilies An array of available font families
27018      */
27019     fontFamilies : [
27020         'Arial',
27021         'Courier New',
27022         'Tahoma',
27023         'Times New Roman',
27024         'Verdana'
27025     ],
27026     
27027     specialChars : [
27028            "&#169;",
27029           "&#174;",     
27030           "&#8482;",    
27031           "&#163;" ,    
27032          // "&#8212;",    
27033           "&#8230;",    
27034           "&#247;" ,    
27035         //  "&#225;" ,     ?? a acute?
27036            "&#8364;"    , //Euro
27037        //   "&#8220;"    ,
27038         //  "&#8221;"    ,
27039         //  "&#8226;"    ,
27040           "&#176;"  //   , // degrees
27041
27042          // "&#233;"     , // e ecute
27043          // "&#250;"     , // u ecute?
27044     ],
27045     
27046     specialElements : [
27047         {
27048             text: "Insert Table",
27049             xtype: 'MenuItem',
27050             xns : Roo.Menu,
27051             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
27052                 
27053         },
27054         {    
27055             text: "Insert Image",
27056             xtype: 'MenuItem',
27057             xns : Roo.Menu,
27058             ihtml : '<img src="about:blank"/>'
27059             
27060         }
27061         
27062          
27063     ],
27064     
27065     
27066     inputElements : [ 
27067             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
27068             "input:submit", "input:button", "select", "textarea", "label" ],
27069     formats : [
27070         ["p"] ,  
27071         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
27072         ["pre"],[ "code"], 
27073         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27074         ['div'],['span']
27075     ],
27076     
27077     cleanStyles : [
27078         "font-size"
27079     ],
27080      /**
27081      * @cfg {String} defaultFont default font to use.
27082      */
27083     defaultFont: 'tahoma',
27084    
27085     fontSelect : false,
27086     
27087     
27088     formatCombo : false,
27089     
27090     init : function(editor)
27091     {
27092         this.editor = editor;
27093         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27094         var editorcore = this.editorcore;
27095         
27096         var _t = this;
27097         
27098         var fid = editorcore.frameId;
27099         var etb = this;
27100         function btn(id, toggle, handler){
27101             var xid = fid + '-'+ id ;
27102             return {
27103                 id : xid,
27104                 cmd : id,
27105                 cls : 'x-btn-icon x-edit-'+id,
27106                 enableToggle:toggle !== false,
27107                 scope: _t, // was editor...
27108                 handler:handler||_t.relayBtnCmd,
27109                 clickEvent:'mousedown',
27110                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27111                 tabIndex:-1
27112             };
27113         }
27114         
27115         
27116         
27117         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27118         this.tb = tb;
27119          // stop form submits
27120         tb.el.on('click', function(e){
27121             e.preventDefault(); // what does this do?
27122         });
27123
27124         if(!this.disable.font) { // && !Roo.isSafari){
27125             /* why no safari for fonts 
27126             editor.fontSelect = tb.el.createChild({
27127                 tag:'select',
27128                 tabIndex: -1,
27129                 cls:'x-font-select',
27130                 html: this.createFontOptions()
27131             });
27132             
27133             editor.fontSelect.on('change', function(){
27134                 var font = editor.fontSelect.dom.value;
27135                 editor.relayCmd('fontname', font);
27136                 editor.deferFocus();
27137             }, editor);
27138             
27139             tb.add(
27140                 editor.fontSelect.dom,
27141                 '-'
27142             );
27143             */
27144             
27145         };
27146         if(!this.disable.formats){
27147             this.formatCombo = new Roo.form.ComboBox({
27148                 store: new Roo.data.SimpleStore({
27149                     id : 'tag',
27150                     fields: ['tag'],
27151                     data : this.formats // from states.js
27152                 }),
27153                 blockFocus : true,
27154                 name : '',
27155                 //autoCreate : {tag: "div",  size: "20"},
27156                 displayField:'tag',
27157                 typeAhead: false,
27158                 mode: 'local',
27159                 editable : false,
27160                 triggerAction: 'all',
27161                 emptyText:'Add tag',
27162                 selectOnFocus:true,
27163                 width:135,
27164                 listeners : {
27165                     'select': function(c, r, i) {
27166                         editorcore.insertTag(r.get('tag'));
27167                         editor.focus();
27168                     }
27169                 }
27170
27171             });
27172             tb.addField(this.formatCombo);
27173             
27174         }
27175         
27176         if(!this.disable.format){
27177             tb.add(
27178                 btn('bold'),
27179                 btn('italic'),
27180                 btn('underline')
27181             );
27182         };
27183         if(!this.disable.fontSize){
27184             tb.add(
27185                 '-',
27186                 
27187                 
27188                 btn('increasefontsize', false, editorcore.adjustFont),
27189                 btn('decreasefontsize', false, editorcore.adjustFont)
27190             );
27191         };
27192         
27193         
27194         if(!this.disable.colors){
27195             tb.add(
27196                 '-', {
27197                     id:editorcore.frameId +'-forecolor',
27198                     cls:'x-btn-icon x-edit-forecolor',
27199                     clickEvent:'mousedown',
27200                     tooltip: this.buttonTips['forecolor'] || undefined,
27201                     tabIndex:-1,
27202                     menu : new Roo.menu.ColorMenu({
27203                         allowReselect: true,
27204                         focus: Roo.emptyFn,
27205                         value:'000000',
27206                         plain:true,
27207                         selectHandler: function(cp, color){
27208                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27209                             editor.deferFocus();
27210                         },
27211                         scope: editorcore,
27212                         clickEvent:'mousedown'
27213                     })
27214                 }, {
27215                     id:editorcore.frameId +'backcolor',
27216                     cls:'x-btn-icon x-edit-backcolor',
27217                     clickEvent:'mousedown',
27218                     tooltip: this.buttonTips['backcolor'] || undefined,
27219                     tabIndex:-1,
27220                     menu : new Roo.menu.ColorMenu({
27221                         focus: Roo.emptyFn,
27222                         value:'FFFFFF',
27223                         plain:true,
27224                         allowReselect: true,
27225                         selectHandler: function(cp, color){
27226                             if(Roo.isGecko){
27227                                 editorcore.execCmd('useCSS', false);
27228                                 editorcore.execCmd('hilitecolor', color);
27229                                 editorcore.execCmd('useCSS', true);
27230                                 editor.deferFocus();
27231                             }else{
27232                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
27233                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
27234                                 editor.deferFocus();
27235                             }
27236                         },
27237                         scope:editorcore,
27238                         clickEvent:'mousedown'
27239                     })
27240                 }
27241             );
27242         };
27243         // now add all the items...
27244         
27245
27246         if(!this.disable.alignments){
27247             tb.add(
27248                 '-',
27249                 btn('justifyleft'),
27250                 btn('justifycenter'),
27251                 btn('justifyright')
27252             );
27253         };
27254
27255         //if(!Roo.isSafari){
27256             if(!this.disable.links){
27257                 tb.add(
27258                     '-',
27259                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
27260                 );
27261             };
27262
27263             if(!this.disable.lists){
27264                 tb.add(
27265                     '-',
27266                     btn('insertorderedlist'),
27267                     btn('insertunorderedlist')
27268                 );
27269             }
27270             if(!this.disable.sourceEdit){
27271                 tb.add(
27272                     '-',
27273                     btn('sourceedit', true, function(btn){
27274                         this.toggleSourceEdit(btn.pressed);
27275                     })
27276                 );
27277             }
27278         //}
27279         
27280         var smenu = { };
27281         // special menu.. - needs to be tidied up..
27282         if (!this.disable.special) {
27283             smenu = {
27284                 text: "&#169;",
27285                 cls: 'x-edit-none',
27286                 
27287                 menu : {
27288                     items : []
27289                 }
27290             };
27291             for (var i =0; i < this.specialChars.length; i++) {
27292                 smenu.menu.items.push({
27293                     
27294                     html: this.specialChars[i],
27295                     handler: function(a,b) {
27296                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27297                         //editor.insertAtCursor(a.html);
27298                         
27299                     },
27300                     tabIndex:-1
27301                 });
27302             }
27303             
27304             
27305             tb.add(smenu);
27306             
27307             
27308         }
27309         
27310         var cmenu = { };
27311         if (!this.disable.cleanStyles) {
27312             cmenu = {
27313                 cls: 'x-btn-icon x-btn-clear',
27314                 
27315                 menu : {
27316                     items : []
27317                 }
27318             };
27319             for (var i =0; i < this.cleanStyles.length; i++) {
27320                 cmenu.menu.items.push({
27321                     actiontype : this.cleanStyles[i],
27322                     html: 'Remove ' + this.cleanStyles[i],
27323                     handler: function(a,b) {
27324 //                        Roo.log(a);
27325 //                        Roo.log(b);
27326                         var c = Roo.get(editorcore.doc.body);
27327                         c.select('[style]').each(function(s) {
27328                             s.dom.style.removeProperty(a.actiontype);
27329                         });
27330                         editorcore.syncValue();
27331                     },
27332                     tabIndex:-1
27333                 });
27334             }
27335              cmenu.menu.items.push({
27336                 actiontype : 'tablewidths',
27337                 html: 'Remove Table Widths',
27338                 handler: function(a,b) {
27339                     editorcore.cleanTableWidths();
27340                     editorcore.syncValue();
27341                 },
27342                 tabIndex:-1
27343             });
27344             cmenu.menu.items.push({
27345                 actiontype : 'word',
27346                 html: 'Remove MS Word Formating',
27347                 handler: function(a,b) {
27348                     editorcore.cleanWord();
27349                     editorcore.syncValue();
27350                 },
27351                 tabIndex:-1
27352             });
27353             
27354             cmenu.menu.items.push({
27355                 actiontype : 'all',
27356                 html: 'Remove All Styles',
27357                 handler: function(a,b) {
27358                     
27359                     var c = Roo.get(editorcore.doc.body);
27360                     c.select('[style]').each(function(s) {
27361                         s.dom.removeAttribute('style');
27362                     });
27363                     editorcore.syncValue();
27364                 },
27365                 tabIndex:-1
27366             });
27367              cmenu.menu.items.push({
27368                 actiontype : 'tidy',
27369                 html: 'Tidy HTML Source',
27370                 handler: function(a,b) {
27371                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
27372                     editorcore.syncValue();
27373                 },
27374                 tabIndex:-1
27375             });
27376             
27377             
27378             tb.add(cmenu);
27379         }
27380          
27381         if (!this.disable.specialElements) {
27382             var semenu = {
27383                 text: "Other;",
27384                 cls: 'x-edit-none',
27385                 menu : {
27386                     items : []
27387                 }
27388             };
27389             for (var i =0; i < this.specialElements.length; i++) {
27390                 semenu.menu.items.push(
27391                     Roo.apply({ 
27392                         handler: function(a,b) {
27393                             editor.insertAtCursor(this.ihtml);
27394                         }
27395                     }, this.specialElements[i])
27396                 );
27397                     
27398             }
27399             
27400             tb.add(semenu);
27401             
27402             
27403         }
27404          
27405         
27406         if (this.btns) {
27407             for(var i =0; i< this.btns.length;i++) {
27408                 var b = Roo.factory(this.btns[i],Roo.form);
27409                 b.cls =  'x-edit-none';
27410                 
27411                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27412                     b.cls += ' x-init-enable';
27413                 }
27414                 
27415                 b.scope = editorcore;
27416                 tb.add(b);
27417             }
27418         
27419         }
27420         
27421         
27422         
27423         // disable everything...
27424         
27425         this.tb.items.each(function(item){
27426             
27427            if(
27428                 item.id != editorcore.frameId+ '-sourceedit' && 
27429                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27430             ){
27431                 
27432                 item.disable();
27433             }
27434         });
27435         this.rendered = true;
27436         
27437         // the all the btns;
27438         editor.on('editorevent', this.updateToolbar, this);
27439         // other toolbars need to implement this..
27440         //editor.on('editmodechange', this.updateToolbar, this);
27441     },
27442     
27443     
27444     relayBtnCmd : function(btn) {
27445         this.editorcore.relayCmd(btn.cmd);
27446     },
27447     // private used internally
27448     createLink : function(){
27449         Roo.log("create link?");
27450         var url = prompt(this.createLinkText, this.defaultLinkValue);
27451         if(url && url != 'http:/'+'/'){
27452             this.editorcore.relayCmd('createlink', url);
27453         }
27454     },
27455
27456     
27457     /**
27458      * Protected method that will not generally be called directly. It triggers
27459      * a toolbar update by reading the markup state of the current selection in the editor.
27460      */
27461     updateToolbar: function(){
27462
27463         if(!this.editorcore.activated){
27464             this.editor.onFirstFocus();
27465             return;
27466         }
27467
27468         var btns = this.tb.items.map, 
27469             doc = this.editorcore.doc,
27470             frameId = this.editorcore.frameId;
27471
27472         if(!this.disable.font && !Roo.isSafari){
27473             /*
27474             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27475             if(name != this.fontSelect.dom.value){
27476                 this.fontSelect.dom.value = name;
27477             }
27478             */
27479         }
27480         if(!this.disable.format){
27481             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27482             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27483             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27484         }
27485         if(!this.disable.alignments){
27486             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27487             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27488             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27489         }
27490         if(!Roo.isSafari && !this.disable.lists){
27491             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27492             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27493         }
27494         
27495         var ans = this.editorcore.getAllAncestors();
27496         if (this.formatCombo) {
27497             
27498             
27499             var store = this.formatCombo.store;
27500             this.formatCombo.setValue("");
27501             for (var i =0; i < ans.length;i++) {
27502                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27503                     // select it..
27504                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27505                     break;
27506                 }
27507             }
27508         }
27509         
27510         
27511         
27512         // hides menus... - so this cant be on a menu...
27513         Roo.menu.MenuMgr.hideAll();
27514
27515         //this.editorsyncValue();
27516     },
27517    
27518     
27519     createFontOptions : function(){
27520         var buf = [], fs = this.fontFamilies, ff, lc;
27521         
27522         
27523         
27524         for(var i = 0, len = fs.length; i< len; i++){
27525             ff = fs[i];
27526             lc = ff.toLowerCase();
27527             buf.push(
27528                 '<option value="',lc,'" style="font-family:',ff,';"',
27529                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27530                     ff,
27531                 '</option>'
27532             );
27533         }
27534         return buf.join('');
27535     },
27536     
27537     toggleSourceEdit : function(sourceEditMode){
27538         
27539         Roo.log("toolbar toogle");
27540         if(sourceEditMode === undefined){
27541             sourceEditMode = !this.sourceEditMode;
27542         }
27543         this.sourceEditMode = sourceEditMode === true;
27544         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
27545         // just toggle the button?
27546         if(btn.pressed !== this.sourceEditMode){
27547             btn.toggle(this.sourceEditMode);
27548             return;
27549         }
27550         
27551         if(sourceEditMode){
27552             Roo.log("disabling buttons");
27553             this.tb.items.each(function(item){
27554                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
27555                     item.disable();
27556                 }
27557             });
27558           
27559         }else{
27560             Roo.log("enabling buttons");
27561             if(this.editorcore.initialized){
27562                 this.tb.items.each(function(item){
27563                     item.enable();
27564                 });
27565             }
27566             
27567         }
27568         Roo.log("calling toggole on editor");
27569         // tell the editor that it's been pressed..
27570         this.editor.toggleSourceEdit(sourceEditMode);
27571        
27572     },
27573      /**
27574      * Object collection of toolbar tooltips for the buttons in the editor. The key
27575      * is the command id associated with that button and the value is a valid QuickTips object.
27576      * For example:
27577 <pre><code>
27578 {
27579     bold : {
27580         title: 'Bold (Ctrl+B)',
27581         text: 'Make the selected text bold.',
27582         cls: 'x-html-editor-tip'
27583     },
27584     italic : {
27585         title: 'Italic (Ctrl+I)',
27586         text: 'Make the selected text italic.',
27587         cls: 'x-html-editor-tip'
27588     },
27589     ...
27590 </code></pre>
27591     * @type Object
27592      */
27593     buttonTips : {
27594         bold : {
27595             title: 'Bold (Ctrl+B)',
27596             text: 'Make the selected text bold.',
27597             cls: 'x-html-editor-tip'
27598         },
27599         italic : {
27600             title: 'Italic (Ctrl+I)',
27601             text: 'Make the selected text italic.',
27602             cls: 'x-html-editor-tip'
27603         },
27604         underline : {
27605             title: 'Underline (Ctrl+U)',
27606             text: 'Underline the selected text.',
27607             cls: 'x-html-editor-tip'
27608         },
27609         increasefontsize : {
27610             title: 'Grow Text',
27611             text: 'Increase the font size.',
27612             cls: 'x-html-editor-tip'
27613         },
27614         decreasefontsize : {
27615             title: 'Shrink Text',
27616             text: 'Decrease the font size.',
27617             cls: 'x-html-editor-tip'
27618         },
27619         backcolor : {
27620             title: 'Text Highlight Color',
27621             text: 'Change the background color of the selected text.',
27622             cls: 'x-html-editor-tip'
27623         },
27624         forecolor : {
27625             title: 'Font Color',
27626             text: 'Change the color of the selected text.',
27627             cls: 'x-html-editor-tip'
27628         },
27629         justifyleft : {
27630             title: 'Align Text Left',
27631             text: 'Align text to the left.',
27632             cls: 'x-html-editor-tip'
27633         },
27634         justifycenter : {
27635             title: 'Center Text',
27636             text: 'Center text in the editor.',
27637             cls: 'x-html-editor-tip'
27638         },
27639         justifyright : {
27640             title: 'Align Text Right',
27641             text: 'Align text to the right.',
27642             cls: 'x-html-editor-tip'
27643         },
27644         insertunorderedlist : {
27645             title: 'Bullet List',
27646             text: 'Start a bulleted list.',
27647             cls: 'x-html-editor-tip'
27648         },
27649         insertorderedlist : {
27650             title: 'Numbered List',
27651             text: 'Start a numbered list.',
27652             cls: 'x-html-editor-tip'
27653         },
27654         createlink : {
27655             title: 'Hyperlink',
27656             text: 'Make the selected text a hyperlink.',
27657             cls: 'x-html-editor-tip'
27658         },
27659         sourceedit : {
27660             title: 'Source Edit',
27661             text: 'Switch to source editing mode.',
27662             cls: 'x-html-editor-tip'
27663         }
27664     },
27665     // private
27666     onDestroy : function(){
27667         if(this.rendered){
27668             
27669             this.tb.items.each(function(item){
27670                 if(item.menu){
27671                     item.menu.removeAll();
27672                     if(item.menu.el){
27673                         item.menu.el.destroy();
27674                     }
27675                 }
27676                 item.destroy();
27677             });
27678              
27679         }
27680     },
27681     onFirstFocus: function() {
27682         this.tb.items.each(function(item){
27683            item.enable();
27684         });
27685     }
27686 });
27687
27688
27689
27690
27691 // <script type="text/javascript">
27692 /*
27693  * Based on
27694  * Ext JS Library 1.1.1
27695  * Copyright(c) 2006-2007, Ext JS, LLC.
27696  *  
27697  
27698  */
27699
27700  
27701 /**
27702  * @class Roo.form.HtmlEditor.ToolbarContext
27703  * Context Toolbar
27704  * 
27705  * Usage:
27706  *
27707  new Roo.form.HtmlEditor({
27708     ....
27709     toolbars : [
27710         { xtype: 'ToolbarStandard', styles : {} }
27711         { xtype: 'ToolbarContext', disable : {} }
27712     ]
27713 })
27714
27715      
27716  * 
27717  * @config : {Object} disable List of elements to disable.. (not done yet.)
27718  * @config : {Object} styles  Map of styles available.
27719  * 
27720  */
27721
27722 Roo.form.HtmlEditor.ToolbarContext = function(config)
27723 {
27724     
27725     Roo.apply(this, config);
27726     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27727     // dont call parent... till later.
27728     this.styles = this.styles || {};
27729 }
27730
27731  
27732
27733 Roo.form.HtmlEditor.ToolbarContext.types = {
27734     'IMG' : {
27735         width : {
27736             title: "Width",
27737             width: 40
27738         },
27739         height:  {
27740             title: "Height",
27741             width: 40
27742         },
27743         align: {
27744             title: "Align",
27745             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27746             width : 80
27747             
27748         },
27749         border: {
27750             title: "Border",
27751             width: 40
27752         },
27753         alt: {
27754             title: "Alt",
27755             width: 120
27756         },
27757         src : {
27758             title: "Src",
27759             width: 220
27760         }
27761         
27762     },
27763     'A' : {
27764         name : {
27765             title: "Name",
27766             width: 50
27767         },
27768         target:  {
27769             title: "Target",
27770             width: 120
27771         },
27772         href:  {
27773             title: "Href",
27774             width: 220
27775         } // border?
27776         
27777     },
27778     'TABLE' : {
27779         rows : {
27780             title: "Rows",
27781             width: 20
27782         },
27783         cols : {
27784             title: "Cols",
27785             width: 20
27786         },
27787         width : {
27788             title: "Width",
27789             width: 40
27790         },
27791         height : {
27792             title: "Height",
27793             width: 40
27794         },
27795         border : {
27796             title: "Border",
27797             width: 20
27798         }
27799     },
27800     'TD' : {
27801         width : {
27802             title: "Width",
27803             width: 40
27804         },
27805         height : {
27806             title: "Height",
27807             width: 40
27808         },   
27809         align: {
27810             title: "Align",
27811             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27812             width: 80
27813         },
27814         valign: {
27815             title: "Valign",
27816             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27817             width: 80
27818         },
27819         colspan: {
27820             title: "Colspan",
27821             width: 20
27822             
27823         },
27824          'font-family'  : {
27825             title : "Font",
27826             style : 'fontFamily',
27827             displayField: 'display',
27828             optname : 'font-family',
27829             width: 140
27830         }
27831     },
27832     'INPUT' : {
27833         name : {
27834             title: "name",
27835             width: 120
27836         },
27837         value : {
27838             title: "Value",
27839             width: 120
27840         },
27841         width : {
27842             title: "Width",
27843             width: 40
27844         }
27845     },
27846     'LABEL' : {
27847         'for' : {
27848             title: "For",
27849             width: 120
27850         }
27851     },
27852     'TEXTAREA' : {
27853           name : {
27854             title: "name",
27855             width: 120
27856         },
27857         rows : {
27858             title: "Rows",
27859             width: 20
27860         },
27861         cols : {
27862             title: "Cols",
27863             width: 20
27864         }
27865     },
27866     'SELECT' : {
27867         name : {
27868             title: "name",
27869             width: 120
27870         },
27871         selectoptions : {
27872             title: "Options",
27873             width: 200
27874         }
27875     },
27876     
27877     // should we really allow this??
27878     // should this just be 
27879     'BODY' : {
27880         title : {
27881             title: "Title",
27882             width: 200,
27883             disabled : true
27884         }
27885     },
27886     'SPAN' : {
27887         'font-family'  : {
27888             title : "Font",
27889             style : 'fontFamily',
27890             displayField: 'display',
27891             optname : 'font-family',
27892             width: 140
27893         }
27894     },
27895     'DIV' : {
27896         'font-family'  : {
27897             title : "Font",
27898             style : 'fontFamily',
27899             displayField: 'display',
27900             optname : 'font-family',
27901             width: 140
27902         }
27903     },
27904      'P' : {
27905         'font-family'  : {
27906             title : "Font",
27907             style : 'fontFamily',
27908             displayField: 'display',
27909             optname : 'font-family',
27910             width: 140
27911         }
27912     },
27913     
27914     '*' : {
27915         // empty..
27916     }
27917
27918 };
27919
27920 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27921 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27922
27923 Roo.form.HtmlEditor.ToolbarContext.options = {
27924         'font-family'  : [ 
27925                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27926                 [ 'Courier New', 'Courier New'],
27927                 [ 'Tahoma', 'Tahoma'],
27928                 [ 'Times New Roman,serif', 'Times'],
27929                 [ 'Verdana','Verdana' ]
27930         ]
27931 };
27932
27933 // fixme - these need to be configurable..
27934  
27935
27936 Roo.form.HtmlEditor.ToolbarContext.types
27937
27938
27939 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27940     
27941     tb: false,
27942     
27943     rendered: false,
27944     
27945     editor : false,
27946     editorcore : false,
27947     /**
27948      * @cfg {Object} disable  List of toolbar elements to disable
27949          
27950      */
27951     disable : false,
27952     /**
27953      * @cfg {Object} styles List of styles 
27954      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27955      *
27956      * These must be defined in the page, so they get rendered correctly..
27957      * .headline { }
27958      * TD.underline { }
27959      * 
27960      */
27961     styles : false,
27962     
27963     options: false,
27964     
27965     toolbars : false,
27966     
27967     init : function(editor)
27968     {
27969         this.editor = editor;
27970         this.editorcore = editor.editorcore ? editor.editorcore : editor;
27971         var editorcore = this.editorcore;
27972         
27973         var fid = editorcore.frameId;
27974         var etb = this;
27975         function btn(id, toggle, handler){
27976             var xid = fid + '-'+ id ;
27977             return {
27978                 id : xid,
27979                 cmd : id,
27980                 cls : 'x-btn-icon x-edit-'+id,
27981                 enableToggle:toggle !== false,
27982                 scope: editorcore, // was editor...
27983                 handler:handler||editorcore.relayBtnCmd,
27984                 clickEvent:'mousedown',
27985                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27986                 tabIndex:-1
27987             };
27988         }
27989         // create a new element.
27990         var wdiv = editor.wrap.createChild({
27991                 tag: 'div'
27992             }, editor.wrap.dom.firstChild.nextSibling, true);
27993         
27994         // can we do this more than once??
27995         
27996          // stop form submits
27997       
27998  
27999         // disable everything...
28000         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28001         this.toolbars = {};
28002            
28003         for (var i in  ty) {
28004           
28005             this.toolbars[i] = this.buildToolbar(ty[i],i);
28006         }
28007         this.tb = this.toolbars.BODY;
28008         this.tb.el.show();
28009         this.buildFooter();
28010         this.footer.show();
28011         editor.on('hide', function( ) { this.footer.hide() }, this);
28012         editor.on('show', function( ) { this.footer.show() }, this);
28013         
28014          
28015         this.rendered = true;
28016         
28017         // the all the btns;
28018         editor.on('editorevent', this.updateToolbar, this);
28019         // other toolbars need to implement this..
28020         //editor.on('editmodechange', this.updateToolbar, this);
28021     },
28022     
28023     
28024     
28025     /**
28026      * Protected method that will not generally be called directly. It triggers
28027      * a toolbar update by reading the markup state of the current selection in the editor.
28028      *
28029      * Note you can force an update by calling on('editorevent', scope, false)
28030      */
28031     updateToolbar: function(editor,ev,sel){
28032
28033         //Roo.log(ev);
28034         // capture mouse up - this is handy for selecting images..
28035         // perhaps should go somewhere else...
28036         if(!this.editorcore.activated){
28037              this.editor.onFirstFocus();
28038             return;
28039         }
28040         
28041         
28042         
28043         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28044         // selectNode - might want to handle IE?
28045         if (ev &&
28046             (ev.type == 'mouseup' || ev.type == 'click' ) &&
28047             ev.target && ev.target.tagName == 'IMG') {
28048             // they have click on an image...
28049             // let's see if we can change the selection...
28050             sel = ev.target;
28051          
28052               var nodeRange = sel.ownerDocument.createRange();
28053             try {
28054                 nodeRange.selectNode(sel);
28055             } catch (e) {
28056                 nodeRange.selectNodeContents(sel);
28057             }
28058             //nodeRange.collapse(true);
28059             var s = this.editorcore.win.getSelection();
28060             s.removeAllRanges();
28061             s.addRange(nodeRange);
28062         }  
28063         
28064       
28065         var updateFooter = sel ? false : true;
28066         
28067         
28068         var ans = this.editorcore.getAllAncestors();
28069         
28070         // pick
28071         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28072         
28073         if (!sel) { 
28074             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
28075             sel = sel ? sel : this.editorcore.doc.body;
28076             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28077             
28078         }
28079         // pick a menu that exists..
28080         var tn = sel.tagName.toUpperCase();
28081         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
28082         
28083         tn = sel.tagName.toUpperCase();
28084         
28085         var lastSel = this.tb.selectedNode
28086         
28087         this.tb.selectedNode = sel;
28088         
28089         // if current menu does not match..
28090         
28091         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
28092                 
28093             this.tb.el.hide();
28094             ///console.log("show: " + tn);
28095             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28096             this.tb.el.show();
28097             // update name
28098             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
28099             
28100             
28101             // update attributes
28102             if (this.tb.fields) {
28103                 this.tb.fields.each(function(e) {
28104                     if (e.stylename) {
28105                         e.setValue(sel.style[e.stylename]);
28106                         return;
28107                     } 
28108                    e.setValue(sel.getAttribute(e.attrname));
28109                 });
28110             }
28111             
28112             var hasStyles = false;
28113             for(var i in this.styles) {
28114                 hasStyles = true;
28115                 break;
28116             }
28117             
28118             // update styles
28119             if (hasStyles) { 
28120                 var st = this.tb.fields.item(0);
28121                 
28122                 st.store.removeAll();
28123                
28124                 
28125                 var cn = sel.className.split(/\s+/);
28126                 
28127                 var avs = [];
28128                 if (this.styles['*']) {
28129                     
28130                     Roo.each(this.styles['*'], function(v) {
28131                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28132                     });
28133                 }
28134                 if (this.styles[tn]) { 
28135                     Roo.each(this.styles[tn], function(v) {
28136                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
28137                     });
28138                 }
28139                 
28140                 st.store.loadData(avs);
28141                 st.collapse();
28142                 st.setValue(cn);
28143             }
28144             // flag our selected Node.
28145             this.tb.selectedNode = sel;
28146            
28147            
28148             Roo.menu.MenuMgr.hideAll();
28149
28150         }
28151         
28152         if (!updateFooter) {
28153             //this.footDisp.dom.innerHTML = ''; 
28154             return;
28155         }
28156         // update the footer
28157         //
28158         var html = '';
28159         
28160         this.footerEls = ans.reverse();
28161         Roo.each(this.footerEls, function(a,i) {
28162             if (!a) { return; }
28163             html += html.length ? ' &gt; '  :  '';
28164             
28165             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28166             
28167         });
28168        
28169         // 
28170         var sz = this.footDisp.up('td').getSize();
28171         this.footDisp.dom.style.width = (sz.width -10) + 'px';
28172         this.footDisp.dom.style.marginLeft = '5px';
28173         
28174         this.footDisp.dom.style.overflow = 'hidden';
28175         
28176         this.footDisp.dom.innerHTML = html;
28177             
28178         //this.editorsyncValue();
28179     },
28180      
28181     
28182    
28183        
28184     // private
28185     onDestroy : function(){
28186         if(this.rendered){
28187             
28188             this.tb.items.each(function(item){
28189                 if(item.menu){
28190                     item.menu.removeAll();
28191                     if(item.menu.el){
28192                         item.menu.el.destroy();
28193                     }
28194                 }
28195                 item.destroy();
28196             });
28197              
28198         }
28199     },
28200     onFirstFocus: function() {
28201         // need to do this for all the toolbars..
28202         this.tb.items.each(function(item){
28203            item.enable();
28204         });
28205     },
28206     buildToolbar: function(tlist, nm)
28207     {
28208         var editor = this.editor;
28209         var editorcore = this.editorcore;
28210          // create a new element.
28211         var wdiv = editor.wrap.createChild({
28212                 tag: 'div'
28213             }, editor.wrap.dom.firstChild.nextSibling, true);
28214         
28215        
28216         var tb = new Roo.Toolbar(wdiv);
28217         // add the name..
28218         
28219         tb.add(nm+ ":&nbsp;");
28220         
28221         var styles = [];
28222         for(var i in this.styles) {
28223             styles.push(i);
28224         }
28225         
28226         // styles...
28227         if (styles && styles.length) {
28228             
28229             // this needs a multi-select checkbox...
28230             tb.addField( new Roo.form.ComboBox({
28231                 store: new Roo.data.SimpleStore({
28232                     id : 'val',
28233                     fields: ['val', 'selected'],
28234                     data : [] 
28235                 }),
28236                 name : '-roo-edit-className',
28237                 attrname : 'className',
28238                 displayField: 'val',
28239                 typeAhead: false,
28240                 mode: 'local',
28241                 editable : false,
28242                 triggerAction: 'all',
28243                 emptyText:'Select Style',
28244                 selectOnFocus:true,
28245                 width: 130,
28246                 listeners : {
28247                     'select': function(c, r, i) {
28248                         // initial support only for on class per el..
28249                         tb.selectedNode.className =  r ? r.get('val') : '';
28250                         editorcore.syncValue();
28251                     }
28252                 }
28253     
28254             }));
28255         }
28256         
28257         var tbc = Roo.form.HtmlEditor.ToolbarContext;
28258         var tbops = tbc.options;
28259         
28260         for (var i in tlist) {
28261             
28262             var item = tlist[i];
28263             tb.add(item.title + ":&nbsp;");
28264             
28265             
28266             //optname == used so you can configure the options available..
28267             var opts = item.opts ? item.opts : false;
28268             if (item.optname) {
28269                 opts = tbops[item.optname];
28270            
28271             }
28272             
28273             if (opts) {
28274                 // opts == pulldown..
28275                 tb.addField( new Roo.form.ComboBox({
28276                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28277                         id : 'val',
28278                         fields: ['val', 'display'],
28279                         data : opts  
28280                     }),
28281                     name : '-roo-edit-' + i,
28282                     attrname : i,
28283                     stylename : item.style ? item.style : false,
28284                     displayField: item.displayField ? item.displayField : 'val',
28285                     valueField :  'val',
28286                     typeAhead: false,
28287                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
28288                     editable : false,
28289                     triggerAction: 'all',
28290                     emptyText:'Select',
28291                     selectOnFocus:true,
28292                     width: item.width ? item.width  : 130,
28293                     listeners : {
28294                         'select': function(c, r, i) {
28295                             if (c.stylename) {
28296                                 tb.selectedNode.style[c.stylename] =  r.get('val');
28297                                 return;
28298                             }
28299                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28300                         }
28301                     }
28302
28303                 }));
28304                 continue;
28305                     
28306                  
28307                 
28308                 tb.addField( new Roo.form.TextField({
28309                     name: i,
28310                     width: 100,
28311                     //allowBlank:false,
28312                     value: ''
28313                 }));
28314                 continue;
28315             }
28316             tb.addField( new Roo.form.TextField({
28317                 name: '-roo-edit-' + i,
28318                 attrname : i,
28319                 
28320                 width: item.width,
28321                 //allowBlank:true,
28322                 value: '',
28323                 listeners: {
28324                     'change' : function(f, nv, ov) {
28325                         tb.selectedNode.setAttribute(f.attrname, nv);
28326                     }
28327                 }
28328             }));
28329              
28330         }
28331         
28332         var _this = this;
28333         
28334         if(nm == 'BODY'){
28335             tb.addSeparator();
28336         
28337             tb.addButton( {
28338                 text: 'Stylesheets',
28339
28340                 listeners : {
28341                     click : function ()
28342                     {
28343                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
28344                     }
28345                 }
28346             });
28347         }
28348         
28349         tb.addFill();
28350         tb.addButton( {
28351             text: 'Remove Tag',
28352     
28353             listeners : {
28354                 click : function ()
28355                 {
28356                     // remove
28357                     // undo does not work.
28358                      
28359                     var sn = tb.selectedNode;
28360                     
28361                     var pn = sn.parentNode;
28362                     
28363                     var stn =  sn.childNodes[0];
28364                     var en = sn.childNodes[sn.childNodes.length - 1 ];
28365                     while (sn.childNodes.length) {
28366                         var node = sn.childNodes[0];
28367                         sn.removeChild(node);
28368                         //Roo.log(node);
28369                         pn.insertBefore(node, sn);
28370                         
28371                     }
28372                     pn.removeChild(sn);
28373                     var range = editorcore.createRange();
28374         
28375                     range.setStart(stn,0);
28376                     range.setEnd(en,0); //????
28377                     //range.selectNode(sel);
28378                     
28379                     
28380                     var selection = editorcore.getSelection();
28381                     selection.removeAllRanges();
28382                     selection.addRange(range);
28383                     
28384                     
28385                     
28386                     //_this.updateToolbar(null, null, pn);
28387                     _this.updateToolbar(null, null, null);
28388                     _this.footDisp.dom.innerHTML = ''; 
28389                 }
28390             }
28391             
28392                     
28393                 
28394             
28395         });
28396         
28397         
28398         tb.el.on('click', function(e){
28399             e.preventDefault(); // what does this do?
28400         });
28401         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28402         tb.el.hide();
28403         tb.name = nm;
28404         // dont need to disable them... as they will get hidden
28405         return tb;
28406          
28407         
28408     },
28409     buildFooter : function()
28410     {
28411         
28412         var fel = this.editor.wrap.createChild();
28413         this.footer = new Roo.Toolbar(fel);
28414         // toolbar has scrolly on left / right?
28415         var footDisp= new Roo.Toolbar.Fill();
28416         var _t = this;
28417         this.footer.add(
28418             {
28419                 text : '&lt;',
28420                 xtype: 'Button',
28421                 handler : function() {
28422                     _t.footDisp.scrollTo('left',0,true)
28423                 }
28424             }
28425         );
28426         this.footer.add( footDisp );
28427         this.footer.add( 
28428             {
28429                 text : '&gt;',
28430                 xtype: 'Button',
28431                 handler : function() {
28432                     // no animation..
28433                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
28434                 }
28435             }
28436         );
28437         var fel = Roo.get(footDisp.el);
28438         fel.addClass('x-editor-context');
28439         this.footDispWrap = fel; 
28440         this.footDispWrap.overflow  = 'hidden';
28441         
28442         this.footDisp = fel.createChild();
28443         this.footDispWrap.on('click', this.onContextClick, this)
28444         
28445         
28446     },
28447     onContextClick : function (ev,dom)
28448     {
28449         ev.preventDefault();
28450         var  cn = dom.className;
28451         //Roo.log(cn);
28452         if (!cn.match(/x-ed-loc-/)) {
28453             return;
28454         }
28455         var n = cn.split('-').pop();
28456         var ans = this.footerEls;
28457         var sel = ans[n];
28458         
28459          // pick
28460         var range = this.editorcore.createRange();
28461         
28462         range.selectNodeContents(sel);
28463         //range.selectNode(sel);
28464         
28465         
28466         var selection = this.editorcore.getSelection();
28467         selection.removeAllRanges();
28468         selection.addRange(range);
28469         
28470         
28471         
28472         this.updateToolbar(null, null, sel);
28473         
28474         
28475     }
28476     
28477     
28478     
28479     
28480     
28481 });
28482
28483
28484
28485
28486
28487 /*
28488  * Based on:
28489  * Ext JS Library 1.1.1
28490  * Copyright(c) 2006-2007, Ext JS, LLC.
28491  *
28492  * Originally Released Under LGPL - original licence link has changed is not relivant.
28493  *
28494  * Fork - LGPL
28495  * <script type="text/javascript">
28496  */
28497  
28498 /**
28499  * @class Roo.form.BasicForm
28500  * @extends Roo.util.Observable
28501  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28502  * @constructor
28503  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28504  * @param {Object} config Configuration options
28505  */
28506 Roo.form.BasicForm = function(el, config){
28507     this.allItems = [];
28508     this.childForms = [];
28509     Roo.apply(this, config);
28510     /*
28511      * The Roo.form.Field items in this form.
28512      * @type MixedCollection
28513      */
28514      
28515      
28516     this.items = new Roo.util.MixedCollection(false, function(o){
28517         return o.id || (o.id = Roo.id());
28518     });
28519     this.addEvents({
28520         /**
28521          * @event beforeaction
28522          * Fires before any action is performed. Return false to cancel the action.
28523          * @param {Form} this
28524          * @param {Action} action The action to be performed
28525          */
28526         beforeaction: true,
28527         /**
28528          * @event actionfailed
28529          * Fires when an action fails.
28530          * @param {Form} this
28531          * @param {Action} action The action that failed
28532          */
28533         actionfailed : true,
28534         /**
28535          * @event actioncomplete
28536          * Fires when an action is completed.
28537          * @param {Form} this
28538          * @param {Action} action The action that completed
28539          */
28540         actioncomplete : true
28541     });
28542     if(el){
28543         this.initEl(el);
28544     }
28545     Roo.form.BasicForm.superclass.constructor.call(this);
28546 };
28547
28548 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28549     /**
28550      * @cfg {String} method
28551      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28552      */
28553     /**
28554      * @cfg {DataReader} reader
28555      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28556      * This is optional as there is built-in support for processing JSON.
28557      */
28558     /**
28559      * @cfg {DataReader} errorReader
28560      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28561      * This is completely optional as there is built-in support for processing JSON.
28562      */
28563     /**
28564      * @cfg {String} url
28565      * The URL to use for form actions if one isn't supplied in the action options.
28566      */
28567     /**
28568      * @cfg {Boolean} fileUpload
28569      * Set to true if this form is a file upload.
28570      */
28571      
28572     /**
28573      * @cfg {Object} baseParams
28574      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28575      */
28576      /**
28577      
28578     /**
28579      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28580      */
28581     timeout: 30,
28582
28583     // private
28584     activeAction : null,
28585
28586     /**
28587      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28588      * or setValues() data instead of when the form was first created.
28589      */
28590     trackResetOnLoad : false,
28591     
28592     
28593     /**
28594      * childForms - used for multi-tab forms
28595      * @type {Array}
28596      */
28597     childForms : false,
28598     
28599     /**
28600      * allItems - full list of fields.
28601      * @type {Array}
28602      */
28603     allItems : false,
28604     
28605     /**
28606      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28607      * element by passing it or its id or mask the form itself by passing in true.
28608      * @type Mixed
28609      */
28610     waitMsgTarget : false,
28611
28612     // private
28613     initEl : function(el){
28614         this.el = Roo.get(el);
28615         this.id = this.el.id || Roo.id();
28616         this.el.on('submit', this.onSubmit, this);
28617         this.el.addClass('x-form');
28618     },
28619
28620     // private
28621     onSubmit : function(e){
28622         e.stopEvent();
28623     },
28624
28625     /**
28626      * Returns true if client-side validation on the form is successful.
28627      * @return Boolean
28628      */
28629     isValid : function(){
28630         var valid = true;
28631         this.items.each(function(f){
28632            if(!f.validate()){
28633                valid = false;
28634            }
28635         });
28636         return valid;
28637     },
28638
28639     /**
28640      * Returns true if any fields in this form have changed since their original load.
28641      * @return Boolean
28642      */
28643     isDirty : function(){
28644         var dirty = false;
28645         this.items.each(function(f){
28646            if(f.isDirty()){
28647                dirty = true;
28648                return false;
28649            }
28650         });
28651         return dirty;
28652     },
28653
28654     /**
28655      * Performs a predefined action (submit or load) or custom actions you define on this form.
28656      * @param {String} actionName The name of the action type
28657      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28658      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28659      * accept other config options):
28660      * <pre>
28661 Property          Type             Description
28662 ----------------  ---------------  ----------------------------------------------------------------------------------
28663 url               String           The url for the action (defaults to the form's url)
28664 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28665 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28666 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28667                                    validate the form on the client (defaults to false)
28668      * </pre>
28669      * @return {BasicForm} this
28670      */
28671     doAction : function(action, options){
28672         if(typeof action == 'string'){
28673             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28674         }
28675         if(this.fireEvent('beforeaction', this, action) !== false){
28676             this.beforeAction(action);
28677             action.run.defer(100, action);
28678         }
28679         return this;
28680     },
28681
28682     /**
28683      * Shortcut to do a submit action.
28684      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28685      * @return {BasicForm} this
28686      */
28687     submit : function(options){
28688         this.doAction('submit', options);
28689         return this;
28690     },
28691
28692     /**
28693      * Shortcut to do a load action.
28694      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28695      * @return {BasicForm} this
28696      */
28697     load : function(options){
28698         this.doAction('load', options);
28699         return this;
28700     },
28701
28702     /**
28703      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28704      * @param {Record} record The record to edit
28705      * @return {BasicForm} this
28706      */
28707     updateRecord : function(record){
28708         record.beginEdit();
28709         var fs = record.fields;
28710         fs.each(function(f){
28711             var field = this.findField(f.name);
28712             if(field){
28713                 record.set(f.name, field.getValue());
28714             }
28715         }, this);
28716         record.endEdit();
28717         return this;
28718     },
28719
28720     /**
28721      * Loads an Roo.data.Record into this form.
28722      * @param {Record} record The record to load
28723      * @return {BasicForm} this
28724      */
28725     loadRecord : function(record){
28726         this.setValues(record.data);
28727         return this;
28728     },
28729
28730     // private
28731     beforeAction : function(action){
28732         var o = action.options;
28733         
28734        
28735         if(this.waitMsgTarget === true){
28736             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28737         }else if(this.waitMsgTarget){
28738             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28739             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28740         }else {
28741             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28742         }
28743          
28744     },
28745
28746     // private
28747     afterAction : function(action, success){
28748         this.activeAction = null;
28749         var o = action.options;
28750         
28751         if(this.waitMsgTarget === true){
28752             this.el.unmask();
28753         }else if(this.waitMsgTarget){
28754             this.waitMsgTarget.unmask();
28755         }else{
28756             Roo.MessageBox.updateProgress(1);
28757             Roo.MessageBox.hide();
28758         }
28759          
28760         if(success){
28761             if(o.reset){
28762                 this.reset();
28763             }
28764             Roo.callback(o.success, o.scope, [this, action]);
28765             this.fireEvent('actioncomplete', this, action);
28766             
28767         }else{
28768             
28769             // failure condition..
28770             // we have a scenario where updates need confirming.
28771             // eg. if a locking scenario exists..
28772             // we look for { errors : { needs_confirm : true }} in the response.
28773             if (
28774                 (typeof(action.result) != 'undefined')  &&
28775                 (typeof(action.result.errors) != 'undefined')  &&
28776                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28777            ){
28778                 var _t = this;
28779                 Roo.MessageBox.confirm(
28780                     "Change requires confirmation",
28781                     action.result.errorMsg,
28782                     function(r) {
28783                         if (r != 'yes') {
28784                             return;
28785                         }
28786                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28787                     }
28788                     
28789                 );
28790                 
28791                 
28792                 
28793                 return;
28794             }
28795             
28796             Roo.callback(o.failure, o.scope, [this, action]);
28797             // show an error message if no failed handler is set..
28798             if (!this.hasListener('actionfailed')) {
28799                 Roo.MessageBox.alert("Error",
28800                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28801                         action.result.errorMsg :
28802                         "Saving Failed, please check your entries or try again"
28803                 );
28804             }
28805             
28806             this.fireEvent('actionfailed', this, action);
28807         }
28808         
28809     },
28810
28811     /**
28812      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28813      * @param {String} id The value to search for
28814      * @return Field
28815      */
28816     findField : function(id){
28817         var field = this.items.get(id);
28818         if(!field){
28819             this.items.each(function(f){
28820                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28821                     field = f;
28822                     return false;
28823                 }
28824             });
28825         }
28826         return field || null;
28827     },
28828
28829     /**
28830      * Add a secondary form to this one, 
28831      * Used to provide tabbed forms. One form is primary, with hidden values 
28832      * which mirror the elements from the other forms.
28833      * 
28834      * @param {Roo.form.Form} form to add.
28835      * 
28836      */
28837     addForm : function(form)
28838     {
28839        
28840         if (this.childForms.indexOf(form) > -1) {
28841             // already added..
28842             return;
28843         }
28844         this.childForms.push(form);
28845         var n = '';
28846         Roo.each(form.allItems, function (fe) {
28847             
28848             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28849             if (this.findField(n)) { // already added..
28850                 return;
28851             }
28852             var add = new Roo.form.Hidden({
28853                 name : n
28854             });
28855             add.render(this.el);
28856             
28857             this.add( add );
28858         }, this);
28859         
28860     },
28861     /**
28862      * Mark fields in this form invalid in bulk.
28863      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28864      * @return {BasicForm} this
28865      */
28866     markInvalid : function(errors){
28867         if(errors instanceof Array){
28868             for(var i = 0, len = errors.length; i < len; i++){
28869                 var fieldError = errors[i];
28870                 var f = this.findField(fieldError.id);
28871                 if(f){
28872                     f.markInvalid(fieldError.msg);
28873                 }
28874             }
28875         }else{
28876             var field, id;
28877             for(id in errors){
28878                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28879                     field.markInvalid(errors[id]);
28880                 }
28881             }
28882         }
28883         Roo.each(this.childForms || [], function (f) {
28884             f.markInvalid(errors);
28885         });
28886         
28887         return this;
28888     },
28889
28890     /**
28891      * Set values for fields in this form in bulk.
28892      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28893      * @return {BasicForm} this
28894      */
28895     setValues : function(values){
28896         if(values instanceof Array){ // array of objects
28897             for(var i = 0, len = values.length; i < len; i++){
28898                 var v = values[i];
28899                 var f = this.findField(v.id);
28900                 if(f){
28901                     f.setValue(v.value);
28902                     if(this.trackResetOnLoad){
28903                         f.originalValue = f.getValue();
28904                     }
28905                 }
28906             }
28907         }else{ // object hash
28908             var field, id;
28909             for(id in values){
28910                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28911                     
28912                     if (field.setFromData && 
28913                         field.valueField && 
28914                         field.displayField &&
28915                         // combos' with local stores can 
28916                         // be queried via setValue()
28917                         // to set their value..
28918                         (field.store && !field.store.isLocal)
28919                         ) {
28920                         // it's a combo
28921                         var sd = { };
28922                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28923                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28924                         field.setFromData(sd);
28925                         
28926                     } else {
28927                         field.setValue(values[id]);
28928                     }
28929                     
28930                     
28931                     if(this.trackResetOnLoad){
28932                         field.originalValue = field.getValue();
28933                     }
28934                 }
28935             }
28936         }
28937          
28938         Roo.each(this.childForms || [], function (f) {
28939             f.setValues(values);
28940         });
28941                 
28942         return this;
28943     },
28944
28945     /**
28946      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28947      * they are returned as an array.
28948      * @param {Boolean} asString
28949      * @return {Object}
28950      */
28951     getValues : function(asString){
28952         if (this.childForms) {
28953             // copy values from the child forms
28954             Roo.each(this.childForms, function (f) {
28955                 this.setValues(f.getValues());
28956             }, this);
28957         }
28958         
28959         
28960         
28961         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28962         if(asString === true){
28963             return fs;
28964         }
28965         return Roo.urlDecode(fs);
28966     },
28967     
28968     /**
28969      * Returns the fields in this form as an object with key/value pairs. 
28970      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28971      * @return {Object}
28972      */
28973     getFieldValues : function(with_hidden)
28974     {
28975         if (this.childForms) {
28976             // copy values from the child forms
28977             // should this call getFieldValues - probably not as we do not currently copy
28978             // hidden fields when we generate..
28979             Roo.each(this.childForms, function (f) {
28980                 this.setValues(f.getValues());
28981             }, this);
28982         }
28983         
28984         var ret = {};
28985         this.items.each(function(f){
28986             if (!f.getName()) {
28987                 return;
28988             }
28989             var v = f.getValue();
28990             if (f.inputType =='radio') {
28991                 if (typeof(ret[f.getName()]) == 'undefined') {
28992                     ret[f.getName()] = ''; // empty..
28993                 }
28994                 
28995                 if (!f.el.dom.checked) {
28996                     return;
28997                     
28998                 }
28999                 v = f.el.dom.value;
29000                 
29001             }
29002             
29003             // not sure if this supported any more..
29004             if ((typeof(v) == 'object') && f.getRawValue) {
29005                 v = f.getRawValue() ; // dates..
29006             }
29007             // combo boxes where name != hiddenName...
29008             if (f.name != f.getName()) {
29009                 ret[f.name] = f.getRawValue();
29010             }
29011             ret[f.getName()] = v;
29012         });
29013         
29014         return ret;
29015     },
29016
29017     /**
29018      * Clears all invalid messages in this form.
29019      * @return {BasicForm} this
29020      */
29021     clearInvalid : function(){
29022         this.items.each(function(f){
29023            f.clearInvalid();
29024         });
29025         
29026         Roo.each(this.childForms || [], function (f) {
29027             f.clearInvalid();
29028         });
29029         
29030         
29031         return this;
29032     },
29033
29034     /**
29035      * Resets this form.
29036      * @return {BasicForm} this
29037      */
29038     reset : function(){
29039         this.items.each(function(f){
29040             f.reset();
29041         });
29042         
29043         Roo.each(this.childForms || [], function (f) {
29044             f.reset();
29045         });
29046        
29047         
29048         return this;
29049     },
29050
29051     /**
29052      * Add Roo.form components to this form.
29053      * @param {Field} field1
29054      * @param {Field} field2 (optional)
29055      * @param {Field} etc (optional)
29056      * @return {BasicForm} this
29057      */
29058     add : function(){
29059         this.items.addAll(Array.prototype.slice.call(arguments, 0));
29060         return this;
29061     },
29062
29063
29064     /**
29065      * Removes a field from the items collection (does NOT remove its markup).
29066      * @param {Field} field
29067      * @return {BasicForm} this
29068      */
29069     remove : function(field){
29070         this.items.remove(field);
29071         return this;
29072     },
29073
29074     /**
29075      * Looks at the fields in this form, checks them for an id attribute,
29076      * and calls applyTo on the existing dom element with that id.
29077      * @return {BasicForm} this
29078      */
29079     render : function(){
29080         this.items.each(function(f){
29081             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29082                 f.applyTo(f.id);
29083             }
29084         });
29085         return this;
29086     },
29087
29088     /**
29089      * Calls {@link Ext#apply} for all fields in this form with the passed object.
29090      * @param {Object} values
29091      * @return {BasicForm} this
29092      */
29093     applyToFields : function(o){
29094         this.items.each(function(f){
29095            Roo.apply(f, o);
29096         });
29097         return this;
29098     },
29099
29100     /**
29101      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29102      * @param {Object} values
29103      * @return {BasicForm} this
29104      */
29105     applyIfToFields : function(o){
29106         this.items.each(function(f){
29107            Roo.applyIf(f, o);
29108         });
29109         return this;
29110     }
29111 });
29112
29113 // back compat
29114 Roo.BasicForm = Roo.form.BasicForm;/*
29115  * Based on:
29116  * Ext JS Library 1.1.1
29117  * Copyright(c) 2006-2007, Ext JS, LLC.
29118  *
29119  * Originally Released Under LGPL - original licence link has changed is not relivant.
29120  *
29121  * Fork - LGPL
29122  * <script type="text/javascript">
29123  */
29124
29125 /**
29126  * @class Roo.form.Form
29127  * @extends Roo.form.BasicForm
29128  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29129  * @constructor
29130  * @param {Object} config Configuration options
29131  */
29132 Roo.form.Form = function(config){
29133     var xitems =  [];
29134     if (config.items) {
29135         xitems = config.items;
29136         delete config.items;
29137     }
29138    
29139     
29140     Roo.form.Form.superclass.constructor.call(this, null, config);
29141     this.url = this.url || this.action;
29142     if(!this.root){
29143         this.root = new Roo.form.Layout(Roo.applyIf({
29144             id: Roo.id()
29145         }, config));
29146     }
29147     this.active = this.root;
29148     /**
29149      * Array of all the buttons that have been added to this form via {@link addButton}
29150      * @type Array
29151      */
29152     this.buttons = [];
29153     this.allItems = [];
29154     this.addEvents({
29155         /**
29156          * @event clientvalidation
29157          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
29158          * @param {Form} this
29159          * @param {Boolean} valid true if the form has passed client-side validation
29160          */
29161         clientvalidation: true,
29162         /**
29163          * @event rendered
29164          * Fires when the form is rendered
29165          * @param {Roo.form.Form} form
29166          */
29167         rendered : true
29168     });
29169     
29170     if (this.progressUrl) {
29171             // push a hidden field onto the list of fields..
29172             this.addxtype( {
29173                     xns: Roo.form, 
29174                     xtype : 'Hidden', 
29175                     name : 'UPLOAD_IDENTIFIER' 
29176             });
29177         }
29178         
29179     
29180     Roo.each(xitems, this.addxtype, this);
29181     
29182     
29183     
29184 };
29185
29186 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
29187     /**
29188      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
29189      */
29190     /**
29191      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
29192      */
29193     /**
29194      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
29195      */
29196     buttonAlign:'center',
29197
29198     /**
29199      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
29200      */
29201     minButtonWidth:75,
29202
29203     /**
29204      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
29205      * This property cascades to child containers if not set.
29206      */
29207     labelAlign:'left',
29208
29209     /**
29210      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
29211      * fires a looping event with that state. This is required to bind buttons to the valid
29212      * state using the config value formBind:true on the button.
29213      */
29214     monitorValid : false,
29215
29216     /**
29217      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
29218      */
29219     monitorPoll : 200,
29220     
29221     /**
29222      * @cfg {String} progressUrl - Url to return progress data 
29223      */
29224     
29225     progressUrl : false,
29226   
29227     /**
29228      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
29229      * fields are added and the column is closed. If no fields are passed the column remains open
29230      * until end() is called.
29231      * @param {Object} config The config to pass to the column
29232      * @param {Field} field1 (optional)
29233      * @param {Field} field2 (optional)
29234      * @param {Field} etc (optional)
29235      * @return Column The column container object
29236      */
29237     column : function(c){
29238         var col = new Roo.form.Column(c);
29239         this.start(col);
29240         if(arguments.length > 1){ // duplicate code required because of Opera
29241             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29242             this.end();
29243         }
29244         return col;
29245     },
29246
29247     /**
29248      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
29249      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
29250      * until end() is called.
29251      * @param {Object} config The config to pass to the fieldset
29252      * @param {Field} field1 (optional)
29253      * @param {Field} field2 (optional)
29254      * @param {Field} etc (optional)
29255      * @return FieldSet The fieldset container object
29256      */
29257     fieldset : function(c){
29258         var fs = new Roo.form.FieldSet(c);
29259         this.start(fs);
29260         if(arguments.length > 1){ // duplicate code required because of Opera
29261             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29262             this.end();
29263         }
29264         return fs;
29265     },
29266
29267     /**
29268      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
29269      * fields are added and the container is closed. If no fields are passed the container remains open
29270      * until end() is called.
29271      * @param {Object} config The config to pass to the Layout
29272      * @param {Field} field1 (optional)
29273      * @param {Field} field2 (optional)
29274      * @param {Field} etc (optional)
29275      * @return Layout The container object
29276      */
29277     container : function(c){
29278         var l = new Roo.form.Layout(c);
29279         this.start(l);
29280         if(arguments.length > 1){ // duplicate code required because of Opera
29281             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
29282             this.end();
29283         }
29284         return l;
29285     },
29286
29287     /**
29288      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
29289      * @param {Object} container A Roo.form.Layout or subclass of Layout
29290      * @return {Form} this
29291      */
29292     start : function(c){
29293         // cascade label info
29294         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
29295         this.active.stack.push(c);
29296         c.ownerCt = this.active;
29297         this.active = c;
29298         return this;
29299     },
29300
29301     /**
29302      * Closes the current open container
29303      * @return {Form} this
29304      */
29305     end : function(){
29306         if(this.active == this.root){
29307             return this;
29308         }
29309         this.active = this.active.ownerCt;
29310         return this;
29311     },
29312
29313     /**
29314      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
29315      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
29316      * as the label of the field.
29317      * @param {Field} field1
29318      * @param {Field} field2 (optional)
29319      * @param {Field} etc. (optional)
29320      * @return {Form} this
29321      */
29322     add : function(){
29323         this.active.stack.push.apply(this.active.stack, arguments);
29324         this.allItems.push.apply(this.allItems,arguments);
29325         var r = [];
29326         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
29327             if(a[i].isFormField){
29328                 r.push(a[i]);
29329             }
29330         }
29331         if(r.length > 0){
29332             Roo.form.Form.superclass.add.apply(this, r);
29333         }
29334         return this;
29335     },
29336     
29337
29338     
29339     
29340     
29341      /**
29342      * Find any element that has been added to a form, using it's ID or name
29343      * This can include framesets, columns etc. along with regular fields..
29344      * @param {String} id - id or name to find.
29345      
29346      * @return {Element} e - or false if nothing found.
29347      */
29348     findbyId : function(id)
29349     {
29350         var ret = false;
29351         if (!id) {
29352             return ret;
29353         }
29354         Roo.each(this.allItems, function(f){
29355             if (f.id == id || f.name == id ){
29356                 ret = f;
29357                 return false;
29358             }
29359         });
29360         return ret;
29361     },
29362
29363     
29364     
29365     /**
29366      * Render this form into the passed container. This should only be called once!
29367      * @param {String/HTMLElement/Element} container The element this component should be rendered into
29368      * @return {Form} this
29369      */
29370     render : function(ct)
29371     {
29372         
29373         
29374         
29375         ct = Roo.get(ct);
29376         var o = this.autoCreate || {
29377             tag: 'form',
29378             method : this.method || 'POST',
29379             id : this.id || Roo.id()
29380         };
29381         this.initEl(ct.createChild(o));
29382
29383         this.root.render(this.el);
29384         
29385        
29386              
29387         this.items.each(function(f){
29388             f.render('x-form-el-'+f.id);
29389         });
29390
29391         if(this.buttons.length > 0){
29392             // tables are required to maintain order and for correct IE layout
29393             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
29394                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
29395                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29396             }}, null, true);
29397             var tr = tb.getElementsByTagName('tr')[0];
29398             for(var i = 0, len = this.buttons.length; i < len; i++) {
29399                 var b = this.buttons[i];
29400                 var td = document.createElement('td');
29401                 td.className = 'x-form-btn-td';
29402                 b.render(tr.appendChild(td));
29403             }
29404         }
29405         if(this.monitorValid){ // initialize after render
29406             this.startMonitoring();
29407         }
29408         this.fireEvent('rendered', this);
29409         return this;
29410     },
29411
29412     /**
29413      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
29414      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29415      * object or a valid Roo.DomHelper element config
29416      * @param {Function} handler The function called when the button is clicked
29417      * @param {Object} scope (optional) The scope of the handler function
29418      * @return {Roo.Button}
29419      */
29420     addButton : function(config, handler, scope){
29421         var bc = {
29422             handler: handler,
29423             scope: scope,
29424             minWidth: this.minButtonWidth,
29425             hideParent:true
29426         };
29427         if(typeof config == "string"){
29428             bc.text = config;
29429         }else{
29430             Roo.apply(bc, config);
29431         }
29432         var btn = new Roo.Button(null, bc);
29433         this.buttons.push(btn);
29434         return btn;
29435     },
29436
29437      /**
29438      * Adds a series of form elements (using the xtype property as the factory method.
29439      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
29440      * @param {Object} config 
29441      */
29442     
29443     addxtype : function()
29444     {
29445         var ar = Array.prototype.slice.call(arguments, 0);
29446         var ret = false;
29447         for(var i = 0; i < ar.length; i++) {
29448             if (!ar[i]) {
29449                 continue; // skip -- if this happends something invalid got sent, we 
29450                 // should ignore it, as basically that interface element will not show up
29451                 // and that should be pretty obvious!!
29452             }
29453             
29454             if (Roo.form[ar[i].xtype]) {
29455                 ar[i].form = this;
29456                 var fe = Roo.factory(ar[i], Roo.form);
29457                 if (!ret) {
29458                     ret = fe;
29459                 }
29460                 fe.form = this;
29461                 if (fe.store) {
29462                     fe.store.form = this;
29463                 }
29464                 if (fe.isLayout) {  
29465                          
29466                     this.start(fe);
29467                     this.allItems.push(fe);
29468                     if (fe.items && fe.addxtype) {
29469                         fe.addxtype.apply(fe, fe.items);
29470                         delete fe.items;
29471                     }
29472                      this.end();
29473                     continue;
29474                 }
29475                 
29476                 
29477                  
29478                 this.add(fe);
29479               //  console.log('adding ' + ar[i].xtype);
29480             }
29481             if (ar[i].xtype == 'Button') {  
29482                 //console.log('adding button');
29483                 //console.log(ar[i]);
29484                 this.addButton(ar[i]);
29485                 this.allItems.push(fe);
29486                 continue;
29487             }
29488             
29489             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
29490                 alert('end is not supported on xtype any more, use items');
29491             //    this.end();
29492             //    //console.log('adding end');
29493             }
29494             
29495         }
29496         return ret;
29497     },
29498     
29499     /**
29500      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29501      * option "monitorValid"
29502      */
29503     startMonitoring : function(){
29504         if(!this.bound){
29505             this.bound = true;
29506             Roo.TaskMgr.start({
29507                 run : this.bindHandler,
29508                 interval : this.monitorPoll || 200,
29509                 scope: this
29510             });
29511         }
29512     },
29513
29514     /**
29515      * Stops monitoring of the valid state of this form
29516      */
29517     stopMonitoring : function(){
29518         this.bound = false;
29519     },
29520
29521     // private
29522     bindHandler : function(){
29523         if(!this.bound){
29524             return false; // stops binding
29525         }
29526         var valid = true;
29527         this.items.each(function(f){
29528             if(!f.isValid(true)){
29529                 valid = false;
29530                 return false;
29531             }
29532         });
29533         for(var i = 0, len = this.buttons.length; i < len; i++){
29534             var btn = this.buttons[i];
29535             if(btn.formBind === true && btn.disabled === valid){
29536                 btn.setDisabled(!valid);
29537             }
29538         }
29539         this.fireEvent('clientvalidation', this, valid);
29540     }
29541     
29542     
29543     
29544     
29545     
29546     
29547     
29548     
29549 });
29550
29551
29552 // back compat
29553 Roo.Form = Roo.form.Form;
29554 /*
29555  * Based on:
29556  * Ext JS Library 1.1.1
29557  * Copyright(c) 2006-2007, Ext JS, LLC.
29558  *
29559  * Originally Released Under LGPL - original licence link has changed is not relivant.
29560  *
29561  * Fork - LGPL
29562  * <script type="text/javascript">
29563  */
29564
29565 // as we use this in bootstrap.
29566 Roo.namespace('Roo.form');
29567  /**
29568  * @class Roo.form.Action
29569  * Internal Class used to handle form actions
29570  * @constructor
29571  * @param {Roo.form.BasicForm} el The form element or its id
29572  * @param {Object} config Configuration options
29573  */
29574
29575  
29576  
29577 // define the action interface
29578 Roo.form.Action = function(form, options){
29579     this.form = form;
29580     this.options = options || {};
29581 };
29582 /**
29583  * Client Validation Failed
29584  * @const 
29585  */
29586 Roo.form.Action.CLIENT_INVALID = 'client';
29587 /**
29588  * Server Validation Failed
29589  * @const 
29590  */
29591 Roo.form.Action.SERVER_INVALID = 'server';
29592  /**
29593  * Connect to Server Failed
29594  * @const 
29595  */
29596 Roo.form.Action.CONNECT_FAILURE = 'connect';
29597 /**
29598  * Reading Data from Server Failed
29599  * @const 
29600  */
29601 Roo.form.Action.LOAD_FAILURE = 'load';
29602
29603 Roo.form.Action.prototype = {
29604     type : 'default',
29605     failureType : undefined,
29606     response : undefined,
29607     result : undefined,
29608
29609     // interface method
29610     run : function(options){
29611
29612     },
29613
29614     // interface method
29615     success : function(response){
29616
29617     },
29618
29619     // interface method
29620     handleResponse : function(response){
29621
29622     },
29623
29624     // default connection failure
29625     failure : function(response){
29626         
29627         this.response = response;
29628         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29629         this.form.afterAction(this, false);
29630     },
29631
29632     processResponse : function(response){
29633         this.response = response;
29634         if(!response.responseText){
29635             return true;
29636         }
29637         this.result = this.handleResponse(response);
29638         return this.result;
29639     },
29640
29641     // utility functions used internally
29642     getUrl : function(appendParams){
29643         var url = this.options.url || this.form.url || this.form.el.dom.action;
29644         if(appendParams){
29645             var p = this.getParams();
29646             if(p){
29647                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29648             }
29649         }
29650         return url;
29651     },
29652
29653     getMethod : function(){
29654         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29655     },
29656
29657     getParams : function(){
29658         var bp = this.form.baseParams;
29659         var p = this.options.params;
29660         if(p){
29661             if(typeof p == "object"){
29662                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29663             }else if(typeof p == 'string' && bp){
29664                 p += '&' + Roo.urlEncode(bp);
29665             }
29666         }else if(bp){
29667             p = Roo.urlEncode(bp);
29668         }
29669         return p;
29670     },
29671
29672     createCallback : function(){
29673         return {
29674             success: this.success,
29675             failure: this.failure,
29676             scope: this,
29677             timeout: (this.form.timeout*1000),
29678             upload: this.form.fileUpload ? this.success : undefined
29679         };
29680     }
29681 };
29682
29683 Roo.form.Action.Submit = function(form, options){
29684     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29685 };
29686
29687 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29688     type : 'submit',
29689
29690     haveProgress : false,
29691     uploadComplete : false,
29692     
29693     // uploadProgress indicator.
29694     uploadProgress : function()
29695     {
29696         if (!this.form.progressUrl) {
29697             return;
29698         }
29699         
29700         if (!this.haveProgress) {
29701             Roo.MessageBox.progress("Uploading", "Uploading");
29702         }
29703         if (this.uploadComplete) {
29704            Roo.MessageBox.hide();
29705            return;
29706         }
29707         
29708         this.haveProgress = true;
29709    
29710         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29711         
29712         var c = new Roo.data.Connection();
29713         c.request({
29714             url : this.form.progressUrl,
29715             params: {
29716                 id : uid
29717             },
29718             method: 'GET',
29719             success : function(req){
29720                //console.log(data);
29721                 var rdata = false;
29722                 var edata;
29723                 try  {
29724                    rdata = Roo.decode(req.responseText)
29725                 } catch (e) {
29726                     Roo.log("Invalid data from server..");
29727                     Roo.log(edata);
29728                     return;
29729                 }
29730                 if (!rdata || !rdata.success) {
29731                     Roo.log(rdata);
29732                     Roo.MessageBox.alert(Roo.encode(rdata));
29733                     return;
29734                 }
29735                 var data = rdata.data;
29736                 
29737                 if (this.uploadComplete) {
29738                    Roo.MessageBox.hide();
29739                    return;
29740                 }
29741                    
29742                 if (data){
29743                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29744                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29745                     );
29746                 }
29747                 this.uploadProgress.defer(2000,this);
29748             },
29749        
29750             failure: function(data) {
29751                 Roo.log('progress url failed ');
29752                 Roo.log(data);
29753             },
29754             scope : this
29755         });
29756            
29757     },
29758     
29759     
29760     run : function()
29761     {
29762         // run get Values on the form, so it syncs any secondary forms.
29763         this.form.getValues();
29764         
29765         var o = this.options;
29766         var method = this.getMethod();
29767         var isPost = method == 'POST';
29768         if(o.clientValidation === false || this.form.isValid()){
29769             
29770             if (this.form.progressUrl) {
29771                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29772                     (new Date() * 1) + '' + Math.random());
29773                     
29774             } 
29775             
29776             
29777             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29778                 form:this.form.el.dom,
29779                 url:this.getUrl(!isPost),
29780                 method: method,
29781                 params:isPost ? this.getParams() : null,
29782                 isUpload: this.form.fileUpload
29783             }));
29784             
29785             this.uploadProgress();
29786
29787         }else if (o.clientValidation !== false){ // client validation failed
29788             this.failureType = Roo.form.Action.CLIENT_INVALID;
29789             this.form.afterAction(this, false);
29790         }
29791     },
29792
29793     success : function(response)
29794     {
29795         this.uploadComplete= true;
29796         if (this.haveProgress) {
29797             Roo.MessageBox.hide();
29798         }
29799         
29800         
29801         var result = this.processResponse(response);
29802         if(result === true || result.success){
29803             this.form.afterAction(this, true);
29804             return;
29805         }
29806         if(result.errors){
29807             this.form.markInvalid(result.errors);
29808             this.failureType = Roo.form.Action.SERVER_INVALID;
29809         }
29810         this.form.afterAction(this, false);
29811     },
29812     failure : function(response)
29813     {
29814         this.uploadComplete= true;
29815         if (this.haveProgress) {
29816             Roo.MessageBox.hide();
29817         }
29818         
29819         this.response = response;
29820         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29821         this.form.afterAction(this, false);
29822     },
29823     
29824     handleResponse : function(response){
29825         if(this.form.errorReader){
29826             var rs = this.form.errorReader.read(response);
29827             var errors = [];
29828             if(rs.records){
29829                 for(var i = 0, len = rs.records.length; i < len; i++) {
29830                     var r = rs.records[i];
29831                     errors[i] = r.data;
29832                 }
29833             }
29834             if(errors.length < 1){
29835                 errors = null;
29836             }
29837             return {
29838                 success : rs.success,
29839                 errors : errors
29840             };
29841         }
29842         var ret = false;
29843         try {
29844             ret = Roo.decode(response.responseText);
29845         } catch (e) {
29846             ret = {
29847                 success: false,
29848                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29849                 errors : []
29850             };
29851         }
29852         return ret;
29853         
29854     }
29855 });
29856
29857
29858 Roo.form.Action.Load = function(form, options){
29859     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29860     this.reader = this.form.reader;
29861 };
29862
29863 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29864     type : 'load',
29865
29866     run : function(){
29867         
29868         Roo.Ajax.request(Roo.apply(
29869                 this.createCallback(), {
29870                     method:this.getMethod(),
29871                     url:this.getUrl(false),
29872                     params:this.getParams()
29873         }));
29874     },
29875
29876     success : function(response){
29877         
29878         var result = this.processResponse(response);
29879         if(result === true || !result.success || !result.data){
29880             this.failureType = Roo.form.Action.LOAD_FAILURE;
29881             this.form.afterAction(this, false);
29882             return;
29883         }
29884         this.form.clearInvalid();
29885         this.form.setValues(result.data);
29886         this.form.afterAction(this, true);
29887     },
29888
29889     handleResponse : function(response){
29890         if(this.form.reader){
29891             var rs = this.form.reader.read(response);
29892             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29893             return {
29894                 success : rs.success,
29895                 data : data
29896             };
29897         }
29898         return Roo.decode(response.responseText);
29899     }
29900 });
29901
29902 Roo.form.Action.ACTION_TYPES = {
29903     'load' : Roo.form.Action.Load,
29904     'submit' : Roo.form.Action.Submit
29905 };/*
29906  * Based on:
29907  * Ext JS Library 1.1.1
29908  * Copyright(c) 2006-2007, Ext JS, LLC.
29909  *
29910  * Originally Released Under LGPL - original licence link has changed is not relivant.
29911  *
29912  * Fork - LGPL
29913  * <script type="text/javascript">
29914  */
29915  
29916 /**
29917  * @class Roo.form.Layout
29918  * @extends Roo.Component
29919  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29920  * @constructor
29921  * @param {Object} config Configuration options
29922  */
29923 Roo.form.Layout = function(config){
29924     var xitems = [];
29925     if (config.items) {
29926         xitems = config.items;
29927         delete config.items;
29928     }
29929     Roo.form.Layout.superclass.constructor.call(this, config);
29930     this.stack = [];
29931     Roo.each(xitems, this.addxtype, this);
29932      
29933 };
29934
29935 Roo.extend(Roo.form.Layout, Roo.Component, {
29936     /**
29937      * @cfg {String/Object} autoCreate
29938      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29939      */
29940     /**
29941      * @cfg {String/Object/Function} style
29942      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29943      * a function which returns such a specification.
29944      */
29945     /**
29946      * @cfg {String} labelAlign
29947      * Valid values are "left," "top" and "right" (defaults to "left")
29948      */
29949     /**
29950      * @cfg {Number} labelWidth
29951      * Fixed width in pixels of all field labels (defaults to undefined)
29952      */
29953     /**
29954      * @cfg {Boolean} clear
29955      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29956      */
29957     clear : true,
29958     /**
29959      * @cfg {String} labelSeparator
29960      * The separator to use after field labels (defaults to ':')
29961      */
29962     labelSeparator : ':',
29963     /**
29964      * @cfg {Boolean} hideLabels
29965      * True to suppress the display of field labels in this layout (defaults to false)
29966      */
29967     hideLabels : false,
29968
29969     // private
29970     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29971     
29972     isLayout : true,
29973     
29974     // private
29975     onRender : function(ct, position){
29976         if(this.el){ // from markup
29977             this.el = Roo.get(this.el);
29978         }else {  // generate
29979             var cfg = this.getAutoCreate();
29980             this.el = ct.createChild(cfg, position);
29981         }
29982         if(this.style){
29983             this.el.applyStyles(this.style);
29984         }
29985         if(this.labelAlign){
29986             this.el.addClass('x-form-label-'+this.labelAlign);
29987         }
29988         if(this.hideLabels){
29989             this.labelStyle = "display:none";
29990             this.elementStyle = "padding-left:0;";
29991         }else{
29992             if(typeof this.labelWidth == 'number'){
29993                 this.labelStyle = "width:"+this.labelWidth+"px;";
29994                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29995             }
29996             if(this.labelAlign == 'top'){
29997                 this.labelStyle = "width:auto;";
29998                 this.elementStyle = "padding-left:0;";
29999             }
30000         }
30001         var stack = this.stack;
30002         var slen = stack.length;
30003         if(slen > 0){
30004             if(!this.fieldTpl){
30005                 var t = new Roo.Template(
30006                     '<div class="x-form-item {5}">',
30007                         '<label for="{0}" style="{2}">{1}{4}</label>',
30008                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30009                         '</div>',
30010                     '</div><div class="x-form-clear-left"></div>'
30011                 );
30012                 t.disableFormats = true;
30013                 t.compile();
30014                 Roo.form.Layout.prototype.fieldTpl = t;
30015             }
30016             for(var i = 0; i < slen; i++) {
30017                 if(stack[i].isFormField){
30018                     this.renderField(stack[i]);
30019                 }else{
30020                     this.renderComponent(stack[i]);
30021                 }
30022             }
30023         }
30024         if(this.clear){
30025             this.el.createChild({cls:'x-form-clear'});
30026         }
30027     },
30028
30029     // private
30030     renderField : function(f){
30031         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30032                f.id, //0
30033                f.fieldLabel, //1
30034                f.labelStyle||this.labelStyle||'', //2
30035                this.elementStyle||'', //3
30036                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30037                f.itemCls||this.itemCls||''  //5
30038        ], true).getPrevSibling());
30039     },
30040
30041     // private
30042     renderComponent : function(c){
30043         c.render(c.isLayout ? this.el : this.el.createChild());    
30044     },
30045     /**
30046      * Adds a object form elements (using the xtype property as the factory method.)
30047      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
30048      * @param {Object} config 
30049      */
30050     addxtype : function(o)
30051     {
30052         // create the lement.
30053         o.form = this.form;
30054         var fe = Roo.factory(o, Roo.form);
30055         this.form.allItems.push(fe);
30056         this.stack.push(fe);
30057         
30058         if (fe.isFormField) {
30059             this.form.items.add(fe);
30060         }
30061          
30062         return fe;
30063     }
30064 });
30065
30066 /**
30067  * @class Roo.form.Column
30068  * @extends Roo.form.Layout
30069  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30070  * @constructor
30071  * @param {Object} config Configuration options
30072  */
30073 Roo.form.Column = function(config){
30074     Roo.form.Column.superclass.constructor.call(this, config);
30075 };
30076
30077 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30078     /**
30079      * @cfg {Number/String} width
30080      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30081      */
30082     /**
30083      * @cfg {String/Object} autoCreate
30084      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30085      */
30086
30087     // private
30088     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30089
30090     // private
30091     onRender : function(ct, position){
30092         Roo.form.Column.superclass.onRender.call(this, ct, position);
30093         if(this.width){
30094             this.el.setWidth(this.width);
30095         }
30096     }
30097 });
30098
30099
30100 /**
30101  * @class Roo.form.Row
30102  * @extends Roo.form.Layout
30103  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30104  * @constructor
30105  * @param {Object} config Configuration options
30106  */
30107
30108  
30109 Roo.form.Row = function(config){
30110     Roo.form.Row.superclass.constructor.call(this, config);
30111 };
30112  
30113 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30114       /**
30115      * @cfg {Number/String} width
30116      * The fixed width of the column in pixels or CSS value (defaults to "auto")
30117      */
30118     /**
30119      * @cfg {Number/String} height
30120      * The fixed height of the column in pixels or CSS value (defaults to "auto")
30121      */
30122     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30123     
30124     padWidth : 20,
30125     // private
30126     onRender : function(ct, position){
30127         //console.log('row render');
30128         if(!this.rowTpl){
30129             var t = new Roo.Template(
30130                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30131                     '<label for="{0}" style="{2}">{1}{4}</label>',
30132                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30133                     '</div>',
30134                 '</div>'
30135             );
30136             t.disableFormats = true;
30137             t.compile();
30138             Roo.form.Layout.prototype.rowTpl = t;
30139         }
30140         this.fieldTpl = this.rowTpl;
30141         
30142         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30143         var labelWidth = 100;
30144         
30145         if ((this.labelAlign != 'top')) {
30146             if (typeof this.labelWidth == 'number') {
30147                 labelWidth = this.labelWidth
30148             }
30149             this.padWidth =  20 + labelWidth;
30150             
30151         }
30152         
30153         Roo.form.Column.superclass.onRender.call(this, ct, position);
30154         if(this.width){
30155             this.el.setWidth(this.width);
30156         }
30157         if(this.height){
30158             this.el.setHeight(this.height);
30159         }
30160     },
30161     
30162     // private
30163     renderField : function(f){
30164         f.fieldEl = this.fieldTpl.append(this.el, [
30165                f.id, f.fieldLabel,
30166                f.labelStyle||this.labelStyle||'',
30167                this.elementStyle||'',
30168                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
30169                f.itemCls||this.itemCls||'',
30170                f.width ? f.width + this.padWidth : 160 + this.padWidth
30171        ],true);
30172     }
30173 });
30174  
30175
30176 /**
30177  * @class Roo.form.FieldSet
30178  * @extends Roo.form.Layout
30179  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
30180  * @constructor
30181  * @param {Object} config Configuration options
30182  */
30183 Roo.form.FieldSet = function(config){
30184     Roo.form.FieldSet.superclass.constructor.call(this, config);
30185 };
30186
30187 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
30188     /**
30189      * @cfg {String} legend
30190      * The text to display as the legend for the FieldSet (defaults to '')
30191      */
30192     /**
30193      * @cfg {String/Object} autoCreate
30194      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
30195      */
30196
30197     // private
30198     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
30199
30200     // private
30201     onRender : function(ct, position){
30202         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
30203         if(this.legend){
30204             this.setLegend(this.legend);
30205         }
30206     },
30207
30208     // private
30209     setLegend : function(text){
30210         if(this.rendered){
30211             this.el.child('legend').update(text);
30212         }
30213     }
30214 });/*
30215  * Based on:
30216  * Ext JS Library 1.1.1
30217  * Copyright(c) 2006-2007, Ext JS, LLC.
30218  *
30219  * Originally Released Under LGPL - original licence link has changed is not relivant.
30220  *
30221  * Fork - LGPL
30222  * <script type="text/javascript">
30223  */
30224 /**
30225  * @class Roo.form.VTypes
30226  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
30227  * @singleton
30228  */
30229 Roo.form.VTypes = function(){
30230     // closure these in so they are only created once.
30231     var alpha = /^[a-zA-Z_]+$/;
30232     var alphanum = /^[a-zA-Z0-9_]+$/;
30233     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
30234     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
30235
30236     // All these messages and functions are configurable
30237     return {
30238         /**
30239          * The function used to validate email addresses
30240          * @param {String} value The email address
30241          */
30242         'email' : function(v){
30243             return email.test(v);
30244         },
30245         /**
30246          * The error text to display when the email validation function returns false
30247          * @type String
30248          */
30249         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
30250         /**
30251          * The keystroke filter mask to be applied on email input
30252          * @type RegExp
30253          */
30254         'emailMask' : /[a-z0-9_\.\-@]/i,
30255
30256         /**
30257          * The function used to validate URLs
30258          * @param {String} value The URL
30259          */
30260         'url' : function(v){
30261             return url.test(v);
30262         },
30263         /**
30264          * The error text to display when the url validation function returns false
30265          * @type String
30266          */
30267         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
30268         
30269         /**
30270          * The function used to validate alpha values
30271          * @param {String} value The value
30272          */
30273         'alpha' : function(v){
30274             return alpha.test(v);
30275         },
30276         /**
30277          * The error text to display when the alpha validation function returns false
30278          * @type String
30279          */
30280         'alphaText' : 'This field should only contain letters and _',
30281         /**
30282          * The keystroke filter mask to be applied on alpha input
30283          * @type RegExp
30284          */
30285         'alphaMask' : /[a-z_]/i,
30286
30287         /**
30288          * The function used to validate alphanumeric values
30289          * @param {String} value The value
30290          */
30291         'alphanum' : function(v){
30292             return alphanum.test(v);
30293         },
30294         /**
30295          * The error text to display when the alphanumeric validation function returns false
30296          * @type String
30297          */
30298         'alphanumText' : 'This field should only contain letters, numbers and _',
30299         /**
30300          * The keystroke filter mask to be applied on alphanumeric input
30301          * @type RegExp
30302          */
30303         'alphanumMask' : /[a-z0-9_]/i
30304     };
30305 }();//<script type="text/javascript">
30306
30307 /**
30308  * @class Roo.form.FCKeditor
30309  * @extends Roo.form.TextArea
30310  * Wrapper around the FCKEditor http://www.fckeditor.net
30311  * @constructor
30312  * Creates a new FCKeditor
30313  * @param {Object} config Configuration options
30314  */
30315 Roo.form.FCKeditor = function(config){
30316     Roo.form.FCKeditor.superclass.constructor.call(this, config);
30317     this.addEvents({
30318          /**
30319          * @event editorinit
30320          * Fired when the editor is initialized - you can add extra handlers here..
30321          * @param {FCKeditor} this
30322          * @param {Object} the FCK object.
30323          */
30324         editorinit : true
30325     });
30326     
30327     
30328 };
30329 Roo.form.FCKeditor.editors = { };
30330 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
30331 {
30332     //defaultAutoCreate : {
30333     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
30334     //},
30335     // private
30336     /**
30337      * @cfg {Object} fck options - see fck manual for details.
30338      */
30339     fckconfig : false,
30340     
30341     /**
30342      * @cfg {Object} fck toolbar set (Basic or Default)
30343      */
30344     toolbarSet : 'Basic',
30345     /**
30346      * @cfg {Object} fck BasePath
30347      */ 
30348     basePath : '/fckeditor/',
30349     
30350     
30351     frame : false,
30352     
30353     value : '',
30354     
30355    
30356     onRender : function(ct, position)
30357     {
30358         if(!this.el){
30359             this.defaultAutoCreate = {
30360                 tag: "textarea",
30361                 style:"width:300px;height:60px;",
30362                 autocomplete: "new-password"
30363             };
30364         }
30365         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
30366         /*
30367         if(this.grow){
30368             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
30369             if(this.preventScrollbars){
30370                 this.el.setStyle("overflow", "hidden");
30371             }
30372             this.el.setHeight(this.growMin);
30373         }
30374         */
30375         //console.log('onrender' + this.getId() );
30376         Roo.form.FCKeditor.editors[this.getId()] = this;
30377          
30378
30379         this.replaceTextarea() ;
30380         
30381     },
30382     
30383     getEditor : function() {
30384         return this.fckEditor;
30385     },
30386     /**
30387      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
30388      * @param {Mixed} value The value to set
30389      */
30390     
30391     
30392     setValue : function(value)
30393     {
30394         //console.log('setValue: ' + value);
30395         
30396         if(typeof(value) == 'undefined') { // not sure why this is happending...
30397             return;
30398         }
30399         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30400         
30401         //if(!this.el || !this.getEditor()) {
30402         //    this.value = value;
30403             //this.setValue.defer(100,this,[value]);    
30404         //    return;
30405         //} 
30406         
30407         if(!this.getEditor()) {
30408             return;
30409         }
30410         
30411         this.getEditor().SetData(value);
30412         
30413         //
30414
30415     },
30416
30417     /**
30418      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
30419      * @return {Mixed} value The field value
30420      */
30421     getValue : function()
30422     {
30423         
30424         if (this.frame && this.frame.dom.style.display == 'none') {
30425             return Roo.form.FCKeditor.superclass.getValue.call(this);
30426         }
30427         
30428         if(!this.el || !this.getEditor()) {
30429            
30430            // this.getValue.defer(100,this); 
30431             return this.value;
30432         }
30433        
30434         
30435         var value=this.getEditor().GetData();
30436         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
30437         return Roo.form.FCKeditor.superclass.getValue.call(this);
30438         
30439
30440     },
30441
30442     /**
30443      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
30444      * @return {Mixed} value The field value
30445      */
30446     getRawValue : function()
30447     {
30448         if (this.frame && this.frame.dom.style.display == 'none') {
30449             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30450         }
30451         
30452         if(!this.el || !this.getEditor()) {
30453             //this.getRawValue.defer(100,this); 
30454             return this.value;
30455             return;
30456         }
30457         
30458         
30459         
30460         var value=this.getEditor().GetData();
30461         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
30462         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
30463          
30464     },
30465     
30466     setSize : function(w,h) {
30467         
30468         
30469         
30470         //if (this.frame && this.frame.dom.style.display == 'none') {
30471         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30472         //    return;
30473         //}
30474         //if(!this.el || !this.getEditor()) {
30475         //    this.setSize.defer(100,this, [w,h]); 
30476         //    return;
30477         //}
30478         
30479         
30480         
30481         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
30482         
30483         this.frame.dom.setAttribute('width', w);
30484         this.frame.dom.setAttribute('height', h);
30485         this.frame.setSize(w,h);
30486         
30487     },
30488     
30489     toggleSourceEdit : function(value) {
30490         
30491       
30492          
30493         this.el.dom.style.display = value ? '' : 'none';
30494         this.frame.dom.style.display = value ?  'none' : '';
30495         
30496     },
30497     
30498     
30499     focus: function(tag)
30500     {
30501         if (this.frame.dom.style.display == 'none') {
30502             return Roo.form.FCKeditor.superclass.focus.call(this);
30503         }
30504         if(!this.el || !this.getEditor()) {
30505             this.focus.defer(100,this, [tag]); 
30506             return;
30507         }
30508         
30509         
30510         
30511         
30512         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30513         this.getEditor().Focus();
30514         if (tgs.length) {
30515             if (!this.getEditor().Selection.GetSelection()) {
30516                 this.focus.defer(100,this, [tag]); 
30517                 return;
30518             }
30519             
30520             
30521             var r = this.getEditor().EditorDocument.createRange();
30522             r.setStart(tgs[0],0);
30523             r.setEnd(tgs[0],0);
30524             this.getEditor().Selection.GetSelection().removeAllRanges();
30525             this.getEditor().Selection.GetSelection().addRange(r);
30526             this.getEditor().Focus();
30527         }
30528         
30529     },
30530     
30531     
30532     
30533     replaceTextarea : function()
30534     {
30535         if ( document.getElementById( this.getId() + '___Frame' ) )
30536             return ;
30537         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30538         //{
30539             // We must check the elements firstly using the Id and then the name.
30540         var oTextarea = document.getElementById( this.getId() );
30541         
30542         var colElementsByName = document.getElementsByName( this.getId() ) ;
30543          
30544         oTextarea.style.display = 'none' ;
30545
30546         if ( oTextarea.tabIndex ) {            
30547             this.TabIndex = oTextarea.tabIndex ;
30548         }
30549         
30550         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30551         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30552         this.frame = Roo.get(this.getId() + '___Frame')
30553     },
30554     
30555     _getConfigHtml : function()
30556     {
30557         var sConfig = '' ;
30558
30559         for ( var o in this.fckconfig ) {
30560             sConfig += sConfig.length > 0  ? '&amp;' : '';
30561             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30562         }
30563
30564         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30565     },
30566     
30567     
30568     _getIFrameHtml : function()
30569     {
30570         var sFile = 'fckeditor.html' ;
30571         /* no idea what this is about..
30572         try
30573         {
30574             if ( (/fcksource=true/i).test( window.top.location.search ) )
30575                 sFile = 'fckeditor.original.html' ;
30576         }
30577         catch (e) { 
30578         */
30579
30580         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30581         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30582         
30583         
30584         var html = '<iframe id="' + this.getId() +
30585             '___Frame" src="' + sLink +
30586             '" width="' + this.width +
30587             '" height="' + this.height + '"' +
30588             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30589             ' frameborder="0" scrolling="no"></iframe>' ;
30590
30591         return html ;
30592     },
30593     
30594     _insertHtmlBefore : function( html, element )
30595     {
30596         if ( element.insertAdjacentHTML )       {
30597             // IE
30598             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30599         } else { // Gecko
30600             var oRange = document.createRange() ;
30601             oRange.setStartBefore( element ) ;
30602             var oFragment = oRange.createContextualFragment( html );
30603             element.parentNode.insertBefore( oFragment, element ) ;
30604         }
30605     }
30606     
30607     
30608   
30609     
30610     
30611     
30612     
30613
30614 });
30615
30616 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30617
30618 function FCKeditor_OnComplete(editorInstance){
30619     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30620     f.fckEditor = editorInstance;
30621     //console.log("loaded");
30622     f.fireEvent('editorinit', f, editorInstance);
30623
30624   
30625
30626  
30627
30628
30629
30630
30631
30632
30633
30634
30635
30636
30637
30638
30639
30640
30641
30642 //<script type="text/javascript">
30643 /**
30644  * @class Roo.form.GridField
30645  * @extends Roo.form.Field
30646  * Embed a grid (or editable grid into a form)
30647  * STATUS ALPHA
30648  * 
30649  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30650  * it needs 
30651  * xgrid.store = Roo.data.Store
30652  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30653  * xgrid.store.reader = Roo.data.JsonReader 
30654  * 
30655  * 
30656  * @constructor
30657  * Creates a new GridField
30658  * @param {Object} config Configuration options
30659  */
30660 Roo.form.GridField = function(config){
30661     Roo.form.GridField.superclass.constructor.call(this, config);
30662      
30663 };
30664
30665 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30666     /**
30667      * @cfg {Number} width  - used to restrict width of grid..
30668      */
30669     width : 100,
30670     /**
30671      * @cfg {Number} height - used to restrict height of grid..
30672      */
30673     height : 50,
30674      /**
30675      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30676          * 
30677          *}
30678      */
30679     xgrid : false, 
30680     /**
30681      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30682      * {tag: "input", type: "checkbox", autocomplete: "off"})
30683      */
30684    // defaultAutoCreate : { tag: 'div' },
30685     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
30686     /**
30687      * @cfg {String} addTitle Text to include for adding a title.
30688      */
30689     addTitle : false,
30690     //
30691     onResize : function(){
30692         Roo.form.Field.superclass.onResize.apply(this, arguments);
30693     },
30694
30695     initEvents : function(){
30696         // Roo.form.Checkbox.superclass.initEvents.call(this);
30697         // has no events...
30698        
30699     },
30700
30701
30702     getResizeEl : function(){
30703         return this.wrap;
30704     },
30705
30706     getPositionEl : function(){
30707         return this.wrap;
30708     },
30709
30710     // private
30711     onRender : function(ct, position){
30712         
30713         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30714         var style = this.style;
30715         delete this.style;
30716         
30717         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30718         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30719         this.viewEl = this.wrap.createChild({ tag: 'div' });
30720         if (style) {
30721             this.viewEl.applyStyles(style);
30722         }
30723         if (this.width) {
30724             this.viewEl.setWidth(this.width);
30725         }
30726         if (this.height) {
30727             this.viewEl.setHeight(this.height);
30728         }
30729         //if(this.inputValue !== undefined){
30730         //this.setValue(this.value);
30731         
30732         
30733         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30734         
30735         
30736         this.grid.render();
30737         this.grid.getDataSource().on('remove', this.refreshValue, this);
30738         this.grid.getDataSource().on('update', this.refreshValue, this);
30739         this.grid.on('afteredit', this.refreshValue, this);
30740  
30741     },
30742      
30743     
30744     /**
30745      * Sets the value of the item. 
30746      * @param {String} either an object  or a string..
30747      */
30748     setValue : function(v){
30749         //this.value = v;
30750         v = v || []; // empty set..
30751         // this does not seem smart - it really only affects memoryproxy grids..
30752         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30753             var ds = this.grid.getDataSource();
30754             // assumes a json reader..
30755             var data = {}
30756             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30757             ds.loadData( data);
30758         }
30759         // clear selection so it does not get stale.
30760         if (this.grid.sm) { 
30761             this.grid.sm.clearSelections();
30762         }
30763         
30764         Roo.form.GridField.superclass.setValue.call(this, v);
30765         this.refreshValue();
30766         // should load data in the grid really....
30767     },
30768     
30769     // private
30770     refreshValue: function() {
30771          var val = [];
30772         this.grid.getDataSource().each(function(r) {
30773             val.push(r.data);
30774         });
30775         this.el.dom.value = Roo.encode(val);
30776     }
30777     
30778      
30779     
30780     
30781 });/*
30782  * Based on:
30783  * Ext JS Library 1.1.1
30784  * Copyright(c) 2006-2007, Ext JS, LLC.
30785  *
30786  * Originally Released Under LGPL - original licence link has changed is not relivant.
30787  *
30788  * Fork - LGPL
30789  * <script type="text/javascript">
30790  */
30791 /**
30792  * @class Roo.form.DisplayField
30793  * @extends Roo.form.Field
30794  * A generic Field to display non-editable data.
30795  * @constructor
30796  * Creates a new Display Field item.
30797  * @param {Object} config Configuration options
30798  */
30799 Roo.form.DisplayField = function(config){
30800     Roo.form.DisplayField.superclass.constructor.call(this, config);
30801     
30802 };
30803
30804 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30805     inputType:      'hidden',
30806     allowBlank:     true,
30807     readOnly:         true,
30808     
30809  
30810     /**
30811      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30812      */
30813     focusClass : undefined,
30814     /**
30815      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30816      */
30817     fieldClass: 'x-form-field',
30818     
30819      /**
30820      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30821      */
30822     valueRenderer: undefined,
30823     
30824     width: 100,
30825     /**
30826      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30827      * {tag: "input", type: "checkbox", autocomplete: "off"})
30828      */
30829      
30830  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30831
30832     onResize : function(){
30833         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30834         
30835     },
30836
30837     initEvents : function(){
30838         // Roo.form.Checkbox.superclass.initEvents.call(this);
30839         // has no events...
30840        
30841     },
30842
30843
30844     getResizeEl : function(){
30845         return this.wrap;
30846     },
30847
30848     getPositionEl : function(){
30849         return this.wrap;
30850     },
30851
30852     // private
30853     onRender : function(ct, position){
30854         
30855         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30856         //if(this.inputValue !== undefined){
30857         this.wrap = this.el.wrap();
30858         
30859         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30860         
30861         if (this.bodyStyle) {
30862             this.viewEl.applyStyles(this.bodyStyle);
30863         }
30864         //this.viewEl.setStyle('padding', '2px');
30865         
30866         this.setValue(this.value);
30867         
30868     },
30869 /*
30870     // private
30871     initValue : Roo.emptyFn,
30872
30873   */
30874
30875         // private
30876     onClick : function(){
30877         
30878     },
30879
30880     /**
30881      * Sets the checked state of the checkbox.
30882      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30883      */
30884     setValue : function(v){
30885         this.value = v;
30886         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30887         // this might be called before we have a dom element..
30888         if (!this.viewEl) {
30889             return;
30890         }
30891         this.viewEl.dom.innerHTML = html;
30892         Roo.form.DisplayField.superclass.setValue.call(this, v);
30893
30894     }
30895 });/*
30896  * 
30897  * Licence- LGPL
30898  * 
30899  */
30900
30901 /**
30902  * @class Roo.form.DayPicker
30903  * @extends Roo.form.Field
30904  * A Day picker show [M] [T] [W] ....
30905  * @constructor
30906  * Creates a new Day Picker
30907  * @param {Object} config Configuration options
30908  */
30909 Roo.form.DayPicker= function(config){
30910     Roo.form.DayPicker.superclass.constructor.call(this, config);
30911      
30912 };
30913
30914 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30915     /**
30916      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30917      */
30918     focusClass : undefined,
30919     /**
30920      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30921      */
30922     fieldClass: "x-form-field",
30923    
30924     /**
30925      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30926      * {tag: "input", type: "checkbox", autocomplete: "off"})
30927      */
30928     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
30929     
30930    
30931     actionMode : 'viewEl', 
30932     //
30933     // private
30934  
30935     inputType : 'hidden',
30936     
30937      
30938     inputElement: false, // real input element?
30939     basedOn: false, // ????
30940     
30941     isFormField: true, // not sure where this is needed!!!!
30942
30943     onResize : function(){
30944         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30945         if(!this.boxLabel){
30946             this.el.alignTo(this.wrap, 'c-c');
30947         }
30948     },
30949
30950     initEvents : function(){
30951         Roo.form.Checkbox.superclass.initEvents.call(this);
30952         this.el.on("click", this.onClick,  this);
30953         this.el.on("change", this.onClick,  this);
30954     },
30955
30956
30957     getResizeEl : function(){
30958         return this.wrap;
30959     },
30960
30961     getPositionEl : function(){
30962         return this.wrap;
30963     },
30964
30965     
30966     // private
30967     onRender : function(ct, position){
30968         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30969        
30970         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30971         
30972         var r1 = '<table><tr>';
30973         var r2 = '<tr class="x-form-daypick-icons">';
30974         for (var i=0; i < 7; i++) {
30975             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30976             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30977         }
30978         
30979         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30980         viewEl.select('img').on('click', this.onClick, this);
30981         this.viewEl = viewEl;   
30982         
30983         
30984         // this will not work on Chrome!!!
30985         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30986         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30987         
30988         
30989           
30990
30991     },
30992
30993     // private
30994     initValue : Roo.emptyFn,
30995
30996     /**
30997      * Returns the checked state of the checkbox.
30998      * @return {Boolean} True if checked, else false
30999      */
31000     getValue : function(){
31001         return this.el.dom.value;
31002         
31003     },
31004
31005         // private
31006     onClick : function(e){ 
31007         //this.setChecked(!this.checked);
31008         Roo.get(e.target).toggleClass('x-menu-item-checked');
31009         this.refreshValue();
31010         //if(this.el.dom.checked != this.checked){
31011         //    this.setValue(this.el.dom.checked);
31012        // }
31013     },
31014     
31015     // private
31016     refreshValue : function()
31017     {
31018         var val = '';
31019         this.viewEl.select('img',true).each(function(e,i,n)  {
31020             val += e.is(".x-menu-item-checked") ? String(n) : '';
31021         });
31022         this.setValue(val, true);
31023     },
31024
31025     /**
31026      * Sets the checked state of the checkbox.
31027      * On is always based on a string comparison between inputValue and the param.
31028      * @param {Boolean/String} value - the value to set 
31029      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31030      */
31031     setValue : function(v,suppressEvent){
31032         if (!this.el.dom) {
31033             return;
31034         }
31035         var old = this.el.dom.value ;
31036         this.el.dom.value = v;
31037         if (suppressEvent) {
31038             return ;
31039         }
31040          
31041         // update display..
31042         this.viewEl.select('img',true).each(function(e,i,n)  {
31043             
31044             var on = e.is(".x-menu-item-checked");
31045             var newv = v.indexOf(String(n)) > -1;
31046             if (on != newv) {
31047                 e.toggleClass('x-menu-item-checked');
31048             }
31049             
31050         });
31051         
31052         
31053         this.fireEvent('change', this, v, old);
31054         
31055         
31056     },
31057    
31058     // handle setting of hidden value by some other method!!?!?
31059     setFromHidden: function()
31060     {
31061         if(!this.el){
31062             return;
31063         }
31064         //console.log("SET FROM HIDDEN");
31065         //alert('setFrom hidden');
31066         this.setValue(this.el.dom.value);
31067     },
31068     
31069     onDestroy : function()
31070     {
31071         if(this.viewEl){
31072             Roo.get(this.viewEl).remove();
31073         }
31074          
31075         Roo.form.DayPicker.superclass.onDestroy.call(this);
31076     }
31077
31078 });/*
31079  * RooJS Library 1.1.1
31080  * Copyright(c) 2008-2011  Alan Knowles
31081  *
31082  * License - LGPL
31083  */
31084  
31085
31086 /**
31087  * @class Roo.form.ComboCheck
31088  * @extends Roo.form.ComboBox
31089  * A combobox for multiple select items.
31090  *
31091  * FIXME - could do with a reset button..
31092  * 
31093  * @constructor
31094  * Create a new ComboCheck
31095  * @param {Object} config Configuration options
31096  */
31097 Roo.form.ComboCheck = function(config){
31098     Roo.form.ComboCheck.superclass.constructor.call(this, config);
31099     // should verify some data...
31100     // like
31101     // hiddenName = required..
31102     // displayField = required
31103     // valudField == required
31104     var req= [ 'hiddenName', 'displayField', 'valueField' ];
31105     var _t = this;
31106     Roo.each(req, function(e) {
31107         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31108             throw "Roo.form.ComboCheck : missing value for: " + e;
31109         }
31110     });
31111     
31112     
31113 };
31114
31115 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
31116      
31117      
31118     editable : false,
31119      
31120     selectedClass: 'x-menu-item-checked', 
31121     
31122     // private
31123     onRender : function(ct, position){
31124         var _t = this;
31125         
31126         
31127         
31128         if(!this.tpl){
31129             var cls = 'x-combo-list';
31130
31131             
31132             this.tpl =  new Roo.Template({
31133                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
31134                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
31135                    '<span>{' + this.displayField + '}</span>' +
31136                     '</div>' 
31137                 
31138             });
31139         }
31140  
31141         
31142         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
31143         this.view.singleSelect = false;
31144         this.view.multiSelect = true;
31145         this.view.toggleSelect = true;
31146         this.pageTb.add(new Roo.Toolbar.Fill(), {
31147             
31148             text: 'Done',
31149             handler: function()
31150             {
31151                 _t.collapse();
31152             }
31153         });
31154     },
31155     
31156     onViewOver : function(e, t){
31157         // do nothing...
31158         return;
31159         
31160     },
31161     
31162     onViewClick : function(doFocus,index){
31163         return;
31164         
31165     },
31166     select: function () {
31167         //Roo.log("SELECT CALLED");
31168     },
31169      
31170     selectByValue : function(xv, scrollIntoView){
31171         var ar = this.getValueArray();
31172         var sels = [];
31173         
31174         Roo.each(ar, function(v) {
31175             if(v === undefined || v === null){
31176                 return;
31177             }
31178             var r = this.findRecord(this.valueField, v);
31179             if(r){
31180                 sels.push(this.store.indexOf(r))
31181                 
31182             }
31183         },this);
31184         this.view.select(sels);
31185         return false;
31186     },
31187     
31188     
31189     
31190     onSelect : function(record, index){
31191        // Roo.log("onselect Called");
31192        // this is only called by the clear button now..
31193         this.view.clearSelections();
31194         this.setValue('[]');
31195         if (this.value != this.valueBefore) {
31196             this.fireEvent('change', this, this.value, this.valueBefore);
31197             this.valueBefore = this.value;
31198         }
31199     },
31200     getValueArray : function()
31201     {
31202         var ar = [] ;
31203         
31204         try {
31205             //Roo.log(this.value);
31206             if (typeof(this.value) == 'undefined') {
31207                 return [];
31208             }
31209             var ar = Roo.decode(this.value);
31210             return  ar instanceof Array ? ar : []; //?? valid?
31211             
31212         } catch(e) {
31213             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
31214             return [];
31215         }
31216          
31217     },
31218     expand : function ()
31219     {
31220         
31221         Roo.form.ComboCheck.superclass.expand.call(this);
31222         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
31223         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
31224         
31225
31226     },
31227     
31228     collapse : function(){
31229         Roo.form.ComboCheck.superclass.collapse.call(this);
31230         var sl = this.view.getSelectedIndexes();
31231         var st = this.store;
31232         var nv = [];
31233         var tv = [];
31234         var r;
31235         Roo.each(sl, function(i) {
31236             r = st.getAt(i);
31237             nv.push(r.get(this.valueField));
31238         },this);
31239         this.setValue(Roo.encode(nv));
31240         if (this.value != this.valueBefore) {
31241
31242             this.fireEvent('change', this, this.value, this.valueBefore);
31243             this.valueBefore = this.value;
31244         }
31245         
31246     },
31247     
31248     setValue : function(v){
31249         // Roo.log(v);
31250         this.value = v;
31251         
31252         var vals = this.getValueArray();
31253         var tv = [];
31254         Roo.each(vals, function(k) {
31255             var r = this.findRecord(this.valueField, k);
31256             if(r){
31257                 tv.push(r.data[this.displayField]);
31258             }else if(this.valueNotFoundText !== undefined){
31259                 tv.push( this.valueNotFoundText );
31260             }
31261         },this);
31262        // Roo.log(tv);
31263         
31264         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
31265         this.hiddenField.value = v;
31266         this.value = v;
31267     }
31268     
31269 });/*
31270  * Based on:
31271  * Ext JS Library 1.1.1
31272  * Copyright(c) 2006-2007, Ext JS, LLC.
31273  *
31274  * Originally Released Under LGPL - original licence link has changed is not relivant.
31275  *
31276  * Fork - LGPL
31277  * <script type="text/javascript">
31278  */
31279  
31280 /**
31281  * @class Roo.form.Signature
31282  * @extends Roo.form.Field
31283  * Signature field.  
31284  * @constructor
31285  * 
31286  * @param {Object} config Configuration options
31287  */
31288
31289 Roo.form.Signature = function(config){
31290     Roo.form.Signature.superclass.constructor.call(this, config);
31291     
31292     this.addEvents({// not in used??
31293          /**
31294          * @event confirm
31295          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
31296              * @param {Roo.form.Signature} combo This combo box
31297              */
31298         'confirm' : true,
31299         /**
31300          * @event reset
31301          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
31302              * @param {Roo.form.ComboBox} combo This combo box
31303              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
31304              */
31305         'reset' : true
31306     });
31307 };
31308
31309 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
31310     /**
31311      * @cfg {Object} labels Label to use when rendering a form.
31312      * defaults to 
31313      * labels : { 
31314      *      clear : "Clear",
31315      *      confirm : "Confirm"
31316      *  }
31317      */
31318     labels : { 
31319         clear : "Clear",
31320         confirm : "Confirm"
31321     },
31322     /**
31323      * @cfg {Number} width The signature panel width (defaults to 300)
31324      */
31325     width: 300,
31326     /**
31327      * @cfg {Number} height The signature panel height (defaults to 100)
31328      */
31329     height : 100,
31330     /**
31331      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
31332      */
31333     allowBlank : false,
31334     
31335     //private
31336     // {Object} signPanel The signature SVG panel element (defaults to {})
31337     signPanel : {},
31338     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
31339     isMouseDown : false,
31340     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
31341     isConfirmed : false,
31342     // {String} signatureTmp SVG mapping string (defaults to empty string)
31343     signatureTmp : '',
31344     
31345     
31346     defaultAutoCreate : { // modified by initCompnoent..
31347         tag: "input",
31348         type:"hidden"
31349     },
31350
31351     // private
31352     onRender : function(ct, position){
31353         
31354         Roo.form.Signature.superclass.onRender.call(this, ct, position);
31355         
31356         this.wrap = this.el.wrap({
31357             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
31358         });
31359         
31360         this.createToolbar(this);
31361         this.signPanel = this.wrap.createChild({
31362                 tag: 'div',
31363                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
31364             }, this.el
31365         );
31366             
31367         this.svgID = Roo.id();
31368         this.svgEl = this.signPanel.createChild({
31369               xmlns : 'http://www.w3.org/2000/svg',
31370               tag : 'svg',
31371               id : this.svgID + "-svg",
31372               width: this.width,
31373               height: this.height,
31374               viewBox: '0 0 '+this.width+' '+this.height,
31375               cn : [
31376                 {
31377                     tag: "rect",
31378                     id: this.svgID + "-svg-r",
31379                     width: this.width,
31380                     height: this.height,
31381                     fill: "#ffa"
31382                 },
31383                 {
31384                     tag: "line",
31385                     id: this.svgID + "-svg-l",
31386                     x1: "0", // start
31387                     y1: (this.height*0.8), // start set the line in 80% of height
31388                     x2: this.width, // end
31389                     y2: (this.height*0.8), // end set the line in 80% of height
31390                     'stroke': "#666",
31391                     'stroke-width': "1",
31392                     'stroke-dasharray': "3",
31393                     'shape-rendering': "crispEdges",
31394                     'pointer-events': "none"
31395                 },
31396                 {
31397                     tag: "path",
31398                     id: this.svgID + "-svg-p",
31399                     'stroke': "navy",
31400                     'stroke-width': "3",
31401                     'fill': "none",
31402                     'pointer-events': 'none'
31403                 }
31404               ]
31405         });
31406         this.createSVG();
31407         this.svgBox = this.svgEl.dom.getScreenCTM();
31408     },
31409     createSVG : function(){ 
31410         var svg = this.signPanel;
31411         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
31412         var t = this;
31413
31414         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
31415         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
31416         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
31417         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
31418         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
31419         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
31420         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
31421         
31422     },
31423     isTouchEvent : function(e){
31424         return e.type.match(/^touch/);
31425     },
31426     getCoords : function (e) {
31427         var pt    = this.svgEl.dom.createSVGPoint();
31428         pt.x = e.clientX; 
31429         pt.y = e.clientY;
31430         if (this.isTouchEvent(e)) {
31431             pt.x =  e.targetTouches[0].clientX 
31432             pt.y = e.targetTouches[0].clientY;
31433         }
31434         var a = this.svgEl.dom.getScreenCTM();
31435         var b = a.inverse();
31436         var mx = pt.matrixTransform(b);
31437         return mx.x + ',' + mx.y;
31438     },
31439     //mouse event headler 
31440     down : function (e) {
31441         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
31442         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
31443         
31444         this.isMouseDown = true;
31445         
31446         e.preventDefault();
31447     },
31448     move : function (e) {
31449         if (this.isMouseDown) {
31450             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
31451             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
31452         }
31453         
31454         e.preventDefault();
31455     },
31456     up : function (e) {
31457         this.isMouseDown = false;
31458         var sp = this.signatureTmp.split(' ');
31459         
31460         if(sp.length > 1){
31461             if(!sp[sp.length-2].match(/^L/)){
31462                 sp.pop();
31463                 sp.pop();
31464                 sp.push("");
31465                 this.signatureTmp = sp.join(" ");
31466             }
31467         }
31468         if(this.getValue() != this.signatureTmp){
31469             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31470             this.isConfirmed = false;
31471         }
31472         e.preventDefault();
31473     },
31474     
31475     /**
31476      * Protected method that will not generally be called directly. It
31477      * is called when the editor creates its toolbar. Override this method if you need to
31478      * add custom toolbar buttons.
31479      * @param {HtmlEditor} editor
31480      */
31481     createToolbar : function(editor){
31482          function btn(id, toggle, handler){
31483             var xid = fid + '-'+ id ;
31484             return {
31485                 id : xid,
31486                 cmd : id,
31487                 cls : 'x-btn-icon x-edit-'+id,
31488                 enableToggle:toggle !== false,
31489                 scope: editor, // was editor...
31490                 handler:handler||editor.relayBtnCmd,
31491                 clickEvent:'mousedown',
31492                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
31493                 tabIndex:-1
31494             };
31495         }
31496         
31497         
31498         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31499         this.tb = tb;
31500         this.tb.add(
31501            {
31502                 cls : ' x-signature-btn x-signature-'+id,
31503                 scope: editor, // was editor...
31504                 handler: this.reset,
31505                 clickEvent:'mousedown',
31506                 text: this.labels.clear
31507             },
31508             {
31509                  xtype : 'Fill',
31510                  xns: Roo.Toolbar
31511             }, 
31512             {
31513                 cls : '  x-signature-btn x-signature-'+id,
31514                 scope: editor, // was editor...
31515                 handler: this.confirmHandler,
31516                 clickEvent:'mousedown',
31517                 text: this.labels.confirm
31518             }
31519         );
31520     
31521     },
31522     //public
31523     /**
31524      * when user is clicked confirm then show this image.....
31525      * 
31526      * @return {String} Image Data URI
31527      */
31528     getImageDataURI : function(){
31529         var svg = this.svgEl.dom.parentNode.innerHTML;
31530         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31531         return src; 
31532     },
31533     /**
31534      * 
31535      * @return {Boolean} this.isConfirmed
31536      */
31537     getConfirmed : function(){
31538         return this.isConfirmed;
31539     },
31540     /**
31541      * 
31542      * @return {Number} this.width
31543      */
31544     getWidth : function(){
31545         return this.width;
31546     },
31547     /**
31548      * 
31549      * @return {Number} this.height
31550      */
31551     getHeight : function(){
31552         return this.height;
31553     },
31554     // private
31555     getSignature : function(){
31556         return this.signatureTmp;
31557     },
31558     // private
31559     reset : function(){
31560         this.signatureTmp = '';
31561         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31562         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31563         this.isConfirmed = false;
31564         Roo.form.Signature.superclass.reset.call(this);
31565     },
31566     setSignature : function(s){
31567         this.signatureTmp = s;
31568         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31569         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31570         this.setValue(s);
31571         this.isConfirmed = false;
31572         Roo.form.Signature.superclass.reset.call(this);
31573     }, 
31574     test : function(){
31575 //        Roo.log(this.signPanel.dom.contentWindow.up())
31576     },
31577     //private
31578     setConfirmed : function(){
31579         
31580         
31581         
31582 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31583     },
31584     // private
31585     confirmHandler : function(){
31586         if(!this.getSignature()){
31587             return;
31588         }
31589         
31590         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31591         this.setValue(this.getSignature());
31592         this.isConfirmed = true;
31593         
31594         this.fireEvent('confirm', this);
31595     },
31596     // private
31597     // Subclasses should provide the validation implementation by overriding this
31598     validateValue : function(value){
31599         if(this.allowBlank){
31600             return true;
31601         }
31602         
31603         if(this.isConfirmed){
31604             return true;
31605         }
31606         return false;
31607     }
31608 });/*
31609  * Based on:
31610  * Ext JS Library 1.1.1
31611  * Copyright(c) 2006-2007, Ext JS, LLC.
31612  *
31613  * Originally Released Under LGPL - original licence link has changed is not relivant.
31614  *
31615  * Fork - LGPL
31616  * <script type="text/javascript">
31617  */
31618  
31619
31620 /**
31621  * @class Roo.form.ComboBox
31622  * @extends Roo.form.TriggerField
31623  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
31624  * @constructor
31625  * Create a new ComboBox.
31626  * @param {Object} config Configuration options
31627  */
31628 Roo.form.Select = function(config){
31629     Roo.form.Select.superclass.constructor.call(this, config);
31630      
31631 };
31632
31633 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
31634     /**
31635      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
31636      */
31637     /**
31638      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
31639      * rendering into an Roo.Editor, defaults to false)
31640      */
31641     /**
31642      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
31643      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
31644      */
31645     /**
31646      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
31647      */
31648     /**
31649      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
31650      * the dropdown list (defaults to undefined, with no header element)
31651      */
31652
31653      /**
31654      * @cfg {String/Roo.Template} tpl The template to use to render the output
31655      */
31656      
31657     // private
31658     defaultAutoCreate : {tag: "select"  },
31659     /**
31660      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
31661      */
31662     listWidth: undefined,
31663     /**
31664      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
31665      * mode = 'remote' or 'text' if mode = 'local')
31666      */
31667     displayField: undefined,
31668     /**
31669      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
31670      * mode = 'remote' or 'value' if mode = 'local'). 
31671      * Note: use of a valueField requires the user make a selection
31672      * in order for a value to be mapped.
31673      */
31674     valueField: undefined,
31675     
31676     
31677     /**
31678      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
31679      * field's data value (defaults to the underlying DOM element's name)
31680      */
31681     hiddenName: undefined,
31682     /**
31683      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
31684      */
31685     listClass: '',
31686     /**
31687      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
31688      */
31689     selectedClass: 'x-combo-selected',
31690     /**
31691      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
31692      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
31693      * which displays a downward arrow icon).
31694      */
31695     triggerClass : 'x-form-arrow-trigger',
31696     /**
31697      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31698      */
31699     shadow:'sides',
31700     /**
31701      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
31702      * anchor positions (defaults to 'tl-bl')
31703      */
31704     listAlign: 'tl-bl?',
31705     /**
31706      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
31707      */
31708     maxHeight: 300,
31709     /**
31710      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
31711      * query specified by the allQuery config option (defaults to 'query')
31712      */
31713     triggerAction: 'query',
31714     /**
31715      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
31716      * (defaults to 4, does not apply if editable = false)
31717      */
31718     minChars : 4,
31719     /**
31720      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
31721      * delay (typeAheadDelay) if it matches a known value (defaults to false)
31722      */
31723     typeAhead: false,
31724     /**
31725      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
31726      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
31727      */
31728     queryDelay: 500,
31729     /**
31730      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
31731      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
31732      */
31733     pageSize: 0,
31734     /**
31735      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
31736      * when editable = true (defaults to false)
31737      */
31738     selectOnFocus:false,
31739     /**
31740      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
31741      */
31742     queryParam: 'query',
31743     /**
31744      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
31745      * when mode = 'remote' (defaults to 'Loading...')
31746      */
31747     loadingText: 'Loading...',
31748     /**
31749      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
31750      */
31751     resizable: false,
31752     /**
31753      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
31754      */
31755     handleHeight : 8,
31756     /**
31757      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
31758      * traditional select (defaults to true)
31759      */
31760     editable: true,
31761     /**
31762      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
31763      */
31764     allQuery: '',
31765     /**
31766      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
31767      */
31768     mode: 'remote',
31769     /**
31770      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
31771      * listWidth has a higher value)
31772      */
31773     minListWidth : 70,
31774     /**
31775      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
31776      * allow the user to set arbitrary text into the field (defaults to false)
31777      */
31778     forceSelection:false,
31779     /**
31780      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
31781      * if typeAhead = true (defaults to 250)
31782      */
31783     typeAheadDelay : 250,
31784     /**
31785      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
31786      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
31787      */
31788     valueNotFoundText : undefined,
31789     
31790     /**
31791      * @cfg {String} defaultValue The value displayed after loading the store.
31792      */
31793     defaultValue: '',
31794     
31795     /**
31796      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
31797      */
31798     blockFocus : false,
31799     
31800     /**
31801      * @cfg {Boolean} disableClear Disable showing of clear button.
31802      */
31803     disableClear : false,
31804     /**
31805      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
31806      */
31807     alwaysQuery : false,
31808     
31809     //private
31810     addicon : false,
31811     editicon: false,
31812     
31813     // element that contains real text value.. (when hidden is used..)
31814      
31815     // private
31816     onRender : function(ct, position){
31817         Roo.form.Field.prototype.onRender.call(this, ct, position);
31818         
31819         if(this.store){
31820             this.store.on('beforeload', this.onBeforeLoad, this);
31821             this.store.on('load', this.onLoad, this);
31822             this.store.on('loadexception', this.onLoadException, this);
31823             this.store.load({});
31824         }
31825         
31826         
31827         
31828     },
31829
31830     // private
31831     initEvents : function(){
31832         //Roo.form.ComboBox.superclass.initEvents.call(this);
31833  
31834     },
31835
31836     onDestroy : function(){
31837        
31838         if(this.store){
31839             this.store.un('beforeload', this.onBeforeLoad, this);
31840             this.store.un('load', this.onLoad, this);
31841             this.store.un('loadexception', this.onLoadException, this);
31842         }
31843         //Roo.form.ComboBox.superclass.onDestroy.call(this);
31844     },
31845
31846     // private
31847     fireKey : function(e){
31848         if(e.isNavKeyPress() && !this.list.isVisible()){
31849             this.fireEvent("specialkey", this, e);
31850         }
31851     },
31852
31853     // private
31854     onResize: function(w, h){
31855         
31856         return; 
31857     
31858         
31859     },
31860
31861     /**
31862      * Allow or prevent the user from directly editing the field text.  If false is passed,
31863      * the user will only be able to select from the items defined in the dropdown list.  This method
31864      * is the runtime equivalent of setting the 'editable' config option at config time.
31865      * @param {Boolean} value True to allow the user to directly edit the field text
31866      */
31867     setEditable : function(value){
31868          
31869     },
31870
31871     // private
31872     onBeforeLoad : function(){
31873         
31874         Roo.log("Select before load");
31875         return;
31876     
31877         this.innerList.update(this.loadingText ?
31878                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
31879         //this.restrictHeight();
31880         this.selectedIndex = -1;
31881     },
31882
31883     // private
31884     onLoad : function(){
31885
31886     
31887         var dom = this.el.dom;
31888         dom.innerHTML = '';
31889          var od = dom.ownerDocument;
31890          
31891         if (this.emptyText) {
31892             var op = od.createElement('option');
31893             op.setAttribute('value', '');
31894             op.innerHTML = String.format('{0}', this.emptyText);
31895             dom.appendChild(op);
31896         }
31897         if(this.store.getCount() > 0){
31898            
31899             var vf = this.valueField;
31900             var df = this.displayField;
31901             this.store.data.each(function(r) {
31902                 // which colmsn to use... testing - cdoe / title..
31903                 var op = od.createElement('option');
31904                 op.setAttribute('value', r.data[vf]);
31905                 op.innerHTML = String.format('{0}', r.data[df]);
31906                 dom.appendChild(op);
31907             });
31908             if (typeof(this.defaultValue != 'undefined')) {
31909                 this.setValue(this.defaultValue);
31910             }
31911             
31912              
31913         }else{
31914             //this.onEmptyResults();
31915         }
31916         //this.el.focus();
31917     },
31918     // private
31919     onLoadException : function()
31920     {
31921         dom.innerHTML = '';
31922             
31923         Roo.log("Select on load exception");
31924         return;
31925     
31926         this.collapse();
31927         Roo.log(this.store.reader.jsonData);
31928         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
31929             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
31930         }
31931         
31932         
31933     },
31934     // private
31935     onTypeAhead : function(){
31936          
31937     },
31938
31939     // private
31940     onSelect : function(record, index){
31941         Roo.log('on select?');
31942         return;
31943         if(this.fireEvent('beforeselect', this, record, index) !== false){
31944             this.setFromData(index > -1 ? record.data : false);
31945             this.collapse();
31946             this.fireEvent('select', this, record, index);
31947         }
31948     },
31949
31950     /**
31951      * Returns the currently selected field value or empty string if no value is set.
31952      * @return {String} value The selected value
31953      */
31954     getValue : function(){
31955         var dom = this.el.dom;
31956         this.value = dom.options[dom.selectedIndex].value;
31957         return this.value;
31958         
31959     },
31960
31961     /**
31962      * Clears any text/value currently set in the field
31963      */
31964     clearValue : function(){
31965         this.value = '';
31966         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
31967         
31968     },
31969
31970     /**
31971      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
31972      * will be displayed in the field.  If the value does not match the data value of an existing item,
31973      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
31974      * Otherwise the field will be blank (although the value will still be set).
31975      * @param {String} value The value to match
31976      */
31977     setValue : function(v){
31978         var d = this.el.dom;
31979         for (var i =0; i < d.options.length;i++) {
31980             if (v == d.options[i].value) {
31981                 d.selectedIndex = i;
31982                 this.value = v;
31983                 return;
31984             }
31985         }
31986         this.clearValue();
31987     },
31988     /**
31989      * @property {Object} the last set data for the element
31990      */
31991     
31992     lastData : false,
31993     /**
31994      * Sets the value of the field based on a object which is related to the record format for the store.
31995      * @param {Object} value the value to set as. or false on reset?
31996      */
31997     setFromData : function(o){
31998         Roo.log('setfrom data?');
31999          
32000         
32001         
32002     },
32003     // private
32004     reset : function(){
32005         this.clearValue();
32006     },
32007     // private
32008     findRecord : function(prop, value){
32009         
32010         return false;
32011     
32012         var record;
32013         if(this.store.getCount() > 0){
32014             this.store.each(function(r){
32015                 if(r.data[prop] == value){
32016                     record = r;
32017                     return false;
32018                 }
32019                 return true;
32020             });
32021         }
32022         return record;
32023     },
32024     
32025     getName: function()
32026     {
32027         // returns hidden if it's set..
32028         if (!this.rendered) {return ''};
32029         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
32030         
32031     },
32032      
32033
32034     
32035
32036     // private
32037     onEmptyResults : function(){
32038         Roo.log('empty results');
32039         //this.collapse();
32040     },
32041
32042     /**
32043      * Returns true if the dropdown list is expanded, else false.
32044      */
32045     isExpanded : function(){
32046         return false;
32047     },
32048
32049     /**
32050      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32051      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32052      * @param {String} value The data value of the item to select
32053      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32054      * selected item if it is not currently in view (defaults to true)
32055      * @return {Boolean} True if the value matched an item in the list, else false
32056      */
32057     selectByValue : function(v, scrollIntoView){
32058         Roo.log('select By Value');
32059         return false;
32060     
32061         if(v !== undefined && v !== null){
32062             var r = this.findRecord(this.valueField || this.displayField, v);
32063             if(r){
32064                 this.select(this.store.indexOf(r), scrollIntoView);
32065                 return true;
32066             }
32067         }
32068         return false;
32069     },
32070
32071     /**
32072      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32073      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32074      * @param {Number} index The zero-based index of the list item to select
32075      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32076      * selected item if it is not currently in view (defaults to true)
32077      */
32078     select : function(index, scrollIntoView){
32079         Roo.log('select ');
32080         return  ;
32081         
32082         this.selectedIndex = index;
32083         this.view.select(index);
32084         if(scrollIntoView !== false){
32085             var el = this.view.getNode(index);
32086             if(el){
32087                 this.innerList.scrollChildIntoView(el, false);
32088             }
32089         }
32090     },
32091
32092       
32093
32094     // private
32095     validateBlur : function(){
32096         
32097         return;
32098         
32099     },
32100
32101     // private
32102     initQuery : function(){
32103         this.doQuery(this.getRawValue());
32104     },
32105
32106     // private
32107     doForce : function(){
32108         if(this.el.dom.value.length > 0){
32109             this.el.dom.value =
32110                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
32111              
32112         }
32113     },
32114
32115     /**
32116      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
32117      * query allowing the query action to be canceled if needed.
32118      * @param {String} query The SQL query to execute
32119      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
32120      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
32121      * saved in the current store (defaults to false)
32122      */
32123     doQuery : function(q, forceAll){
32124         
32125         Roo.log('doQuery?');
32126         if(q === undefined || q === null){
32127             q = '';
32128         }
32129         var qe = {
32130             query: q,
32131             forceAll: forceAll,
32132             combo: this,
32133             cancel:false
32134         };
32135         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
32136             return false;
32137         }
32138         q = qe.query;
32139         forceAll = qe.forceAll;
32140         if(forceAll === true || (q.length >= this.minChars)){
32141             if(this.lastQuery != q || this.alwaysQuery){
32142                 this.lastQuery = q;
32143                 if(this.mode == 'local'){
32144                     this.selectedIndex = -1;
32145                     if(forceAll){
32146                         this.store.clearFilter();
32147                     }else{
32148                         this.store.filter(this.displayField, q);
32149                     }
32150                     this.onLoad();
32151                 }else{
32152                     this.store.baseParams[this.queryParam] = q;
32153                     this.store.load({
32154                         params: this.getParams(q)
32155                     });
32156                     this.expand();
32157                 }
32158             }else{
32159                 this.selectedIndex = -1;
32160                 this.onLoad();   
32161             }
32162         }
32163     },
32164
32165     // private
32166     getParams : function(q){
32167         var p = {};
32168         //p[this.queryParam] = q;
32169         if(this.pageSize){
32170             p.start = 0;
32171             p.limit = this.pageSize;
32172         }
32173         return p;
32174     },
32175
32176     /**
32177      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
32178      */
32179     collapse : function(){
32180         
32181     },
32182
32183     // private
32184     collapseIf : function(e){
32185         
32186     },
32187
32188     /**
32189      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
32190      */
32191     expand : function(){
32192         
32193     } ,
32194
32195     // private
32196      
32197
32198     /** 
32199     * @cfg {Boolean} grow 
32200     * @hide 
32201     */
32202     /** 
32203     * @cfg {Number} growMin 
32204     * @hide 
32205     */
32206     /** 
32207     * @cfg {Number} growMax 
32208     * @hide 
32209     */
32210     /**
32211      * @hide
32212      * @method autoSize
32213      */
32214     
32215     setWidth : function()
32216     {
32217         
32218     },
32219     getResizeEl : function(){
32220         return this.el;
32221     }
32222 });//<script type="text/javasscript">
32223  
32224
32225 /**
32226  * @class Roo.DDView
32227  * A DnD enabled version of Roo.View.
32228  * @param {Element/String} container The Element in which to create the View.
32229  * @param {String} tpl The template string used to create the markup for each element of the View
32230  * @param {Object} config The configuration properties. These include all the config options of
32231  * {@link Roo.View} plus some specific to this class.<br>
32232  * <p>
32233  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
32234  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
32235  * <p>
32236  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
32237 .x-view-drag-insert-above {
32238         border-top:1px dotted #3366cc;
32239 }
32240 .x-view-drag-insert-below {
32241         border-bottom:1px dotted #3366cc;
32242 }
32243 </code></pre>
32244  * 
32245  */
32246  
32247 Roo.DDView = function(container, tpl, config) {
32248     Roo.DDView.superclass.constructor.apply(this, arguments);
32249     this.getEl().setStyle("outline", "0px none");
32250     this.getEl().unselectable();
32251     if (this.dragGroup) {
32252                 this.setDraggable(this.dragGroup.split(","));
32253     }
32254     if (this.dropGroup) {
32255                 this.setDroppable(this.dropGroup.split(","));
32256     }
32257     if (this.deletable) {
32258         this.setDeletable();
32259     }
32260     this.isDirtyFlag = false;
32261         this.addEvents({
32262                 "drop" : true
32263         });
32264 };
32265
32266 Roo.extend(Roo.DDView, Roo.View, {
32267 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
32268 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
32269 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
32270 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
32271
32272         isFormField: true,
32273
32274         reset: Roo.emptyFn,
32275         
32276         clearInvalid: Roo.form.Field.prototype.clearInvalid,
32277
32278         validate: function() {
32279                 return true;
32280         },
32281         
32282         destroy: function() {
32283                 this.purgeListeners();
32284                 this.getEl.removeAllListeners();
32285                 this.getEl().remove();
32286                 if (this.dragZone) {
32287                         if (this.dragZone.destroy) {
32288                                 this.dragZone.destroy();
32289                         }
32290                 }
32291                 if (this.dropZone) {
32292                         if (this.dropZone.destroy) {
32293                                 this.dropZone.destroy();
32294                         }
32295                 }
32296         },
32297
32298 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
32299         getName: function() {
32300                 return this.name;
32301         },
32302
32303 /**     Loads the View from a JSON string representing the Records to put into the Store. */
32304         setValue: function(v) {
32305                 if (!this.store) {
32306                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
32307                 }
32308                 var data = {};
32309                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
32310                 this.store.proxy = new Roo.data.MemoryProxy(data);
32311                 this.store.load();
32312         },
32313
32314 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
32315         getValue: function() {
32316                 var result = '(';
32317                 this.store.each(function(rec) {
32318                         result += rec.id + ',';
32319                 });
32320                 return result.substr(0, result.length - 1) + ')';
32321         },
32322         
32323         getIds: function() {
32324                 var i = 0, result = new Array(this.store.getCount());
32325                 this.store.each(function(rec) {
32326                         result[i++] = rec.id;
32327                 });
32328                 return result;
32329         },
32330         
32331         isDirty: function() {
32332                 return this.isDirtyFlag;
32333         },
32334
32335 /**
32336  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
32337  *      whole Element becomes the target, and this causes the drop gesture to append.
32338  */
32339     getTargetFromEvent : function(e) {
32340                 var target = e.getTarget();
32341                 while ((target !== null) && (target.parentNode != this.el.dom)) {
32342                 target = target.parentNode;
32343                 }
32344                 if (!target) {
32345                         target = this.el.dom.lastChild || this.el.dom;
32346                 }
32347                 return target;
32348     },
32349
32350 /**
32351  *      Create the drag data which consists of an object which has the property "ddel" as
32352  *      the drag proxy element. 
32353  */
32354     getDragData : function(e) {
32355         var target = this.findItemFromChild(e.getTarget());
32356                 if(target) {
32357                         this.handleSelection(e);
32358                         var selNodes = this.getSelectedNodes();
32359             var dragData = {
32360                 source: this,
32361                 copy: this.copy || (this.allowCopy && e.ctrlKey),
32362                 nodes: selNodes,
32363                 records: []
32364                         };
32365                         var selectedIndices = this.getSelectedIndexes();
32366                         for (var i = 0; i < selectedIndices.length; i++) {
32367                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
32368                         }
32369                         if (selNodes.length == 1) {
32370                                 dragData.ddel = target.cloneNode(true); // the div element
32371                         } else {
32372                                 var div = document.createElement('div'); // create the multi element drag "ghost"
32373                                 div.className = 'multi-proxy';
32374                                 for (var i = 0, len = selNodes.length; i < len; i++) {
32375                                         div.appendChild(selNodes[i].cloneNode(true));
32376                                 }
32377                                 dragData.ddel = div;
32378                         }
32379             //console.log(dragData)
32380             //console.log(dragData.ddel.innerHTML)
32381                         return dragData;
32382                 }
32383         //console.log('nodragData')
32384                 return false;
32385     },
32386     
32387 /**     Specify to which ddGroup items in this DDView may be dragged. */
32388     setDraggable: function(ddGroup) {
32389         if (ddGroup instanceof Array) {
32390                 Roo.each(ddGroup, this.setDraggable, this);
32391                 return;
32392         }
32393         if (this.dragZone) {
32394                 this.dragZone.addToGroup(ddGroup);
32395         } else {
32396                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
32397                                 containerScroll: true,
32398                                 ddGroup: ddGroup 
32399
32400                         });
32401 //                      Draggability implies selection. DragZone's mousedown selects the element.
32402                         if (!this.multiSelect) { this.singleSelect = true; }
32403
32404 //                      Wire the DragZone's handlers up to methods in *this*
32405                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
32406                 }
32407     },
32408
32409 /**     Specify from which ddGroup this DDView accepts drops. */
32410     setDroppable: function(ddGroup) {
32411         if (ddGroup instanceof Array) {
32412                 Roo.each(ddGroup, this.setDroppable, this);
32413                 return;
32414         }
32415         if (this.dropZone) {
32416                 this.dropZone.addToGroup(ddGroup);
32417         } else {
32418                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
32419                                 containerScroll: true,
32420                                 ddGroup: ddGroup
32421                         });
32422
32423 //                      Wire the DropZone's handlers up to methods in *this*
32424                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
32425                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
32426                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
32427                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
32428                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
32429                 }
32430     },
32431
32432 /**     Decide whether to drop above or below a View node. */
32433     getDropPoint : function(e, n, dd){
32434         if (n == this.el.dom) { return "above"; }
32435                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
32436                 var c = t + (b - t) / 2;
32437                 var y = Roo.lib.Event.getPageY(e);
32438                 if(y <= c) {
32439                         return "above";
32440                 }else{
32441                         return "below";
32442                 }
32443     },
32444
32445     onNodeEnter : function(n, dd, e, data){
32446                 return false;
32447     },
32448     
32449     onNodeOver : function(n, dd, e, data){
32450                 var pt = this.getDropPoint(e, n, dd);
32451                 // set the insert point style on the target node
32452                 var dragElClass = this.dropNotAllowed;
32453                 if (pt) {
32454                         var targetElClass;
32455                         if (pt == "above"){
32456                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
32457                                 targetElClass = "x-view-drag-insert-above";
32458                         } else {
32459                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
32460                                 targetElClass = "x-view-drag-insert-below";
32461                         }
32462                         if (this.lastInsertClass != targetElClass){
32463                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
32464                                 this.lastInsertClass = targetElClass;
32465                         }
32466                 }
32467                 return dragElClass;
32468         },
32469
32470     onNodeOut : function(n, dd, e, data){
32471                 this.removeDropIndicators(n);
32472     },
32473
32474     onNodeDrop : function(n, dd, e, data){
32475         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
32476                 return false;
32477         }
32478         var pt = this.getDropPoint(e, n, dd);
32479                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
32480                 if (pt == "below") { insertAt++; }
32481                 for (var i = 0; i < data.records.length; i++) {
32482                         var r = data.records[i];
32483                         var dup = this.store.getById(r.id);
32484                         if (dup && (dd != this.dragZone)) {
32485                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
32486                         } else {
32487                                 if (data.copy) {
32488                                         this.store.insert(insertAt++, r.copy());
32489                                 } else {
32490                                         data.source.isDirtyFlag = true;
32491                                         r.store.remove(r);
32492                                         this.store.insert(insertAt++, r);
32493                                 }
32494                                 this.isDirtyFlag = true;
32495                         }
32496                 }
32497                 this.dragZone.cachedTarget = null;
32498                 return true;
32499     },
32500
32501     removeDropIndicators : function(n){
32502                 if(n){
32503                         Roo.fly(n).removeClass([
32504                                 "x-view-drag-insert-above",
32505                                 "x-view-drag-insert-below"]);
32506                         this.lastInsertClass = "_noclass";
32507                 }
32508     },
32509
32510 /**
32511  *      Utility method. Add a delete option to the DDView's context menu.
32512  *      @param {String} imageUrl The URL of the "delete" icon image.
32513  */
32514         setDeletable: function(imageUrl) {
32515                 if (!this.singleSelect && !this.multiSelect) {
32516                         this.singleSelect = true;
32517                 }
32518                 var c = this.getContextMenu();
32519                 this.contextMenu.on("itemclick", function(item) {
32520                         switch (item.id) {
32521                                 case "delete":
32522                                         this.remove(this.getSelectedIndexes());
32523                                         break;
32524                         }
32525                 }, this);
32526                 this.contextMenu.add({
32527                         icon: imageUrl,
32528                         id: "delete",
32529                         text: 'Delete'
32530                 });
32531         },
32532         
32533 /**     Return the context menu for this DDView. */
32534         getContextMenu: function() {
32535                 if (!this.contextMenu) {
32536 //                      Create the View's context menu
32537                         this.contextMenu = new Roo.menu.Menu({
32538                                 id: this.id + "-contextmenu"
32539                         });
32540                         this.el.on("contextmenu", this.showContextMenu, this);
32541                 }
32542                 return this.contextMenu;
32543         },
32544         
32545         disableContextMenu: function() {
32546                 if (this.contextMenu) {
32547                         this.el.un("contextmenu", this.showContextMenu, this);
32548                 }
32549         },
32550
32551         showContextMenu: function(e, item) {
32552         item = this.findItemFromChild(e.getTarget());
32553                 if (item) {
32554                         e.stopEvent();
32555                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
32556                         this.contextMenu.showAt(e.getXY());
32557             }
32558     },
32559
32560 /**
32561  *      Remove {@link Roo.data.Record}s at the specified indices.
32562  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
32563  */
32564     remove: function(selectedIndices) {
32565                 selectedIndices = [].concat(selectedIndices);
32566                 for (var i = 0; i < selectedIndices.length; i++) {
32567                         var rec = this.store.getAt(selectedIndices[i]);
32568                         this.store.remove(rec);
32569                 }
32570     },
32571
32572 /**
32573  *      Double click fires the event, but also, if this is draggable, and there is only one other
32574  *      related DropZone, it transfers the selected node.
32575  */
32576     onDblClick : function(e){
32577         var item = this.findItemFromChild(e.getTarget());
32578         if(item){
32579             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
32580                 return false;
32581             }
32582             if (this.dragGroup) {
32583                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
32584                     while (targets.indexOf(this.dropZone) > -1) {
32585                             targets.remove(this.dropZone);
32586                                 }
32587                     if (targets.length == 1) {
32588                                         this.dragZone.cachedTarget = null;
32589                         var el = Roo.get(targets[0].getEl());
32590                         var box = el.getBox(true);
32591                         targets[0].onNodeDrop(el.dom, {
32592                                 target: el.dom,
32593                                 xy: [box.x, box.y + box.height - 1]
32594                         }, null, this.getDragData(e));
32595                     }
32596                 }
32597         }
32598     },
32599     
32600     handleSelection: function(e) {
32601                 this.dragZone.cachedTarget = null;
32602         var item = this.findItemFromChild(e.getTarget());
32603         if (!item) {
32604                 this.clearSelections(true);
32605                 return;
32606         }
32607                 if (item && (this.multiSelect || this.singleSelect)){
32608                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
32609                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
32610                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
32611                                 this.unselect(item);
32612                         } else {
32613                                 this.select(item, this.multiSelect && e.ctrlKey);
32614                                 this.lastSelection = item;
32615                         }
32616                 }
32617     },
32618
32619     onItemClick : function(item, index, e){
32620                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
32621                         return false;
32622                 }
32623                 return true;
32624     },
32625
32626     unselect : function(nodeInfo, suppressEvent){
32627                 var node = this.getNode(nodeInfo);
32628                 if(node && this.isSelected(node)){
32629                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
32630                                 Roo.fly(node).removeClass(this.selectedClass);
32631                                 this.selections.remove(node);
32632                                 if(!suppressEvent){
32633                                         this.fireEvent("selectionchange", this, this.selections);
32634                                 }
32635                         }
32636                 }
32637     }
32638 });
32639 /*
32640  * Based on:
32641  * Ext JS Library 1.1.1
32642  * Copyright(c) 2006-2007, Ext JS, LLC.
32643  *
32644  * Originally Released Under LGPL - original licence link has changed is not relivant.
32645  *
32646  * Fork - LGPL
32647  * <script type="text/javascript">
32648  */
32649  
32650 /**
32651  * @class Roo.LayoutManager
32652  * @extends Roo.util.Observable
32653  * Base class for layout managers.
32654  */
32655 Roo.LayoutManager = function(container, config){
32656     Roo.LayoutManager.superclass.constructor.call(this);
32657     this.el = Roo.get(container);
32658     // ie scrollbar fix
32659     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32660         document.body.scroll = "no";
32661     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32662         this.el.position('relative');
32663     }
32664     this.id = this.el.id;
32665     this.el.addClass("x-layout-container");
32666     /** false to disable window resize monitoring @type Boolean */
32667     this.monitorWindowResize = true;
32668     this.regions = {};
32669     this.addEvents({
32670         /**
32671          * @event layout
32672          * Fires when a layout is performed. 
32673          * @param {Roo.LayoutManager} this
32674          */
32675         "layout" : true,
32676         /**
32677          * @event regionresized
32678          * Fires when the user resizes a region. 
32679          * @param {Roo.LayoutRegion} region The resized region
32680          * @param {Number} newSize The new size (width for east/west, height for north/south)
32681          */
32682         "regionresized" : true,
32683         /**
32684          * @event regioncollapsed
32685          * Fires when a region is collapsed. 
32686          * @param {Roo.LayoutRegion} region The collapsed region
32687          */
32688         "regioncollapsed" : true,
32689         /**
32690          * @event regionexpanded
32691          * Fires when a region is expanded.  
32692          * @param {Roo.LayoutRegion} region The expanded region
32693          */
32694         "regionexpanded" : true
32695     });
32696     this.updating = false;
32697     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32698 };
32699
32700 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
32701     /**
32702      * Returns true if this layout is currently being updated
32703      * @return {Boolean}
32704      */
32705     isUpdating : function(){
32706         return this.updating; 
32707     },
32708     
32709     /**
32710      * Suspend the LayoutManager from doing auto-layouts while
32711      * making multiple add or remove calls
32712      */
32713     beginUpdate : function(){
32714         this.updating = true;    
32715     },
32716     
32717     /**
32718      * Restore auto-layouts and optionally disable the manager from performing a layout
32719      * @param {Boolean} noLayout true to disable a layout update 
32720      */
32721     endUpdate : function(noLayout){
32722         this.updating = false;
32723         if(!noLayout){
32724             this.layout();
32725         }    
32726     },
32727     
32728     layout: function(){
32729         
32730     },
32731     
32732     onRegionResized : function(region, newSize){
32733         this.fireEvent("regionresized", region, newSize);
32734         this.layout();
32735     },
32736     
32737     onRegionCollapsed : function(region){
32738         this.fireEvent("regioncollapsed", region);
32739     },
32740     
32741     onRegionExpanded : function(region){
32742         this.fireEvent("regionexpanded", region);
32743     },
32744         
32745     /**
32746      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32747      * performs box-model adjustments.
32748      * @return {Object} The size as an object {width: (the width), height: (the height)}
32749      */
32750     getViewSize : function(){
32751         var size;
32752         if(this.el.dom != document.body){
32753             size = this.el.getSize();
32754         }else{
32755             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32756         }
32757         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32758         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32759         return size;
32760     },
32761     
32762     /**
32763      * Returns the Element this layout is bound to.
32764      * @return {Roo.Element}
32765      */
32766     getEl : function(){
32767         return this.el;
32768     },
32769     
32770     /**
32771      * Returns the specified region.
32772      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32773      * @return {Roo.LayoutRegion}
32774      */
32775     getRegion : function(target){
32776         return this.regions[target.toLowerCase()];
32777     },
32778     
32779     onWindowResize : function(){
32780         if(this.monitorWindowResize){
32781             this.layout();
32782         }
32783     }
32784 });/*
32785  * Based on:
32786  * Ext JS Library 1.1.1
32787  * Copyright(c) 2006-2007, Ext JS, LLC.
32788  *
32789  * Originally Released Under LGPL - original licence link has changed is not relivant.
32790  *
32791  * Fork - LGPL
32792  * <script type="text/javascript">
32793  */
32794 /**
32795  * @class Roo.BorderLayout
32796  * @extends Roo.LayoutManager
32797  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32798  * please see: <br><br>
32799  * <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>
32800  * <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>
32801  * Example:
32802  <pre><code>
32803  var layout = new Roo.BorderLayout(document.body, {
32804     north: {
32805         initialSize: 25,
32806         titlebar: false
32807     },
32808     west: {
32809         split:true,
32810         initialSize: 200,
32811         minSize: 175,
32812         maxSize: 400,
32813         titlebar: true,
32814         collapsible: true
32815     },
32816     east: {
32817         split:true,
32818         initialSize: 202,
32819         minSize: 175,
32820         maxSize: 400,
32821         titlebar: true,
32822         collapsible: true
32823     },
32824     south: {
32825         split:true,
32826         initialSize: 100,
32827         minSize: 100,
32828         maxSize: 200,
32829         titlebar: true,
32830         collapsible: true
32831     },
32832     center: {
32833         titlebar: true,
32834         autoScroll:true,
32835         resizeTabs: true,
32836         minTabWidth: 50,
32837         preferredTabWidth: 150
32838     }
32839 });
32840
32841 // shorthand
32842 var CP = Roo.ContentPanel;
32843
32844 layout.beginUpdate();
32845 layout.add("north", new CP("north", "North"));
32846 layout.add("south", new CP("south", {title: "South", closable: true}));
32847 layout.add("west", new CP("west", {title: "West"}));
32848 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
32849 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
32850 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
32851 layout.getRegion("center").showPanel("center1");
32852 layout.endUpdate();
32853 </code></pre>
32854
32855 <b>The container the layout is rendered into can be either the body element or any other element.
32856 If it is not the body element, the container needs to either be an absolute positioned element,
32857 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32858 the container size if it is not the body element.</b>
32859
32860 * @constructor
32861 * Create a new BorderLayout
32862 * @param {String/HTMLElement/Element} container The container this layout is bound to
32863 * @param {Object} config Configuration options
32864  */
32865 Roo.BorderLayout = function(container, config){
32866     config = config || {};
32867     Roo.BorderLayout.superclass.constructor.call(this, container, config);
32868     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
32869     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
32870         var target = this.factory.validRegions[i];
32871         if(config[target]){
32872             this.addRegion(target, config[target]);
32873         }
32874     }
32875 };
32876
32877 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
32878     /**
32879      * Creates and adds a new region if it doesn't already exist.
32880      * @param {String} target The target region key (north, south, east, west or center).
32881      * @param {Object} config The regions config object
32882      * @return {BorderLayoutRegion} The new region
32883      */
32884     addRegion : function(target, config){
32885         if(!this.regions[target]){
32886             var r = this.factory.create(target, this, config);
32887             this.bindRegion(target, r);
32888         }
32889         return this.regions[target];
32890     },
32891
32892     // private (kinda)
32893     bindRegion : function(name, r){
32894         this.regions[name] = r;
32895         r.on("visibilitychange", this.layout, this);
32896         r.on("paneladded", this.layout, this);
32897         r.on("panelremoved", this.layout, this);
32898         r.on("invalidated", this.layout, this);
32899         r.on("resized", this.onRegionResized, this);
32900         r.on("collapsed", this.onRegionCollapsed, this);
32901         r.on("expanded", this.onRegionExpanded, this);
32902     },
32903
32904     /**
32905      * Performs a layout update.
32906      */
32907     layout : function(){
32908         if(this.updating) return;
32909         var size = this.getViewSize();
32910         var w = size.width;
32911         var h = size.height;
32912         var centerW = w;
32913         var centerH = h;
32914         var centerY = 0;
32915         var centerX = 0;
32916         //var x = 0, y = 0;
32917
32918         var rs = this.regions;
32919         var north = rs["north"];
32920         var south = rs["south"]; 
32921         var west = rs["west"];
32922         var east = rs["east"];
32923         var center = rs["center"];
32924         //if(this.hideOnLayout){ // not supported anymore
32925             //c.el.setStyle("display", "none");
32926         //}
32927         if(north && north.isVisible()){
32928             var b = north.getBox();
32929             var m = north.getMargins();
32930             b.width = w - (m.left+m.right);
32931             b.x = m.left;
32932             b.y = m.top;
32933             centerY = b.height + b.y + m.bottom;
32934             centerH -= centerY;
32935             north.updateBox(this.safeBox(b));
32936         }
32937         if(south && south.isVisible()){
32938             var b = south.getBox();
32939             var m = south.getMargins();
32940             b.width = w - (m.left+m.right);
32941             b.x = m.left;
32942             var totalHeight = (b.height + m.top + m.bottom);
32943             b.y = h - totalHeight + m.top;
32944             centerH -= totalHeight;
32945             south.updateBox(this.safeBox(b));
32946         }
32947         if(west && west.isVisible()){
32948             var b = west.getBox();
32949             var m = west.getMargins();
32950             b.height = centerH - (m.top+m.bottom);
32951             b.x = m.left;
32952             b.y = centerY + m.top;
32953             var totalWidth = (b.width + m.left + m.right);
32954             centerX += totalWidth;
32955             centerW -= totalWidth;
32956             west.updateBox(this.safeBox(b));
32957         }
32958         if(east && east.isVisible()){
32959             var b = east.getBox();
32960             var m = east.getMargins();
32961             b.height = centerH - (m.top+m.bottom);
32962             var totalWidth = (b.width + m.left + m.right);
32963             b.x = w - totalWidth + m.left;
32964             b.y = centerY + m.top;
32965             centerW -= totalWidth;
32966             east.updateBox(this.safeBox(b));
32967         }
32968         if(center){
32969             var m = center.getMargins();
32970             var centerBox = {
32971                 x: centerX + m.left,
32972                 y: centerY + m.top,
32973                 width: centerW - (m.left+m.right),
32974                 height: centerH - (m.top+m.bottom)
32975             };
32976             //if(this.hideOnLayout){
32977                 //center.el.setStyle("display", "block");
32978             //}
32979             center.updateBox(this.safeBox(centerBox));
32980         }
32981         this.el.repaint();
32982         this.fireEvent("layout", this);
32983     },
32984
32985     // private
32986     safeBox : function(box){
32987         box.width = Math.max(0, box.width);
32988         box.height = Math.max(0, box.height);
32989         return box;
32990     },
32991
32992     /**
32993      * Adds a ContentPanel (or subclass) to this layout.
32994      * @param {String} target The target region key (north, south, east, west or center).
32995      * @param {Roo.ContentPanel} panel The panel to add
32996      * @return {Roo.ContentPanel} The added panel
32997      */
32998     add : function(target, panel){
32999          
33000         target = target.toLowerCase();
33001         return this.regions[target].add(panel);
33002     },
33003
33004     /**
33005      * Remove a ContentPanel (or subclass) to this layout.
33006      * @param {String} target The target region key (north, south, east, west or center).
33007      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33008      * @return {Roo.ContentPanel} The removed panel
33009      */
33010     remove : function(target, panel){
33011         target = target.toLowerCase();
33012         return this.regions[target].remove(panel);
33013     },
33014
33015     /**
33016      * Searches all regions for a panel with the specified id
33017      * @param {String} panelId
33018      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33019      */
33020     findPanel : function(panelId){
33021         var rs = this.regions;
33022         for(var target in rs){
33023             if(typeof rs[target] != "function"){
33024                 var p = rs[target].getPanel(panelId);
33025                 if(p){
33026                     return p;
33027                 }
33028             }
33029         }
33030         return null;
33031     },
33032
33033     /**
33034      * Searches all regions for a panel with the specified id and activates (shows) it.
33035      * @param {String/ContentPanel} panelId The panels id or the panel itself
33036      * @return {Roo.ContentPanel} The shown panel or null
33037      */
33038     showPanel : function(panelId) {
33039       var rs = this.regions;
33040       for(var target in rs){
33041          var r = rs[target];
33042          if(typeof r != "function"){
33043             if(r.hasPanel(panelId)){
33044                return r.showPanel(panelId);
33045             }
33046          }
33047       }
33048       return null;
33049    },
33050
33051    /**
33052      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33053      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33054      */
33055     restoreState : function(provider){
33056         if(!provider){
33057             provider = Roo.state.Manager;
33058         }
33059         var sm = new Roo.LayoutStateManager();
33060         sm.init(this, provider);
33061     },
33062
33063     /**
33064      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
33065      * object should contain properties for each region to add ContentPanels to, and each property's value should be
33066      * a valid ContentPanel config object.  Example:
33067      * <pre><code>
33068 // Create the main layout
33069 var layout = new Roo.BorderLayout('main-ct', {
33070     west: {
33071         split:true,
33072         minSize: 175,
33073         titlebar: true
33074     },
33075     center: {
33076         title:'Components'
33077     }
33078 }, 'main-ct');
33079
33080 // Create and add multiple ContentPanels at once via configs
33081 layout.batchAdd({
33082    west: {
33083        id: 'source-files',
33084        autoCreate:true,
33085        title:'Ext Source Files',
33086        autoScroll:true,
33087        fitToFrame:true
33088    },
33089    center : {
33090        el: cview,
33091        autoScroll:true,
33092        fitToFrame:true,
33093        toolbar: tb,
33094        resizeEl:'cbody'
33095    }
33096 });
33097 </code></pre>
33098      * @param {Object} regions An object containing ContentPanel configs by region name
33099      */
33100     batchAdd : function(regions){
33101         this.beginUpdate();
33102         for(var rname in regions){
33103             var lr = this.regions[rname];
33104             if(lr){
33105                 this.addTypedPanels(lr, regions[rname]);
33106             }
33107         }
33108         this.endUpdate();
33109     },
33110
33111     // private
33112     addTypedPanels : function(lr, ps){
33113         if(typeof ps == 'string'){
33114             lr.add(new Roo.ContentPanel(ps));
33115         }
33116         else if(ps instanceof Array){
33117             for(var i =0, len = ps.length; i < len; i++){
33118                 this.addTypedPanels(lr, ps[i]);
33119             }
33120         }
33121         else if(!ps.events){ // raw config?
33122             var el = ps.el;
33123             delete ps.el; // prevent conflict
33124             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
33125         }
33126         else {  // panel object assumed!
33127             lr.add(ps);
33128         }
33129     },
33130     /**
33131      * Adds a xtype elements to the layout.
33132      * <pre><code>
33133
33134 layout.addxtype({
33135        xtype : 'ContentPanel',
33136        region: 'west',
33137        items: [ .... ]
33138    }
33139 );
33140
33141 layout.addxtype({
33142         xtype : 'NestedLayoutPanel',
33143         region: 'west',
33144         layout: {
33145            center: { },
33146            west: { }   
33147         },
33148         items : [ ... list of content panels or nested layout panels.. ]
33149    }
33150 );
33151 </code></pre>
33152      * @param {Object} cfg Xtype definition of item to add.
33153      */
33154     addxtype : function(cfg)
33155     {
33156         // basically accepts a pannel...
33157         // can accept a layout region..!?!?
33158         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33159         
33160         if (!cfg.xtype.match(/Panel$/)) {
33161             return false;
33162         }
33163         var ret = false;
33164         
33165         if (typeof(cfg.region) == 'undefined') {
33166             Roo.log("Failed to add Panel, region was not set");
33167             Roo.log(cfg);
33168             return false;
33169         }
33170         var region = cfg.region;
33171         delete cfg.region;
33172         
33173           
33174         var xitems = [];
33175         if (cfg.items) {
33176             xitems = cfg.items;
33177             delete cfg.items;
33178         }
33179         var nb = false;
33180         
33181         switch(cfg.xtype) 
33182         {
33183             case 'ContentPanel':  // ContentPanel (el, cfg)
33184             case 'ScrollPanel':  // ContentPanel (el, cfg)
33185             case 'ViewPanel': 
33186                 if(cfg.autoCreate) {
33187                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33188                 } else {
33189                     var el = this.el.createChild();
33190                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33191                 }
33192                 
33193                 this.add(region, ret);
33194                 break;
33195             
33196             
33197             case 'TreePanel': // our new panel!
33198                 cfg.el = this.el.createChild();
33199                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33200                 this.add(region, ret);
33201                 break;
33202             
33203             case 'NestedLayoutPanel': 
33204                 // create a new Layout (which is  a Border Layout...
33205                 var el = this.el.createChild();
33206                 var clayout = cfg.layout;
33207                 delete cfg.layout;
33208                 clayout.items   = clayout.items  || [];
33209                 // replace this exitems with the clayout ones..
33210                 xitems = clayout.items;
33211                  
33212                 
33213                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33214                     cfg.background = false;
33215                 }
33216                 var layout = new Roo.BorderLayout(el, clayout);
33217                 
33218                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
33219                 //console.log('adding nested layout panel '  + cfg.toSource());
33220                 this.add(region, ret);
33221                 nb = {}; /// find first...
33222                 break;
33223                 
33224             case 'GridPanel': 
33225             
33226                 // needs grid and region
33227                 
33228                 //var el = this.getRegion(region).el.createChild();
33229                 var el = this.el.createChild();
33230                 // create the grid first...
33231                 
33232                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
33233                 delete cfg.grid;
33234                 if (region == 'center' && this.active ) {
33235                     cfg.background = false;
33236                 }
33237                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
33238                 
33239                 this.add(region, ret);
33240                 if (cfg.background) {
33241                     ret.on('activate', function(gp) {
33242                         if (!gp.grid.rendered) {
33243                             gp.grid.render();
33244                         }
33245                     });
33246                 } else {
33247                     grid.render();
33248                 }
33249                 break;
33250            
33251            
33252            
33253                 
33254                 
33255                 
33256             default:
33257                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33258                     
33259                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33260                     this.add(region, ret);
33261                 } else {
33262                 
33263                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
33264                     return null;
33265                 }
33266                 
33267              // GridPanel (grid, cfg)
33268             
33269         }
33270         this.beginUpdate();
33271         // add children..
33272         var region = '';
33273         var abn = {};
33274         Roo.each(xitems, function(i)  {
33275             region = nb && i.region ? i.region : false;
33276             
33277             var add = ret.addxtype(i);
33278            
33279             if (region) {
33280                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33281                 if (!i.background) {
33282                     abn[region] = nb[region] ;
33283                 }
33284             }
33285             
33286         });
33287         this.endUpdate();
33288
33289         // make the last non-background panel active..
33290         //if (nb) { Roo.log(abn); }
33291         if (nb) {
33292             
33293             for(var r in abn) {
33294                 region = this.getRegion(r);
33295                 if (region) {
33296                     // tried using nb[r], but it does not work..
33297                      
33298                     region.showPanel(abn[r]);
33299                    
33300                 }
33301             }
33302         }
33303         return ret;
33304         
33305     }
33306 });
33307
33308 /**
33309  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
33310  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
33311  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
33312  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
33313  * <pre><code>
33314 // shorthand
33315 var CP = Roo.ContentPanel;
33316
33317 var layout = Roo.BorderLayout.create({
33318     north: {
33319         initialSize: 25,
33320         titlebar: false,
33321         panels: [new CP("north", "North")]
33322     },
33323     west: {
33324         split:true,
33325         initialSize: 200,
33326         minSize: 175,
33327         maxSize: 400,
33328         titlebar: true,
33329         collapsible: true,
33330         panels: [new CP("west", {title: "West"})]
33331     },
33332     east: {
33333         split:true,
33334         initialSize: 202,
33335         minSize: 175,
33336         maxSize: 400,
33337         titlebar: true,
33338         collapsible: true,
33339         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
33340     },
33341     south: {
33342         split:true,
33343         initialSize: 100,
33344         minSize: 100,
33345         maxSize: 200,
33346         titlebar: true,
33347         collapsible: true,
33348         panels: [new CP("south", {title: "South", closable: true})]
33349     },
33350     center: {
33351         titlebar: true,
33352         autoScroll:true,
33353         resizeTabs: true,
33354         minTabWidth: 50,
33355         preferredTabWidth: 150,
33356         panels: [
33357             new CP("center1", {title: "Close Me", closable: true}),
33358             new CP("center2", {title: "Center Panel", closable: false})
33359         ]
33360     }
33361 }, document.body);
33362
33363 layout.getRegion("center").showPanel("center1");
33364 </code></pre>
33365  * @param config
33366  * @param targetEl
33367  */
33368 Roo.BorderLayout.create = function(config, targetEl){
33369     var layout = new Roo.BorderLayout(targetEl || document.body, config);
33370     layout.beginUpdate();
33371     var regions = Roo.BorderLayout.RegionFactory.validRegions;
33372     for(var j = 0, jlen = regions.length; j < jlen; j++){
33373         var lr = regions[j];
33374         if(layout.regions[lr] && config[lr].panels){
33375             var r = layout.regions[lr];
33376             var ps = config[lr].panels;
33377             layout.addTypedPanels(r, ps);
33378         }
33379     }
33380     layout.endUpdate();
33381     return layout;
33382 };
33383
33384 // private
33385 Roo.BorderLayout.RegionFactory = {
33386     // private
33387     validRegions : ["north","south","east","west","center"],
33388
33389     // private
33390     create : function(target, mgr, config){
33391         target = target.toLowerCase();
33392         if(config.lightweight || config.basic){
33393             return new Roo.BasicLayoutRegion(mgr, config, target);
33394         }
33395         switch(target){
33396             case "north":
33397                 return new Roo.NorthLayoutRegion(mgr, config);
33398             case "south":
33399                 return new Roo.SouthLayoutRegion(mgr, config);
33400             case "east":
33401                 return new Roo.EastLayoutRegion(mgr, config);
33402             case "west":
33403                 return new Roo.WestLayoutRegion(mgr, config);
33404             case "center":
33405                 return new Roo.CenterLayoutRegion(mgr, config);
33406         }
33407         throw 'Layout region "'+target+'" not supported.';
33408     }
33409 };/*
33410  * Based on:
33411  * Ext JS Library 1.1.1
33412  * Copyright(c) 2006-2007, Ext JS, LLC.
33413  *
33414  * Originally Released Under LGPL - original licence link has changed is not relivant.
33415  *
33416  * Fork - LGPL
33417  * <script type="text/javascript">
33418  */
33419  
33420 /**
33421  * @class Roo.BasicLayoutRegion
33422  * @extends Roo.util.Observable
33423  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33424  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33425  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33426  */
33427 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
33428     this.mgr = mgr;
33429     this.position  = pos;
33430     this.events = {
33431         /**
33432          * @scope Roo.BasicLayoutRegion
33433          */
33434         
33435         /**
33436          * @event beforeremove
33437          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33438          * @param {Roo.LayoutRegion} this
33439          * @param {Roo.ContentPanel} panel The panel
33440          * @param {Object} e The cancel event object
33441          */
33442         "beforeremove" : true,
33443         /**
33444          * @event invalidated
33445          * Fires when the layout for this region is changed.
33446          * @param {Roo.LayoutRegion} this
33447          */
33448         "invalidated" : true,
33449         /**
33450          * @event visibilitychange
33451          * Fires when this region is shown or hidden 
33452          * @param {Roo.LayoutRegion} this
33453          * @param {Boolean} visibility true or false
33454          */
33455         "visibilitychange" : true,
33456         /**
33457          * @event paneladded
33458          * Fires when a panel is added. 
33459          * @param {Roo.LayoutRegion} this
33460          * @param {Roo.ContentPanel} panel The panel
33461          */
33462         "paneladded" : true,
33463         /**
33464          * @event panelremoved
33465          * Fires when a panel is removed. 
33466          * @param {Roo.LayoutRegion} this
33467          * @param {Roo.ContentPanel} panel The panel
33468          */
33469         "panelremoved" : true,
33470         /**
33471          * @event collapsed
33472          * Fires when this region is collapsed.
33473          * @param {Roo.LayoutRegion} this
33474          */
33475         "collapsed" : true,
33476         /**
33477          * @event expanded
33478          * Fires when this region is expanded.
33479          * @param {Roo.LayoutRegion} this
33480          */
33481         "expanded" : true,
33482         /**
33483          * @event slideshow
33484          * Fires when this region is slid into view.
33485          * @param {Roo.LayoutRegion} this
33486          */
33487         "slideshow" : true,
33488         /**
33489          * @event slidehide
33490          * Fires when this region slides out of view. 
33491          * @param {Roo.LayoutRegion} this
33492          */
33493         "slidehide" : true,
33494         /**
33495          * @event panelactivated
33496          * Fires when a panel is activated. 
33497          * @param {Roo.LayoutRegion} this
33498          * @param {Roo.ContentPanel} panel The activated panel
33499          */
33500         "panelactivated" : true,
33501         /**
33502          * @event resized
33503          * Fires when the user resizes this region. 
33504          * @param {Roo.LayoutRegion} this
33505          * @param {Number} newSize The new size (width for east/west, height for north/south)
33506          */
33507         "resized" : true
33508     };
33509     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33510     this.panels = new Roo.util.MixedCollection();
33511     this.panels.getKey = this.getPanelId.createDelegate(this);
33512     this.box = null;
33513     this.activePanel = null;
33514     // ensure listeners are added...
33515     
33516     if (config.listeners || config.events) {
33517         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
33518             listeners : config.listeners || {},
33519             events : config.events || {}
33520         });
33521     }
33522     
33523     if(skipConfig !== true){
33524         this.applyConfig(config);
33525     }
33526 };
33527
33528 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
33529     getPanelId : function(p){
33530         return p.getId();
33531     },
33532     
33533     applyConfig : function(config){
33534         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33535         this.config = config;
33536         
33537     },
33538     
33539     /**
33540      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33541      * the width, for horizontal (north, south) the height.
33542      * @param {Number} newSize The new width or height
33543      */
33544     resizeTo : function(newSize){
33545         var el = this.el ? this.el :
33546                  (this.activePanel ? this.activePanel.getEl() : null);
33547         if(el){
33548             switch(this.position){
33549                 case "east":
33550                 case "west":
33551                     el.setWidth(newSize);
33552                     this.fireEvent("resized", this, newSize);
33553                 break;
33554                 case "north":
33555                 case "south":
33556                     el.setHeight(newSize);
33557                     this.fireEvent("resized", this, newSize);
33558                 break;                
33559             }
33560         }
33561     },
33562     
33563     getBox : function(){
33564         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33565     },
33566     
33567     getMargins : function(){
33568         return this.margins;
33569     },
33570     
33571     updateBox : function(box){
33572         this.box = box;
33573         var el = this.activePanel.getEl();
33574         el.dom.style.left = box.x + "px";
33575         el.dom.style.top = box.y + "px";
33576         this.activePanel.setSize(box.width, box.height);
33577     },
33578     
33579     /**
33580      * Returns the container element for this region.
33581      * @return {Roo.Element}
33582      */
33583     getEl : function(){
33584         return this.activePanel;
33585     },
33586     
33587     /**
33588      * Returns true if this region is currently visible.
33589      * @return {Boolean}
33590      */
33591     isVisible : function(){
33592         return this.activePanel ? true : false;
33593     },
33594     
33595     setActivePanel : function(panel){
33596         panel = this.getPanel(panel);
33597         if(this.activePanel && this.activePanel != panel){
33598             this.activePanel.setActiveState(false);
33599             this.activePanel.getEl().setLeftTop(-10000,-10000);
33600         }
33601         this.activePanel = panel;
33602         panel.setActiveState(true);
33603         if(this.box){
33604             panel.setSize(this.box.width, this.box.height);
33605         }
33606         this.fireEvent("panelactivated", this, panel);
33607         this.fireEvent("invalidated");
33608     },
33609     
33610     /**
33611      * Show the specified panel.
33612      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33613      * @return {Roo.ContentPanel} The shown panel or null
33614      */
33615     showPanel : function(panel){
33616         if(panel = this.getPanel(panel)){
33617             this.setActivePanel(panel);
33618         }
33619         return panel;
33620     },
33621     
33622     /**
33623      * Get the active panel for this region.
33624      * @return {Roo.ContentPanel} The active panel or null
33625      */
33626     getActivePanel : function(){
33627         return this.activePanel;
33628     },
33629     
33630     /**
33631      * Add the passed ContentPanel(s)
33632      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33633      * @return {Roo.ContentPanel} The panel added (if only one was added)
33634      */
33635     add : function(panel){
33636         if(arguments.length > 1){
33637             for(var i = 0, len = arguments.length; i < len; i++) {
33638                 this.add(arguments[i]);
33639             }
33640             return null;
33641         }
33642         if(this.hasPanel(panel)){
33643             this.showPanel(panel);
33644             return panel;
33645         }
33646         var el = panel.getEl();
33647         if(el.dom.parentNode != this.mgr.el.dom){
33648             this.mgr.el.dom.appendChild(el.dom);
33649         }
33650         if(panel.setRegion){
33651             panel.setRegion(this);
33652         }
33653         this.panels.add(panel);
33654         el.setStyle("position", "absolute");
33655         if(!panel.background){
33656             this.setActivePanel(panel);
33657             if(this.config.initialSize && this.panels.getCount()==1){
33658                 this.resizeTo(this.config.initialSize);
33659             }
33660         }
33661         this.fireEvent("paneladded", this, panel);
33662         return panel;
33663     },
33664     
33665     /**
33666      * Returns true if the panel is in this region.
33667      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33668      * @return {Boolean}
33669      */
33670     hasPanel : function(panel){
33671         if(typeof panel == "object"){ // must be panel obj
33672             panel = panel.getId();
33673         }
33674         return this.getPanel(panel) ? true : false;
33675     },
33676     
33677     /**
33678      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33679      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33680      * @param {Boolean} preservePanel Overrides the config preservePanel option
33681      * @return {Roo.ContentPanel} The panel that was removed
33682      */
33683     remove : function(panel, preservePanel){
33684         panel = this.getPanel(panel);
33685         if(!panel){
33686             return null;
33687         }
33688         var e = {};
33689         this.fireEvent("beforeremove", this, panel, e);
33690         if(e.cancel === true){
33691             return null;
33692         }
33693         var panelId = panel.getId();
33694         this.panels.removeKey(panelId);
33695         return panel;
33696     },
33697     
33698     /**
33699      * Returns the panel specified or null if it's not in this region.
33700      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33701      * @return {Roo.ContentPanel}
33702      */
33703     getPanel : function(id){
33704         if(typeof id == "object"){ // must be panel obj
33705             return id;
33706         }
33707         return this.panels.get(id);
33708     },
33709     
33710     /**
33711      * Returns this regions position (north/south/east/west/center).
33712      * @return {String} 
33713      */
33714     getPosition: function(){
33715         return this.position;    
33716     }
33717 });/*
33718  * Based on:
33719  * Ext JS Library 1.1.1
33720  * Copyright(c) 2006-2007, Ext JS, LLC.
33721  *
33722  * Originally Released Under LGPL - original licence link has changed is not relivant.
33723  *
33724  * Fork - LGPL
33725  * <script type="text/javascript">
33726  */
33727  
33728 /**
33729  * @class Roo.LayoutRegion
33730  * @extends Roo.BasicLayoutRegion
33731  * This class represents a region in a layout manager.
33732  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
33733  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
33734  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
33735  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33736  * @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})
33737  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33738  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
33739  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33740  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33741  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33742  * @cfg {String}    title           The title for the region (overrides panel titles)
33743  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33744  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33745  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33746  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33747  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33748  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33749  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33750  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33751  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33752  * @cfg {Boolean}   showPin         True to show a pin button
33753  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33754  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33755  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33756  * @cfg {Number}    width           For East/West panels
33757  * @cfg {Number}    height          For North/South panels
33758  * @cfg {Boolean}   split           To show the splitter
33759  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33760  */
33761 Roo.LayoutRegion = function(mgr, config, pos){
33762     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
33763     var dh = Roo.DomHelper;
33764     /** This region's container element 
33765     * @type Roo.Element */
33766     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
33767     /** This region's title element 
33768     * @type Roo.Element */
33769
33770     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
33771         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33772         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
33773     ]}, true);
33774     this.titleEl.enableDisplayMode();
33775     /** This region's title text element 
33776     * @type HTMLElement */
33777     this.titleTextEl = this.titleEl.dom.firstChild;
33778     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33779     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
33780     this.closeBtn.enableDisplayMode();
33781     this.closeBtn.on("click", this.closeClicked, this);
33782     this.closeBtn.hide();
33783
33784     this.createBody(config);
33785     this.visible = true;
33786     this.collapsed = false;
33787
33788     if(config.hideWhenEmpty){
33789         this.hide();
33790         this.on("paneladded", this.validateVisibility, this);
33791         this.on("panelremoved", this.validateVisibility, this);
33792     }
33793     this.applyConfig(config);
33794 };
33795
33796 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
33797
33798     createBody : function(){
33799         /** This region's body element 
33800         * @type Roo.Element */
33801         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
33802     },
33803
33804     applyConfig : function(c){
33805         if(c.collapsible && this.position != "center" && !this.collapsedEl){
33806             var dh = Roo.DomHelper;
33807             if(c.titlebar !== false){
33808                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
33809                 this.collapseBtn.on("click", this.collapse, this);
33810                 this.collapseBtn.enableDisplayMode();
33811
33812                 if(c.showPin === true || this.showPin){
33813                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
33814                     this.stickBtn.enableDisplayMode();
33815                     this.stickBtn.on("click", this.expand, this);
33816                     this.stickBtn.hide();
33817                 }
33818             }
33819             /** This region's collapsed element
33820             * @type Roo.Element */
33821             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33822                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33823             ]}, true);
33824             if(c.floatable !== false){
33825                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33826                this.collapsedEl.on("click", this.collapseClick, this);
33827             }
33828
33829             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33830                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33831                    id: "message", unselectable: "on", style:{"float":"left"}});
33832                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33833              }
33834             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33835             this.expandBtn.on("click", this.expand, this);
33836         }
33837         if(this.collapseBtn){
33838             this.collapseBtn.setVisible(c.collapsible == true);
33839         }
33840         this.cmargins = c.cmargins || this.cmargins ||
33841                          (this.position == "west" || this.position == "east" ?
33842                              {top: 0, left: 2, right:2, bottom: 0} :
33843                              {top: 2, left: 0, right:0, bottom: 2});
33844         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33845         this.bottomTabs = c.tabPosition != "top";
33846         this.autoScroll = c.autoScroll || false;
33847         if(this.autoScroll){
33848             this.bodyEl.setStyle("overflow", "auto");
33849         }else{
33850             this.bodyEl.setStyle("overflow", "hidden");
33851         }
33852         //if(c.titlebar !== false){
33853             if((!c.titlebar && !c.title) || c.titlebar === false){
33854                 this.titleEl.hide();
33855             }else{
33856                 this.titleEl.show();
33857                 if(c.title){
33858                     this.titleTextEl.innerHTML = c.title;
33859                 }
33860             }
33861         //}
33862         this.duration = c.duration || .30;
33863         this.slideDuration = c.slideDuration || .45;
33864         this.config = c;
33865         if(c.collapsed){
33866             this.collapse(true);
33867         }
33868         if(c.hidden){
33869             this.hide();
33870         }
33871     },
33872     /**
33873      * Returns true if this region is currently visible.
33874      * @return {Boolean}
33875      */
33876     isVisible : function(){
33877         return this.visible;
33878     },
33879
33880     /**
33881      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33882      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33883      */
33884     setCollapsedTitle : function(title){
33885         title = title || "&#160;";
33886         if(this.collapsedTitleTextEl){
33887             this.collapsedTitleTextEl.innerHTML = title;
33888         }
33889     },
33890
33891     getBox : function(){
33892         var b;
33893         if(!this.collapsed){
33894             b = this.el.getBox(false, true);
33895         }else{
33896             b = this.collapsedEl.getBox(false, true);
33897         }
33898         return b;
33899     },
33900
33901     getMargins : function(){
33902         return this.collapsed ? this.cmargins : this.margins;
33903     },
33904
33905     highlight : function(){
33906         this.el.addClass("x-layout-panel-dragover");
33907     },
33908
33909     unhighlight : function(){
33910         this.el.removeClass("x-layout-panel-dragover");
33911     },
33912
33913     updateBox : function(box){
33914         this.box = box;
33915         if(!this.collapsed){
33916             this.el.dom.style.left = box.x + "px";
33917             this.el.dom.style.top = box.y + "px";
33918             this.updateBody(box.width, box.height);
33919         }else{
33920             this.collapsedEl.dom.style.left = box.x + "px";
33921             this.collapsedEl.dom.style.top = box.y + "px";
33922             this.collapsedEl.setSize(box.width, box.height);
33923         }
33924         if(this.tabs){
33925             this.tabs.autoSizeTabs();
33926         }
33927     },
33928
33929     updateBody : function(w, h){
33930         if(w !== null){
33931             this.el.setWidth(w);
33932             w -= this.el.getBorderWidth("rl");
33933             if(this.config.adjustments){
33934                 w += this.config.adjustments[0];
33935             }
33936         }
33937         if(h !== null){
33938             this.el.setHeight(h);
33939             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33940             h -= this.el.getBorderWidth("tb");
33941             if(this.config.adjustments){
33942                 h += this.config.adjustments[1];
33943             }
33944             this.bodyEl.setHeight(h);
33945             if(this.tabs){
33946                 h = this.tabs.syncHeight(h);
33947             }
33948         }
33949         if(this.panelSize){
33950             w = w !== null ? w : this.panelSize.width;
33951             h = h !== null ? h : this.panelSize.height;
33952         }
33953         if(this.activePanel){
33954             var el = this.activePanel.getEl();
33955             w = w !== null ? w : el.getWidth();
33956             h = h !== null ? h : el.getHeight();
33957             this.panelSize = {width: w, height: h};
33958             this.activePanel.setSize(w, h);
33959         }
33960         if(Roo.isIE && this.tabs){
33961             this.tabs.el.repaint();
33962         }
33963     },
33964
33965     /**
33966      * Returns the container element for this region.
33967      * @return {Roo.Element}
33968      */
33969     getEl : function(){
33970         return this.el;
33971     },
33972
33973     /**
33974      * Hides this region.
33975      */
33976     hide : function(){
33977         if(!this.collapsed){
33978             this.el.dom.style.left = "-2000px";
33979             this.el.hide();
33980         }else{
33981             this.collapsedEl.dom.style.left = "-2000px";
33982             this.collapsedEl.hide();
33983         }
33984         this.visible = false;
33985         this.fireEvent("visibilitychange", this, false);
33986     },
33987
33988     /**
33989      * Shows this region if it was previously hidden.
33990      */
33991     show : function(){
33992         if(!this.collapsed){
33993             this.el.show();
33994         }else{
33995             this.collapsedEl.show();
33996         }
33997         this.visible = true;
33998         this.fireEvent("visibilitychange", this, true);
33999     },
34000
34001     closeClicked : function(){
34002         if(this.activePanel){
34003             this.remove(this.activePanel);
34004         }
34005     },
34006
34007     collapseClick : function(e){
34008         if(this.isSlid){
34009            e.stopPropagation();
34010            this.slideIn();
34011         }else{
34012            e.stopPropagation();
34013            this.slideOut();
34014         }
34015     },
34016
34017     /**
34018      * Collapses this region.
34019      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34020      */
34021     collapse : function(skipAnim){
34022         if(this.collapsed) return;
34023         this.collapsed = true;
34024         if(this.split){
34025             this.split.el.hide();
34026         }
34027         if(this.config.animate && skipAnim !== true){
34028             this.fireEvent("invalidated", this);
34029             this.animateCollapse();
34030         }else{
34031             this.el.setLocation(-20000,-20000);
34032             this.el.hide();
34033             this.collapsedEl.show();
34034             this.fireEvent("collapsed", this);
34035             this.fireEvent("invalidated", this);
34036         }
34037     },
34038
34039     animateCollapse : function(){
34040         // overridden
34041     },
34042
34043     /**
34044      * Expands this region if it was previously collapsed.
34045      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34046      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34047      */
34048     expand : function(e, skipAnim){
34049         if(e) e.stopPropagation();
34050         if(!this.collapsed || this.el.hasActiveFx()) return;
34051         if(this.isSlid){
34052             this.afterSlideIn();
34053             skipAnim = true;
34054         }
34055         this.collapsed = false;
34056         if(this.config.animate && skipAnim !== true){
34057             this.animateExpand();
34058         }else{
34059             this.el.show();
34060             if(this.split){
34061                 this.split.el.show();
34062             }
34063             this.collapsedEl.setLocation(-2000,-2000);
34064             this.collapsedEl.hide();
34065             this.fireEvent("invalidated", this);
34066             this.fireEvent("expanded", this);
34067         }
34068     },
34069
34070     animateExpand : function(){
34071         // overridden
34072     },
34073
34074     initTabs : function()
34075     {
34076         this.bodyEl.setStyle("overflow", "hidden");
34077         var ts = new Roo.TabPanel(
34078                 this.bodyEl.dom,
34079                 {
34080                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
34081                     disableTooltips: this.config.disableTabTips,
34082                     toolbar : this.config.toolbar
34083                 }
34084         );
34085         if(this.config.hideTabs){
34086             ts.stripWrap.setDisplayed(false);
34087         }
34088         this.tabs = ts;
34089         ts.resizeTabs = this.config.resizeTabs === true;
34090         ts.minTabWidth = this.config.minTabWidth || 40;
34091         ts.maxTabWidth = this.config.maxTabWidth || 250;
34092         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34093         ts.monitorResize = false;
34094         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34095         ts.bodyEl.addClass('x-layout-tabs-body');
34096         this.panels.each(this.initPanelAsTab, this);
34097     },
34098
34099     initPanelAsTab : function(panel){
34100         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
34101                     this.config.closeOnTab && panel.isClosable());
34102         if(panel.tabTip !== undefined){
34103             ti.setTooltip(panel.tabTip);
34104         }
34105         ti.on("activate", function(){
34106               this.setActivePanel(panel);
34107         }, this);
34108         if(this.config.closeOnTab){
34109             ti.on("beforeclose", function(t, e){
34110                 e.cancel = true;
34111                 this.remove(panel);
34112             }, this);
34113         }
34114         return ti;
34115     },
34116
34117     updatePanelTitle : function(panel, title){
34118         if(this.activePanel == panel){
34119             this.updateTitle(title);
34120         }
34121         if(this.tabs){
34122             var ti = this.tabs.getTab(panel.getEl().id);
34123             ti.setText(title);
34124             if(panel.tabTip !== undefined){
34125                 ti.setTooltip(panel.tabTip);
34126             }
34127         }
34128     },
34129
34130     updateTitle : function(title){
34131         if(this.titleTextEl && !this.config.title){
34132             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34133         }
34134     },
34135
34136     setActivePanel : function(panel){
34137         panel = this.getPanel(panel);
34138         if(this.activePanel && this.activePanel != panel){
34139             this.activePanel.setActiveState(false);
34140         }
34141         this.activePanel = panel;
34142         panel.setActiveState(true);
34143         if(this.panelSize){
34144             panel.setSize(this.panelSize.width, this.panelSize.height);
34145         }
34146         if(this.closeBtn){
34147             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34148         }
34149         this.updateTitle(panel.getTitle());
34150         if(this.tabs){
34151             this.fireEvent("invalidated", this);
34152         }
34153         this.fireEvent("panelactivated", this, panel);
34154     },
34155
34156     /**
34157      * Shows the specified panel.
34158      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34159      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34160      */
34161     showPanel : function(panel)
34162     {
34163         panel = this.getPanel(panel);
34164         if(panel){
34165             if(this.tabs){
34166                 var tab = this.tabs.getTab(panel.getEl().id);
34167                 if(tab.isHidden()){
34168                     this.tabs.unhideTab(tab.id);
34169                 }
34170                 tab.activate();
34171             }else{
34172                 this.setActivePanel(panel);
34173             }
34174         }
34175         return panel;
34176     },
34177
34178     /**
34179      * Get the active panel for this region.
34180      * @return {Roo.ContentPanel} The active panel or null
34181      */
34182     getActivePanel : function(){
34183         return this.activePanel;
34184     },
34185
34186     validateVisibility : function(){
34187         if(this.panels.getCount() < 1){
34188             this.updateTitle("&#160;");
34189             this.closeBtn.hide();
34190             this.hide();
34191         }else{
34192             if(!this.isVisible()){
34193                 this.show();
34194             }
34195         }
34196     },
34197
34198     /**
34199      * Adds the passed ContentPanel(s) to this region.
34200      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34201      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34202      */
34203     add : function(panel){
34204         if(arguments.length > 1){
34205             for(var i = 0, len = arguments.length; i < len; i++) {
34206                 this.add(arguments[i]);
34207             }
34208             return null;
34209         }
34210         if(this.hasPanel(panel)){
34211             this.showPanel(panel);
34212             return panel;
34213         }
34214         panel.setRegion(this);
34215         this.panels.add(panel);
34216         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34217             this.bodyEl.dom.appendChild(panel.getEl().dom);
34218             if(panel.background !== true){
34219                 this.setActivePanel(panel);
34220             }
34221             this.fireEvent("paneladded", this, panel);
34222             return panel;
34223         }
34224         if(!this.tabs){
34225             this.initTabs();
34226         }else{
34227             this.initPanelAsTab(panel);
34228         }
34229         if(panel.background !== true){
34230             this.tabs.activate(panel.getEl().id);
34231         }
34232         this.fireEvent("paneladded", this, panel);
34233         return panel;
34234     },
34235
34236     /**
34237      * Hides the tab for the specified panel.
34238      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34239      */
34240     hidePanel : function(panel){
34241         if(this.tabs && (panel = this.getPanel(panel))){
34242             this.tabs.hideTab(panel.getEl().id);
34243         }
34244     },
34245
34246     /**
34247      * Unhides the tab for a previously hidden panel.
34248      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34249      */
34250     unhidePanel : function(panel){
34251         if(this.tabs && (panel = this.getPanel(panel))){
34252             this.tabs.unhideTab(panel.getEl().id);
34253         }
34254     },
34255
34256     clearPanels : function(){
34257         while(this.panels.getCount() > 0){
34258              this.remove(this.panels.first());
34259         }
34260     },
34261
34262     /**
34263      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34264      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34265      * @param {Boolean} preservePanel Overrides the config preservePanel option
34266      * @return {Roo.ContentPanel} The panel that was removed
34267      */
34268     remove : function(panel, preservePanel){
34269         panel = this.getPanel(panel);
34270         if(!panel){
34271             return null;
34272         }
34273         var e = {};
34274         this.fireEvent("beforeremove", this, panel, e);
34275         if(e.cancel === true){
34276             return null;
34277         }
34278         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34279         var panelId = panel.getId();
34280         this.panels.removeKey(panelId);
34281         if(preservePanel){
34282             document.body.appendChild(panel.getEl().dom);
34283         }
34284         if(this.tabs){
34285             this.tabs.removeTab(panel.getEl().id);
34286         }else if (!preservePanel){
34287             this.bodyEl.dom.removeChild(panel.getEl().dom);
34288         }
34289         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34290             var p = this.panels.first();
34291             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34292             tempEl.appendChild(p.getEl().dom);
34293             this.bodyEl.update("");
34294             this.bodyEl.dom.appendChild(p.getEl().dom);
34295             tempEl = null;
34296             this.updateTitle(p.getTitle());
34297             this.tabs = null;
34298             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34299             this.setActivePanel(p);
34300         }
34301         panel.setRegion(null);
34302         if(this.activePanel == panel){
34303             this.activePanel = null;
34304         }
34305         if(this.config.autoDestroy !== false && preservePanel !== true){
34306             try{panel.destroy();}catch(e){}
34307         }
34308         this.fireEvent("panelremoved", this, panel);
34309         return panel;
34310     },
34311
34312     /**
34313      * Returns the TabPanel component used by this region
34314      * @return {Roo.TabPanel}
34315      */
34316     getTabs : function(){
34317         return this.tabs;
34318     },
34319
34320     createTool : function(parentEl, className){
34321         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
34322             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
34323         btn.addClassOnOver("x-layout-tools-button-over");
34324         return btn;
34325     }
34326 });/*
34327  * Based on:
34328  * Ext JS Library 1.1.1
34329  * Copyright(c) 2006-2007, Ext JS, LLC.
34330  *
34331  * Originally Released Under LGPL - original licence link has changed is not relivant.
34332  *
34333  * Fork - LGPL
34334  * <script type="text/javascript">
34335  */
34336  
34337
34338
34339 /**
34340  * @class Roo.SplitLayoutRegion
34341  * @extends Roo.LayoutRegion
34342  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34343  */
34344 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
34345     this.cursor = cursor;
34346     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
34347 };
34348
34349 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
34350     splitTip : "Drag to resize.",
34351     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34352     useSplitTips : false,
34353
34354     applyConfig : function(config){
34355         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
34356         if(config.split){
34357             if(!this.split){
34358                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
34359                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
34360                 /** The SplitBar for this region 
34361                 * @type Roo.SplitBar */
34362                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
34363                 this.split.on("moved", this.onSplitMove, this);
34364                 this.split.useShim = config.useShim === true;
34365                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34366                 if(this.useSplitTips){
34367                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34368                 }
34369                 if(config.collapsible){
34370                     this.split.el.on("dblclick", this.collapse,  this);
34371                 }
34372             }
34373             if(typeof config.minSize != "undefined"){
34374                 this.split.minSize = config.minSize;
34375             }
34376             if(typeof config.maxSize != "undefined"){
34377                 this.split.maxSize = config.maxSize;
34378             }
34379             if(config.hideWhenEmpty || config.hidden || config.collapsed){
34380                 this.hideSplitter();
34381             }
34382         }
34383     },
34384
34385     getHMaxSize : function(){
34386          var cmax = this.config.maxSize || 10000;
34387          var center = this.mgr.getRegion("center");
34388          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34389     },
34390
34391     getVMaxSize : function(){
34392          var cmax = this.config.maxSize || 10000;
34393          var center = this.mgr.getRegion("center");
34394          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34395     },
34396
34397     onSplitMove : function(split, newSize){
34398         this.fireEvent("resized", this, newSize);
34399     },
34400     
34401     /** 
34402      * Returns the {@link Roo.SplitBar} for this region.
34403      * @return {Roo.SplitBar}
34404      */
34405     getSplitBar : function(){
34406         return this.split;
34407     },
34408     
34409     hide : function(){
34410         this.hideSplitter();
34411         Roo.SplitLayoutRegion.superclass.hide.call(this);
34412     },
34413
34414     hideSplitter : function(){
34415         if(this.split){
34416             this.split.el.setLocation(-2000,-2000);
34417             this.split.el.hide();
34418         }
34419     },
34420
34421     show : function(){
34422         if(this.split){
34423             this.split.el.show();
34424         }
34425         Roo.SplitLayoutRegion.superclass.show.call(this);
34426     },
34427     
34428     beforeSlide: function(){
34429         if(Roo.isGecko){// firefox overflow auto bug workaround
34430             this.bodyEl.clip();
34431             if(this.tabs) this.tabs.bodyEl.clip();
34432             if(this.activePanel){
34433                 this.activePanel.getEl().clip();
34434                 
34435                 if(this.activePanel.beforeSlide){
34436                     this.activePanel.beforeSlide();
34437                 }
34438             }
34439         }
34440     },
34441     
34442     afterSlide : function(){
34443         if(Roo.isGecko){// firefox overflow auto bug workaround
34444             this.bodyEl.unclip();
34445             if(this.tabs) this.tabs.bodyEl.unclip();
34446             if(this.activePanel){
34447                 this.activePanel.getEl().unclip();
34448                 if(this.activePanel.afterSlide){
34449                     this.activePanel.afterSlide();
34450                 }
34451             }
34452         }
34453     },
34454
34455     initAutoHide : function(){
34456         if(this.autoHide !== false){
34457             if(!this.autoHideHd){
34458                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34459                 this.autoHideHd = {
34460                     "mouseout": function(e){
34461                         if(!e.within(this.el, true)){
34462                             st.delay(500);
34463                         }
34464                     },
34465                     "mouseover" : function(e){
34466                         st.cancel();
34467                     },
34468                     scope : this
34469                 };
34470             }
34471             this.el.on(this.autoHideHd);
34472         }
34473     },
34474
34475     clearAutoHide : function(){
34476         if(this.autoHide !== false){
34477             this.el.un("mouseout", this.autoHideHd.mouseout);
34478             this.el.un("mouseover", this.autoHideHd.mouseover);
34479         }
34480     },
34481
34482     clearMonitor : function(){
34483         Roo.get(document).un("click", this.slideInIf, this);
34484     },
34485
34486     // these names are backwards but not changed for compat
34487     slideOut : function(){
34488         if(this.isSlid || this.el.hasActiveFx()){
34489             return;
34490         }
34491         this.isSlid = true;
34492         if(this.collapseBtn){
34493             this.collapseBtn.hide();
34494         }
34495         this.closeBtnState = this.closeBtn.getStyle('display');
34496         this.closeBtn.hide();
34497         if(this.stickBtn){
34498             this.stickBtn.show();
34499         }
34500         this.el.show();
34501         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34502         this.beforeSlide();
34503         this.el.setStyle("z-index", 10001);
34504         this.el.slideIn(this.getSlideAnchor(), {
34505             callback: function(){
34506                 this.afterSlide();
34507                 this.initAutoHide();
34508                 Roo.get(document).on("click", this.slideInIf, this);
34509                 this.fireEvent("slideshow", this);
34510             },
34511             scope: this,
34512             block: true
34513         });
34514     },
34515
34516     afterSlideIn : function(){
34517         this.clearAutoHide();
34518         this.isSlid = false;
34519         this.clearMonitor();
34520         this.el.setStyle("z-index", "");
34521         if(this.collapseBtn){
34522             this.collapseBtn.show();
34523         }
34524         this.closeBtn.setStyle('display', this.closeBtnState);
34525         if(this.stickBtn){
34526             this.stickBtn.hide();
34527         }
34528         this.fireEvent("slidehide", this);
34529     },
34530
34531     slideIn : function(cb){
34532         if(!this.isSlid || this.el.hasActiveFx()){
34533             Roo.callback(cb);
34534             return;
34535         }
34536         this.isSlid = false;
34537         this.beforeSlide();
34538         this.el.slideOut(this.getSlideAnchor(), {
34539             callback: function(){
34540                 this.el.setLeftTop(-10000, -10000);
34541                 this.afterSlide();
34542                 this.afterSlideIn();
34543                 Roo.callback(cb);
34544             },
34545             scope: this,
34546             block: true
34547         });
34548     },
34549     
34550     slideInIf : function(e){
34551         if(!e.within(this.el)){
34552             this.slideIn();
34553         }
34554     },
34555
34556     animateCollapse : function(){
34557         this.beforeSlide();
34558         this.el.setStyle("z-index", 20000);
34559         var anchor = this.getSlideAnchor();
34560         this.el.slideOut(anchor, {
34561             callback : function(){
34562                 this.el.setStyle("z-index", "");
34563                 this.collapsedEl.slideIn(anchor, {duration:.3});
34564                 this.afterSlide();
34565                 this.el.setLocation(-10000,-10000);
34566                 this.el.hide();
34567                 this.fireEvent("collapsed", this);
34568             },
34569             scope: this,
34570             block: true
34571         });
34572     },
34573
34574     animateExpand : function(){
34575         this.beforeSlide();
34576         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34577         this.el.setStyle("z-index", 20000);
34578         this.collapsedEl.hide({
34579             duration:.1
34580         });
34581         this.el.slideIn(this.getSlideAnchor(), {
34582             callback : function(){
34583                 this.el.setStyle("z-index", "");
34584                 this.afterSlide();
34585                 if(this.split){
34586                     this.split.el.show();
34587                 }
34588                 this.fireEvent("invalidated", this);
34589                 this.fireEvent("expanded", this);
34590             },
34591             scope: this,
34592             block: true
34593         });
34594     },
34595
34596     anchors : {
34597         "west" : "left",
34598         "east" : "right",
34599         "north" : "top",
34600         "south" : "bottom"
34601     },
34602
34603     sanchors : {
34604         "west" : "l",
34605         "east" : "r",
34606         "north" : "t",
34607         "south" : "b"
34608     },
34609
34610     canchors : {
34611         "west" : "tl-tr",
34612         "east" : "tr-tl",
34613         "north" : "tl-bl",
34614         "south" : "bl-tl"
34615     },
34616
34617     getAnchor : function(){
34618         return this.anchors[this.position];
34619     },
34620
34621     getCollapseAnchor : function(){
34622         return this.canchors[this.position];
34623     },
34624
34625     getSlideAnchor : function(){
34626         return this.sanchors[this.position];
34627     },
34628
34629     getAlignAdj : function(){
34630         var cm = this.cmargins;
34631         switch(this.position){
34632             case "west":
34633                 return [0, 0];
34634             break;
34635             case "east":
34636                 return [0, 0];
34637             break;
34638             case "north":
34639                 return [0, 0];
34640             break;
34641             case "south":
34642                 return [0, 0];
34643             break;
34644         }
34645     },
34646
34647     getExpandAdj : function(){
34648         var c = this.collapsedEl, cm = this.cmargins;
34649         switch(this.position){
34650             case "west":
34651                 return [-(cm.right+c.getWidth()+cm.left), 0];
34652             break;
34653             case "east":
34654                 return [cm.right+c.getWidth()+cm.left, 0];
34655             break;
34656             case "north":
34657                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34658             break;
34659             case "south":
34660                 return [0, cm.top+cm.bottom+c.getHeight()];
34661             break;
34662         }
34663     }
34664 });/*
34665  * Based on:
34666  * Ext JS Library 1.1.1
34667  * Copyright(c) 2006-2007, Ext JS, LLC.
34668  *
34669  * Originally Released Under LGPL - original licence link has changed is not relivant.
34670  *
34671  * Fork - LGPL
34672  * <script type="text/javascript">
34673  */
34674 /*
34675  * These classes are private internal classes
34676  */
34677 Roo.CenterLayoutRegion = function(mgr, config){
34678     Roo.LayoutRegion.call(this, mgr, config, "center");
34679     this.visible = true;
34680     this.minWidth = config.minWidth || 20;
34681     this.minHeight = config.minHeight || 20;
34682 };
34683
34684 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
34685     hide : function(){
34686         // center panel can't be hidden
34687     },
34688     
34689     show : function(){
34690         // center panel can't be hidden
34691     },
34692     
34693     getMinWidth: function(){
34694         return this.minWidth;
34695     },
34696     
34697     getMinHeight: function(){
34698         return this.minHeight;
34699     }
34700 });
34701
34702
34703 Roo.NorthLayoutRegion = function(mgr, config){
34704     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
34705     if(this.split){
34706         this.split.placement = Roo.SplitBar.TOP;
34707         this.split.orientation = Roo.SplitBar.VERTICAL;
34708         this.split.el.addClass("x-layout-split-v");
34709     }
34710     var size = config.initialSize || config.height;
34711     if(typeof size != "undefined"){
34712         this.el.setHeight(size);
34713     }
34714 };
34715 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
34716     orientation: Roo.SplitBar.VERTICAL,
34717     getBox : function(){
34718         if(this.collapsed){
34719             return this.collapsedEl.getBox();
34720         }
34721         var box = this.el.getBox();
34722         if(this.split){
34723             box.height += this.split.el.getHeight();
34724         }
34725         return box;
34726     },
34727     
34728     updateBox : function(box){
34729         if(this.split && !this.collapsed){
34730             box.height -= this.split.el.getHeight();
34731             this.split.el.setLeft(box.x);
34732             this.split.el.setTop(box.y+box.height);
34733             this.split.el.setWidth(box.width);
34734         }
34735         if(this.collapsed){
34736             this.updateBody(box.width, null);
34737         }
34738         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34739     }
34740 });
34741
34742 Roo.SouthLayoutRegion = function(mgr, config){
34743     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
34744     if(this.split){
34745         this.split.placement = Roo.SplitBar.BOTTOM;
34746         this.split.orientation = Roo.SplitBar.VERTICAL;
34747         this.split.el.addClass("x-layout-split-v");
34748     }
34749     var size = config.initialSize || config.height;
34750     if(typeof size != "undefined"){
34751         this.el.setHeight(size);
34752     }
34753 };
34754 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
34755     orientation: Roo.SplitBar.VERTICAL,
34756     getBox : function(){
34757         if(this.collapsed){
34758             return this.collapsedEl.getBox();
34759         }
34760         var box = this.el.getBox();
34761         if(this.split){
34762             var sh = this.split.el.getHeight();
34763             box.height += sh;
34764             box.y -= sh;
34765         }
34766         return box;
34767     },
34768     
34769     updateBox : function(box){
34770         if(this.split && !this.collapsed){
34771             var sh = this.split.el.getHeight();
34772             box.height -= sh;
34773             box.y += sh;
34774             this.split.el.setLeft(box.x);
34775             this.split.el.setTop(box.y-sh);
34776             this.split.el.setWidth(box.width);
34777         }
34778         if(this.collapsed){
34779             this.updateBody(box.width, null);
34780         }
34781         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34782     }
34783 });
34784
34785 Roo.EastLayoutRegion = function(mgr, config){
34786     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
34787     if(this.split){
34788         this.split.placement = Roo.SplitBar.RIGHT;
34789         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34790         this.split.el.addClass("x-layout-split-h");
34791     }
34792     var size = config.initialSize || config.width;
34793     if(typeof size != "undefined"){
34794         this.el.setWidth(size);
34795     }
34796 };
34797 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
34798     orientation: Roo.SplitBar.HORIZONTAL,
34799     getBox : function(){
34800         if(this.collapsed){
34801             return this.collapsedEl.getBox();
34802         }
34803         var box = this.el.getBox();
34804         if(this.split){
34805             var sw = this.split.el.getWidth();
34806             box.width += sw;
34807             box.x -= sw;
34808         }
34809         return box;
34810     },
34811
34812     updateBox : function(box){
34813         if(this.split && !this.collapsed){
34814             var sw = this.split.el.getWidth();
34815             box.width -= sw;
34816             this.split.el.setLeft(box.x);
34817             this.split.el.setTop(box.y);
34818             this.split.el.setHeight(box.height);
34819             box.x += sw;
34820         }
34821         if(this.collapsed){
34822             this.updateBody(null, box.height);
34823         }
34824         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34825     }
34826 });
34827
34828 Roo.WestLayoutRegion = function(mgr, config){
34829     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
34830     if(this.split){
34831         this.split.placement = Roo.SplitBar.LEFT;
34832         this.split.orientation = Roo.SplitBar.HORIZONTAL;
34833         this.split.el.addClass("x-layout-split-h");
34834     }
34835     var size = config.initialSize || config.width;
34836     if(typeof size != "undefined"){
34837         this.el.setWidth(size);
34838     }
34839 };
34840 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
34841     orientation: Roo.SplitBar.HORIZONTAL,
34842     getBox : function(){
34843         if(this.collapsed){
34844             return this.collapsedEl.getBox();
34845         }
34846         var box = this.el.getBox();
34847         if(this.split){
34848             box.width += this.split.el.getWidth();
34849         }
34850         return box;
34851     },
34852     
34853     updateBox : function(box){
34854         if(this.split && !this.collapsed){
34855             var sw = this.split.el.getWidth();
34856             box.width -= sw;
34857             this.split.el.setLeft(box.x+box.width);
34858             this.split.el.setTop(box.y);
34859             this.split.el.setHeight(box.height);
34860         }
34861         if(this.collapsed){
34862             this.updateBody(null, box.height);
34863         }
34864         Roo.LayoutRegion.prototype.updateBox.call(this, box);
34865     }
34866 });
34867 /*
34868  * Based on:
34869  * Ext JS Library 1.1.1
34870  * Copyright(c) 2006-2007, Ext JS, LLC.
34871  *
34872  * Originally Released Under LGPL - original licence link has changed is not relivant.
34873  *
34874  * Fork - LGPL
34875  * <script type="text/javascript">
34876  */
34877  
34878  
34879 /*
34880  * Private internal class for reading and applying state
34881  */
34882 Roo.LayoutStateManager = function(layout){
34883      // default empty state
34884      this.state = {
34885         north: {},
34886         south: {},
34887         east: {},
34888         west: {}       
34889     };
34890 };
34891
34892 Roo.LayoutStateManager.prototype = {
34893     init : function(layout, provider){
34894         this.provider = provider;
34895         var state = provider.get(layout.id+"-layout-state");
34896         if(state){
34897             var wasUpdating = layout.isUpdating();
34898             if(!wasUpdating){
34899                 layout.beginUpdate();
34900             }
34901             for(var key in state){
34902                 if(typeof state[key] != "function"){
34903                     var rstate = state[key];
34904                     var r = layout.getRegion(key);
34905                     if(r && rstate){
34906                         if(rstate.size){
34907                             r.resizeTo(rstate.size);
34908                         }
34909                         if(rstate.collapsed == true){
34910                             r.collapse(true);
34911                         }else{
34912                             r.expand(null, true);
34913                         }
34914                     }
34915                 }
34916             }
34917             if(!wasUpdating){
34918                 layout.endUpdate();
34919             }
34920             this.state = state; 
34921         }
34922         this.layout = layout;
34923         layout.on("regionresized", this.onRegionResized, this);
34924         layout.on("regioncollapsed", this.onRegionCollapsed, this);
34925         layout.on("regionexpanded", this.onRegionExpanded, this);
34926     },
34927     
34928     storeState : function(){
34929         this.provider.set(this.layout.id+"-layout-state", this.state);
34930     },
34931     
34932     onRegionResized : function(region, newSize){
34933         this.state[region.getPosition()].size = newSize;
34934         this.storeState();
34935     },
34936     
34937     onRegionCollapsed : function(region){
34938         this.state[region.getPosition()].collapsed = true;
34939         this.storeState();
34940     },
34941     
34942     onRegionExpanded : function(region){
34943         this.state[region.getPosition()].collapsed = false;
34944         this.storeState();
34945     }
34946 };/*
34947  * Based on:
34948  * Ext JS Library 1.1.1
34949  * Copyright(c) 2006-2007, Ext JS, LLC.
34950  *
34951  * Originally Released Under LGPL - original licence link has changed is not relivant.
34952  *
34953  * Fork - LGPL
34954  * <script type="text/javascript">
34955  */
34956 /**
34957  * @class Roo.ContentPanel
34958  * @extends Roo.util.Observable
34959  * A basic ContentPanel element.
34960  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34961  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34962  * @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
34963  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34964  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34965  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34966  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34967  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34968  * @cfg {String} title          The title for this panel
34969  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34970  * @cfg {String} url            Calls {@link #setUrl} with this value
34971  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34972  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34973  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34974  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34975
34976  * @constructor
34977  * Create a new ContentPanel.
34978  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34979  * @param {String/Object} config A string to set only the title or a config object
34980  * @param {String} content (optional) Set the HTML content for this panel
34981  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34982  */
34983 Roo.ContentPanel = function(el, config, content){
34984     
34985      
34986     /*
34987     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
34988         config = el;
34989         el = Roo.id();
34990     }
34991     if (config && config.parentLayout) { 
34992         el = config.parentLayout.el.createChild(); 
34993     }
34994     */
34995     if(el.autoCreate){ // xtype is available if this is called from factory
34996         config = el;
34997         el = Roo.id();
34998     }
34999     this.el = Roo.get(el);
35000     if(!this.el && config && config.autoCreate){
35001         if(typeof config.autoCreate == "object"){
35002             if(!config.autoCreate.id){
35003                 config.autoCreate.id = config.id||el;
35004             }
35005             this.el = Roo.DomHelper.append(document.body,
35006                         config.autoCreate, true);
35007         }else{
35008             this.el = Roo.DomHelper.append(document.body,
35009                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35010         }
35011     }
35012     this.closable = false;
35013     this.loaded = false;
35014     this.active = false;
35015     if(typeof config == "string"){
35016         this.title = config;
35017     }else{
35018         Roo.apply(this, config);
35019     }
35020     
35021     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35022         this.wrapEl = this.el.wrap();
35023         this.toolbar.container = this.el.insertSibling(false, 'before');
35024         this.toolbar = new Roo.Toolbar(this.toolbar);
35025     }
35026     
35027     // xtype created footer. - not sure if will work as we normally have to render first..
35028     if (this.footer && !this.footer.el && this.footer.xtype) {
35029         if (!this.wrapEl) {
35030             this.wrapEl = this.el.wrap();
35031         }
35032     
35033         this.footer.container = this.wrapEl.createChild();
35034          
35035         this.footer = Roo.factory(this.footer, Roo);
35036         
35037     }
35038     
35039     if(this.resizeEl){
35040         this.resizeEl = Roo.get(this.resizeEl, true);
35041     }else{
35042         this.resizeEl = this.el;
35043     }
35044     // handle view.xtype
35045     
35046  
35047     
35048     
35049     this.addEvents({
35050         /**
35051          * @event activate
35052          * Fires when this panel is activated. 
35053          * @param {Roo.ContentPanel} this
35054          */
35055         "activate" : true,
35056         /**
35057          * @event deactivate
35058          * Fires when this panel is activated. 
35059          * @param {Roo.ContentPanel} this
35060          */
35061         "deactivate" : true,
35062
35063         /**
35064          * @event resize
35065          * Fires when this panel is resized if fitToFrame is true.
35066          * @param {Roo.ContentPanel} this
35067          * @param {Number} width The width after any component adjustments
35068          * @param {Number} height The height after any component adjustments
35069          */
35070         "resize" : true,
35071         
35072          /**
35073          * @event render
35074          * Fires when this tab is created
35075          * @param {Roo.ContentPanel} this
35076          */
35077         "render" : true
35078         
35079         
35080         
35081     });
35082     
35083
35084     
35085     
35086     if(this.autoScroll){
35087         this.resizeEl.setStyle("overflow", "auto");
35088     } else {
35089         // fix randome scrolling
35090         this.el.on('scroll', function() {
35091             Roo.log('fix random scolling');
35092             this.scrollTo('top',0); 
35093         });
35094     }
35095     content = content || this.content;
35096     if(content){
35097         this.setContent(content);
35098     }
35099     if(config && config.url){
35100         this.setUrl(this.url, this.params, this.loadOnce);
35101     }
35102     
35103     
35104     
35105     Roo.ContentPanel.superclass.constructor.call(this);
35106     
35107     if (this.view && typeof(this.view.xtype) != 'undefined') {
35108         this.view.el = this.el.appendChild(document.createElement("div"));
35109         this.view = Roo.factory(this.view); 
35110         this.view.render  &&  this.view.render(false, '');  
35111     }
35112     
35113     
35114     this.fireEvent('render', this);
35115 };
35116
35117 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
35118     tabTip:'',
35119     setRegion : function(region){
35120         this.region = region;
35121         if(region){
35122            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
35123         }else{
35124            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
35125         } 
35126     },
35127     
35128     /**
35129      * Returns the toolbar for this Panel if one was configured. 
35130      * @return {Roo.Toolbar} 
35131      */
35132     getToolbar : function(){
35133         return this.toolbar;
35134     },
35135     
35136     setActiveState : function(active){
35137         this.active = active;
35138         if(!active){
35139             this.fireEvent("deactivate", this);
35140         }else{
35141             this.fireEvent("activate", this);
35142         }
35143     },
35144     /**
35145      * Updates this panel's element
35146      * @param {String} content The new content
35147      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35148     */
35149     setContent : function(content, loadScripts){
35150         this.el.update(content, loadScripts);
35151     },
35152
35153     ignoreResize : function(w, h){
35154         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35155             return true;
35156         }else{
35157             this.lastSize = {width: w, height: h};
35158             return false;
35159         }
35160     },
35161     /**
35162      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35163      * @return {Roo.UpdateManager} The UpdateManager
35164      */
35165     getUpdateManager : function(){
35166         return this.el.getUpdateManager();
35167     },
35168      /**
35169      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35170      * @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:
35171 <pre><code>
35172 panel.load({
35173     url: "your-url.php",
35174     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35175     callback: yourFunction,
35176     scope: yourObject, //(optional scope)
35177     discardUrl: false,
35178     nocache: false,
35179     text: "Loading...",
35180     timeout: 30,
35181     scripts: false
35182 });
35183 </code></pre>
35184      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35185      * 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.
35186      * @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}
35187      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35188      * @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.
35189      * @return {Roo.ContentPanel} this
35190      */
35191     load : function(){
35192         var um = this.el.getUpdateManager();
35193         um.update.apply(um, arguments);
35194         return this;
35195     },
35196
35197
35198     /**
35199      * 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.
35200      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35201      * @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)
35202      * @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)
35203      * @return {Roo.UpdateManager} The UpdateManager
35204      */
35205     setUrl : function(url, params, loadOnce){
35206         if(this.refreshDelegate){
35207             this.removeListener("activate", this.refreshDelegate);
35208         }
35209         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35210         this.on("activate", this.refreshDelegate);
35211         return this.el.getUpdateManager();
35212     },
35213     
35214     _handleRefresh : function(url, params, loadOnce){
35215         if(!loadOnce || !this.loaded){
35216             var updater = this.el.getUpdateManager();
35217             updater.update(url, params, this._setLoaded.createDelegate(this));
35218         }
35219     },
35220     
35221     _setLoaded : function(){
35222         this.loaded = true;
35223     }, 
35224     
35225     /**
35226      * Returns this panel's id
35227      * @return {String} 
35228      */
35229     getId : function(){
35230         return this.el.id;
35231     },
35232     
35233     /** 
35234      * Returns this panel's element - used by regiosn to add.
35235      * @return {Roo.Element} 
35236      */
35237     getEl : function(){
35238         return this.wrapEl || this.el;
35239     },
35240     
35241     adjustForComponents : function(width, height)
35242     {
35243         //Roo.log('adjustForComponents ');
35244         if(this.resizeEl != this.el){
35245             width -= this.el.getFrameWidth('lr');
35246             height -= this.el.getFrameWidth('tb');
35247         }
35248         if(this.toolbar){
35249             var te = this.toolbar.getEl();
35250             height -= te.getHeight();
35251             te.setWidth(width);
35252         }
35253         if(this.footer){
35254             var te = this.footer.getEl();
35255             Roo.log("footer:" + te.getHeight());
35256             
35257             height -= te.getHeight();
35258             te.setWidth(width);
35259         }
35260         
35261         
35262         if(this.adjustments){
35263             width += this.adjustments[0];
35264             height += this.adjustments[1];
35265         }
35266         return {"width": width, "height": height};
35267     },
35268     
35269     setSize : function(width, height){
35270         if(this.fitToFrame && !this.ignoreResize(width, height)){
35271             if(this.fitContainer && this.resizeEl != this.el){
35272                 this.el.setSize(width, height);
35273             }
35274             var size = this.adjustForComponents(width, height);
35275             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35276             this.fireEvent('resize', this, size.width, size.height);
35277         }
35278     },
35279     
35280     /**
35281      * Returns this panel's title
35282      * @return {String} 
35283      */
35284     getTitle : function(){
35285         return this.title;
35286     },
35287     
35288     /**
35289      * Set this panel's title
35290      * @param {String} title
35291      */
35292     setTitle : function(title){
35293         this.title = title;
35294         if(this.region){
35295             this.region.updatePanelTitle(this, title);
35296         }
35297     },
35298     
35299     /**
35300      * Returns true is this panel was configured to be closable
35301      * @return {Boolean} 
35302      */
35303     isClosable : function(){
35304         return this.closable;
35305     },
35306     
35307     beforeSlide : function(){
35308         this.el.clip();
35309         this.resizeEl.clip();
35310     },
35311     
35312     afterSlide : function(){
35313         this.el.unclip();
35314         this.resizeEl.unclip();
35315     },
35316     
35317     /**
35318      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35319      *   Will fail silently if the {@link #setUrl} method has not been called.
35320      *   This does not activate the panel, just updates its content.
35321      */
35322     refresh : function(){
35323         if(this.refreshDelegate){
35324            this.loaded = false;
35325            this.refreshDelegate();
35326         }
35327     },
35328     
35329     /**
35330      * Destroys this panel
35331      */
35332     destroy : function(){
35333         this.el.removeAllListeners();
35334         var tempEl = document.createElement("span");
35335         tempEl.appendChild(this.el.dom);
35336         tempEl.innerHTML = "";
35337         this.el.remove();
35338         this.el = null;
35339     },
35340     
35341     /**
35342      * form - if the content panel contains a form - this is a reference to it.
35343      * @type {Roo.form.Form}
35344      */
35345     form : false,
35346     /**
35347      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35348      *    This contains a reference to it.
35349      * @type {Roo.View}
35350      */
35351     view : false,
35352     
35353       /**
35354      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35355      * <pre><code>
35356
35357 layout.addxtype({
35358        xtype : 'Form',
35359        items: [ .... ]
35360    }
35361 );
35362
35363 </code></pre>
35364      * @param {Object} cfg Xtype definition of item to add.
35365      */
35366     
35367     addxtype : function(cfg) {
35368         // add form..
35369         if (cfg.xtype.match(/^Form$/)) {
35370             
35371             var el;
35372             //if (this.footer) {
35373             //    el = this.footer.container.insertSibling(false, 'before');
35374             //} else {
35375                 el = this.el.createChild();
35376             //}
35377
35378             this.form = new  Roo.form.Form(cfg);
35379             
35380             
35381             if ( this.form.allItems.length) this.form.render(el.dom);
35382             return this.form;
35383         }
35384         // should only have one of theses..
35385         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35386             // views.. should not be just added - used named prop 'view''
35387             
35388             cfg.el = this.el.appendChild(document.createElement("div"));
35389             // factory?
35390             
35391             var ret = new Roo.factory(cfg);
35392              
35393              ret.render && ret.render(false, ''); // render blank..
35394             this.view = ret;
35395             return ret;
35396         }
35397         return false;
35398     }
35399 });
35400
35401 /**
35402  * @class Roo.GridPanel
35403  * @extends Roo.ContentPanel
35404  * @constructor
35405  * Create a new GridPanel.
35406  * @param {Roo.grid.Grid} grid The grid for this panel
35407  * @param {String/Object} config A string to set only the panel's title, or a config object
35408  */
35409 Roo.GridPanel = function(grid, config){
35410     
35411   
35412     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35413         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
35414         
35415     this.wrapper.dom.appendChild(grid.getGridEl().dom);
35416     
35417     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
35418     
35419     if(this.toolbar){
35420         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
35421     }
35422     // xtype created footer. - not sure if will work as we normally have to render first..
35423     if (this.footer && !this.footer.el && this.footer.xtype) {
35424         
35425         this.footer.container = this.grid.getView().getFooterPanel(true);
35426         this.footer.dataSource = this.grid.dataSource;
35427         this.footer = Roo.factory(this.footer, Roo);
35428         
35429     }
35430     
35431     grid.monitorWindowResize = false; // turn off autosizing
35432     grid.autoHeight = false;
35433     grid.autoWidth = false;
35434     this.grid = grid;
35435     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
35436 };
35437
35438 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
35439     getId : function(){
35440         return this.grid.id;
35441     },
35442     
35443     /**
35444      * Returns the grid for this panel
35445      * @return {Roo.grid.Grid} 
35446      */
35447     getGrid : function(){
35448         return this.grid;    
35449     },
35450     
35451     setSize : function(width, height){
35452         if(!this.ignoreResize(width, height)){
35453             var grid = this.grid;
35454             var size = this.adjustForComponents(width, height);
35455             grid.getGridEl().setSize(size.width, size.height);
35456             grid.autoSize();
35457         }
35458     },
35459     
35460     beforeSlide : function(){
35461         this.grid.getView().scroller.clip();
35462     },
35463     
35464     afterSlide : function(){
35465         this.grid.getView().scroller.unclip();
35466     },
35467     
35468     destroy : function(){
35469         this.grid.destroy();
35470         delete this.grid;
35471         Roo.GridPanel.superclass.destroy.call(this); 
35472     }
35473 });
35474
35475
35476 /**
35477  * @class Roo.NestedLayoutPanel
35478  * @extends Roo.ContentPanel
35479  * @constructor
35480  * Create a new NestedLayoutPanel.
35481  * 
35482  * 
35483  * @param {Roo.BorderLayout} layout The layout for this panel
35484  * @param {String/Object} config A string to set only the title or a config object
35485  */
35486 Roo.NestedLayoutPanel = function(layout, config)
35487 {
35488     // construct with only one argument..
35489     /* FIXME - implement nicer consturctors
35490     if (layout.layout) {
35491         config = layout;
35492         layout = config.layout;
35493         delete config.layout;
35494     }
35495     if (layout.xtype && !layout.getEl) {
35496         // then layout needs constructing..
35497         layout = Roo.factory(layout, Roo);
35498     }
35499     */
35500     
35501     
35502     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
35503     
35504     layout.monitorWindowResize = false; // turn off autosizing
35505     this.layout = layout;
35506     this.layout.getEl().addClass("x-layout-nested-layout");
35507     
35508     
35509     
35510     
35511 };
35512
35513 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
35514
35515     setSize : function(width, height){
35516         if(!this.ignoreResize(width, height)){
35517             var size = this.adjustForComponents(width, height);
35518             var el = this.layout.getEl();
35519             el.setSize(size.width, size.height);
35520             var touch = el.dom.offsetWidth;
35521             this.layout.layout();
35522             // ie requires a double layout on the first pass
35523             if(Roo.isIE && !this.initialized){
35524                 this.initialized = true;
35525                 this.layout.layout();
35526             }
35527         }
35528     },
35529     
35530     // activate all subpanels if not currently active..
35531     
35532     setActiveState : function(active){
35533         this.active = active;
35534         if(!active){
35535             this.fireEvent("deactivate", this);
35536             return;
35537         }
35538         
35539         this.fireEvent("activate", this);
35540         // not sure if this should happen before or after..
35541         if (!this.layout) {
35542             return; // should not happen..
35543         }
35544         var reg = false;
35545         for (var r in this.layout.regions) {
35546             reg = this.layout.getRegion(r);
35547             if (reg.getActivePanel()) {
35548                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35549                 reg.setActivePanel(reg.getActivePanel());
35550                 continue;
35551             }
35552             if (!reg.panels.length) {
35553                 continue;
35554             }
35555             reg.showPanel(reg.getPanel(0));
35556         }
35557         
35558         
35559         
35560         
35561     },
35562     
35563     /**
35564      * Returns the nested BorderLayout for this panel
35565      * @return {Roo.BorderLayout} 
35566      */
35567     getLayout : function(){
35568         return this.layout;
35569     },
35570     
35571      /**
35572      * Adds a xtype elements to the layout of the nested panel
35573      * <pre><code>
35574
35575 panel.addxtype({
35576        xtype : 'ContentPanel',
35577        region: 'west',
35578        items: [ .... ]
35579    }
35580 );
35581
35582 panel.addxtype({
35583         xtype : 'NestedLayoutPanel',
35584         region: 'west',
35585         layout: {
35586            center: { },
35587            west: { }   
35588         },
35589         items : [ ... list of content panels or nested layout panels.. ]
35590    }
35591 );
35592 </code></pre>
35593      * @param {Object} cfg Xtype definition of item to add.
35594      */
35595     addxtype : function(cfg) {
35596         return this.layout.addxtype(cfg);
35597     
35598     }
35599 });
35600
35601 Roo.ScrollPanel = function(el, config, content){
35602     config = config || {};
35603     config.fitToFrame = true;
35604     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
35605     
35606     this.el.dom.style.overflow = "hidden";
35607     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
35608     this.el.removeClass("x-layout-inactive-content");
35609     this.el.on("mousewheel", this.onWheel, this);
35610
35611     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
35612     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
35613     up.unselectable(); down.unselectable();
35614     up.on("click", this.scrollUp, this);
35615     down.on("click", this.scrollDown, this);
35616     up.addClassOnOver("x-scroller-btn-over");
35617     down.addClassOnOver("x-scroller-btn-over");
35618     up.addClassOnClick("x-scroller-btn-click");
35619     down.addClassOnClick("x-scroller-btn-click");
35620     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
35621
35622     this.resizeEl = this.el;
35623     this.el = wrap; this.up = up; this.down = down;
35624 };
35625
35626 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
35627     increment : 100,
35628     wheelIncrement : 5,
35629     scrollUp : function(){
35630         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
35631     },
35632
35633     scrollDown : function(){
35634         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
35635     },
35636
35637     afterScroll : function(){
35638         var el = this.resizeEl;
35639         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
35640         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35641         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
35642     },
35643
35644     setSize : function(){
35645         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
35646         this.afterScroll();
35647     },
35648
35649     onWheel : function(e){
35650         var d = e.getWheelDelta();
35651         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
35652         this.afterScroll();
35653         e.stopEvent();
35654     },
35655
35656     setContent : function(content, loadScripts){
35657         this.resizeEl.update(content, loadScripts);
35658     }
35659
35660 });
35661
35662
35663
35664
35665
35666
35667
35668
35669
35670 /**
35671  * @class Roo.TreePanel
35672  * @extends Roo.ContentPanel
35673  * @constructor
35674  * Create a new TreePanel. - defaults to fit/scoll contents.
35675  * @param {String/Object} config A string to set only the panel's title, or a config object
35676  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
35677  */
35678 Roo.TreePanel = function(config){
35679     var el = config.el;
35680     var tree = config.tree;
35681     delete config.tree; 
35682     delete config.el; // hopefull!
35683     
35684     // wrapper for IE7 strict & safari scroll issue
35685     
35686     var treeEl = el.createChild();
35687     config.resizeEl = treeEl;
35688     
35689     
35690     
35691     Roo.TreePanel.superclass.constructor.call(this, el, config);
35692  
35693  
35694     this.tree = new Roo.tree.TreePanel(treeEl , tree);
35695     //console.log(tree);
35696     this.on('activate', function()
35697     {
35698         if (this.tree.rendered) {
35699             return;
35700         }
35701         //console.log('render tree');
35702         this.tree.render();
35703     });
35704     // this should not be needed.. - it's actually the 'el' that resizes?
35705     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
35706     
35707     //this.on('resize',  function (cp, w, h) {
35708     //        this.tree.innerCt.setWidth(w);
35709     //        this.tree.innerCt.setHeight(h);
35710     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
35711     //});
35712
35713         
35714     
35715 };
35716
35717 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
35718     fitToFrame : true,
35719     autoScroll : true
35720 });
35721
35722
35723
35724
35725
35726
35727
35728
35729
35730
35731
35732 /*
35733  * Based on:
35734  * Ext JS Library 1.1.1
35735  * Copyright(c) 2006-2007, Ext JS, LLC.
35736  *
35737  * Originally Released Under LGPL - original licence link has changed is not relivant.
35738  *
35739  * Fork - LGPL
35740  * <script type="text/javascript">
35741  */
35742  
35743
35744 /**
35745  * @class Roo.ReaderLayout
35746  * @extends Roo.BorderLayout
35747  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
35748  * center region containing two nested regions (a top one for a list view and one for item preview below),
35749  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
35750  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
35751  * expedites the setup of the overall layout and regions for this common application style.
35752  * Example:
35753  <pre><code>
35754 var reader = new Roo.ReaderLayout();
35755 var CP = Roo.ContentPanel;  // shortcut for adding
35756
35757 reader.beginUpdate();
35758 reader.add("north", new CP("north", "North"));
35759 reader.add("west", new CP("west", {title: "West"}));
35760 reader.add("east", new CP("east", {title: "East"}));
35761
35762 reader.regions.listView.add(new CP("listView", "List"));
35763 reader.regions.preview.add(new CP("preview", "Preview"));
35764 reader.endUpdate();
35765 </code></pre>
35766 * @constructor
35767 * Create a new ReaderLayout
35768 * @param {Object} config Configuration options
35769 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
35770 * document.body if omitted)
35771 */
35772 Roo.ReaderLayout = function(config, renderTo){
35773     var c = config || {size:{}};
35774     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
35775         north: c.north !== false ? Roo.apply({
35776             split:false,
35777             initialSize: 32,
35778             titlebar: false
35779         }, c.north) : false,
35780         west: c.west !== false ? Roo.apply({
35781             split:true,
35782             initialSize: 200,
35783             minSize: 175,
35784             maxSize: 400,
35785             titlebar: true,
35786             collapsible: true,
35787             animate: true,
35788             margins:{left:5,right:0,bottom:5,top:5},
35789             cmargins:{left:5,right:5,bottom:5,top:5}
35790         }, c.west) : false,
35791         east: c.east !== false ? Roo.apply({
35792             split:true,
35793             initialSize: 200,
35794             minSize: 175,
35795             maxSize: 400,
35796             titlebar: true,
35797             collapsible: true,
35798             animate: true,
35799             margins:{left:0,right:5,bottom:5,top:5},
35800             cmargins:{left:5,right:5,bottom:5,top:5}
35801         }, c.east) : false,
35802         center: Roo.apply({
35803             tabPosition: 'top',
35804             autoScroll:false,
35805             closeOnTab: true,
35806             titlebar:false,
35807             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
35808         }, c.center)
35809     });
35810
35811     this.el.addClass('x-reader');
35812
35813     this.beginUpdate();
35814
35815     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
35816         south: c.preview !== false ? Roo.apply({
35817             split:true,
35818             initialSize: 200,
35819             minSize: 100,
35820             autoScroll:true,
35821             collapsible:true,
35822             titlebar: true,
35823             cmargins:{top:5,left:0, right:0, bottom:0}
35824         }, c.preview) : false,
35825         center: Roo.apply({
35826             autoScroll:false,
35827             titlebar:false,
35828             minHeight:200
35829         }, c.listView)
35830     });
35831     this.add('center', new Roo.NestedLayoutPanel(inner,
35832             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
35833
35834     this.endUpdate();
35835
35836     this.regions.preview = inner.getRegion('south');
35837     this.regions.listView = inner.getRegion('center');
35838 };
35839
35840 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
35841  * Based on:
35842  * Ext JS Library 1.1.1
35843  * Copyright(c) 2006-2007, Ext JS, LLC.
35844  *
35845  * Originally Released Under LGPL - original licence link has changed is not relivant.
35846  *
35847  * Fork - LGPL
35848  * <script type="text/javascript">
35849  */
35850  
35851 /**
35852  * @class Roo.grid.Grid
35853  * @extends Roo.util.Observable
35854  * This class represents the primary interface of a component based grid control.
35855  * <br><br>Usage:<pre><code>
35856  var grid = new Roo.grid.Grid("my-container-id", {
35857      ds: myDataStore,
35858      cm: myColModel,
35859      selModel: mySelectionModel,
35860      autoSizeColumns: true,
35861      monitorWindowResize: false,
35862      trackMouseOver: true
35863  });
35864  // set any options
35865  grid.render();
35866  * </code></pre>
35867  * <b>Common Problems:</b><br/>
35868  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
35869  * element will correct this<br/>
35870  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
35871  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
35872  * are unpredictable.<br/>
35873  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35874  * grid to calculate dimensions/offsets.<br/>
35875   * @constructor
35876  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35877  * The container MUST have some type of size defined for the grid to fill. The container will be
35878  * automatically set to position relative if it isn't already.
35879  * @param {Object} config A config object that sets properties on this grid.
35880  */
35881 Roo.grid.Grid = function(container, config){
35882         // initialize the container
35883         this.container = Roo.get(container);
35884         this.container.update("");
35885         this.container.setStyle("overflow", "hidden");
35886     this.container.addClass('x-grid-container');
35887
35888     this.id = this.container.id;
35889
35890     Roo.apply(this, config);
35891     // check and correct shorthanded configs
35892     if(this.ds){
35893         this.dataSource = this.ds;
35894         delete this.ds;
35895     }
35896     if(this.cm){
35897         this.colModel = this.cm;
35898         delete this.cm;
35899     }
35900     if(this.sm){
35901         this.selModel = this.sm;
35902         delete this.sm;
35903     }
35904
35905     if (this.selModel) {
35906         this.selModel = Roo.factory(this.selModel, Roo.grid);
35907         this.sm = this.selModel;
35908         this.sm.xmodule = this.xmodule || false;
35909     }
35910     if (typeof(this.colModel.config) == 'undefined') {
35911         this.colModel = new Roo.grid.ColumnModel(this.colModel);
35912         this.cm = this.colModel;
35913         this.cm.xmodule = this.xmodule || false;
35914     }
35915     if (this.dataSource) {
35916         this.dataSource= Roo.factory(this.dataSource, Roo.data);
35917         this.ds = this.dataSource;
35918         this.ds.xmodule = this.xmodule || false;
35919          
35920     }
35921     
35922     
35923     
35924     if(this.width){
35925         this.container.setWidth(this.width);
35926     }
35927
35928     if(this.height){
35929         this.container.setHeight(this.height);
35930     }
35931     /** @private */
35932         this.addEvents({
35933         // raw events
35934         /**
35935          * @event click
35936          * The raw click event for the entire grid.
35937          * @param {Roo.EventObject} e
35938          */
35939         "click" : true,
35940         /**
35941          * @event dblclick
35942          * The raw dblclick event for the entire grid.
35943          * @param {Roo.EventObject} e
35944          */
35945         "dblclick" : true,
35946         /**
35947          * @event contextmenu
35948          * The raw contextmenu event for the entire grid.
35949          * @param {Roo.EventObject} e
35950          */
35951         "contextmenu" : true,
35952         /**
35953          * @event mousedown
35954          * The raw mousedown event for the entire grid.
35955          * @param {Roo.EventObject} e
35956          */
35957         "mousedown" : true,
35958         /**
35959          * @event mouseup
35960          * The raw mouseup event for the entire grid.
35961          * @param {Roo.EventObject} e
35962          */
35963         "mouseup" : true,
35964         /**
35965          * @event mouseover
35966          * The raw mouseover event for the entire grid.
35967          * @param {Roo.EventObject} e
35968          */
35969         "mouseover" : true,
35970         /**
35971          * @event mouseout
35972          * The raw mouseout event for the entire grid.
35973          * @param {Roo.EventObject} e
35974          */
35975         "mouseout" : true,
35976         /**
35977          * @event keypress
35978          * The raw keypress event for the entire grid.
35979          * @param {Roo.EventObject} e
35980          */
35981         "keypress" : true,
35982         /**
35983          * @event keydown
35984          * The raw keydown event for the entire grid.
35985          * @param {Roo.EventObject} e
35986          */
35987         "keydown" : true,
35988
35989         // custom events
35990
35991         /**
35992          * @event cellclick
35993          * Fires when a cell is clicked
35994          * @param {Grid} this
35995          * @param {Number} rowIndex
35996          * @param {Number} columnIndex
35997          * @param {Roo.EventObject} e
35998          */
35999         "cellclick" : true,
36000         /**
36001          * @event celldblclick
36002          * Fires when a cell is double clicked
36003          * @param {Grid} this
36004          * @param {Number} rowIndex
36005          * @param {Number} columnIndex
36006          * @param {Roo.EventObject} e
36007          */
36008         "celldblclick" : true,
36009         /**
36010          * @event rowclick
36011          * Fires when a row is clicked
36012          * @param {Grid} this
36013          * @param {Number} rowIndex
36014          * @param {Roo.EventObject} e
36015          */
36016         "rowclick" : true,
36017         /**
36018          * @event rowdblclick
36019          * Fires when a row is double clicked
36020          * @param {Grid} this
36021          * @param {Number} rowIndex
36022          * @param {Roo.EventObject} e
36023          */
36024         "rowdblclick" : true,
36025         /**
36026          * @event headerclick
36027          * Fires when a header is clicked
36028          * @param {Grid} this
36029          * @param {Number} columnIndex
36030          * @param {Roo.EventObject} e
36031          */
36032         "headerclick" : true,
36033         /**
36034          * @event headerdblclick
36035          * Fires when a header cell is double clicked
36036          * @param {Grid} this
36037          * @param {Number} columnIndex
36038          * @param {Roo.EventObject} e
36039          */
36040         "headerdblclick" : true,
36041         /**
36042          * @event rowcontextmenu
36043          * Fires when a row is right clicked
36044          * @param {Grid} this
36045          * @param {Number} rowIndex
36046          * @param {Roo.EventObject} e
36047          */
36048         "rowcontextmenu" : true,
36049         /**
36050          * @event cellcontextmenu
36051          * Fires when a cell is right clicked
36052          * @param {Grid} this
36053          * @param {Number} rowIndex
36054          * @param {Number} cellIndex
36055          * @param {Roo.EventObject} e
36056          */
36057          "cellcontextmenu" : true,
36058         /**
36059          * @event headercontextmenu
36060          * Fires when a header is right clicked
36061          * @param {Grid} this
36062          * @param {Number} columnIndex
36063          * @param {Roo.EventObject} e
36064          */
36065         "headercontextmenu" : true,
36066         /**
36067          * @event bodyscroll
36068          * Fires when the body element is scrolled
36069          * @param {Number} scrollLeft
36070          * @param {Number} scrollTop
36071          */
36072         "bodyscroll" : true,
36073         /**
36074          * @event columnresize
36075          * Fires when the user resizes a column
36076          * @param {Number} columnIndex
36077          * @param {Number} newSize
36078          */
36079         "columnresize" : true,
36080         /**
36081          * @event columnmove
36082          * Fires when the user moves a column
36083          * @param {Number} oldIndex
36084          * @param {Number} newIndex
36085          */
36086         "columnmove" : true,
36087         /**
36088          * @event startdrag
36089          * Fires when row(s) start being dragged
36090          * @param {Grid} this
36091          * @param {Roo.GridDD} dd The drag drop object
36092          * @param {event} e The raw browser event
36093          */
36094         "startdrag" : true,
36095         /**
36096          * @event enddrag
36097          * Fires when a drag operation is complete
36098          * @param {Grid} this
36099          * @param {Roo.GridDD} dd The drag drop object
36100          * @param {event} e The raw browser event
36101          */
36102         "enddrag" : true,
36103         /**
36104          * @event dragdrop
36105          * Fires when dragged row(s) are dropped on a valid DD target
36106          * @param {Grid} this
36107          * @param {Roo.GridDD} dd The drag drop object
36108          * @param {String} targetId The target drag drop object
36109          * @param {event} e The raw browser event
36110          */
36111         "dragdrop" : true,
36112         /**
36113          * @event dragover
36114          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36115          * @param {Grid} this
36116          * @param {Roo.GridDD} dd The drag drop object
36117          * @param {String} targetId The target drag drop object
36118          * @param {event} e The raw browser event
36119          */
36120         "dragover" : true,
36121         /**
36122          * @event dragenter
36123          *  Fires when the dragged row(s) first cross another DD target while being dragged
36124          * @param {Grid} this
36125          * @param {Roo.GridDD} dd The drag drop object
36126          * @param {String} targetId The target drag drop object
36127          * @param {event} e The raw browser event
36128          */
36129         "dragenter" : true,
36130         /**
36131          * @event dragout
36132          * Fires when the dragged row(s) leave another DD target while being dragged
36133          * @param {Grid} this
36134          * @param {Roo.GridDD} dd The drag drop object
36135          * @param {String} targetId The target drag drop object
36136          * @param {event} e The raw browser event
36137          */
36138         "dragout" : true,
36139         /**
36140          * @event rowclass
36141          * Fires when a row is rendered, so you can change add a style to it.
36142          * @param {GridView} gridview   The grid view
36143          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
36144          */
36145         'rowclass' : true,
36146
36147         /**
36148          * @event render
36149          * Fires when the grid is rendered
36150          * @param {Grid} grid
36151          */
36152         'render' : true
36153     });
36154
36155     Roo.grid.Grid.superclass.constructor.call(this);
36156 };
36157 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
36158     
36159     /**
36160      * @cfg {String} ddGroup - drag drop group.
36161      */
36162
36163     /**
36164      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
36165      */
36166     minColumnWidth : 25,
36167
36168     /**
36169      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
36170      * <b>on initial render.</b> It is more efficient to explicitly size the columns
36171      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
36172      */
36173     autoSizeColumns : false,
36174
36175     /**
36176      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
36177      */
36178     autoSizeHeaders : true,
36179
36180     /**
36181      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
36182      */
36183     monitorWindowResize : true,
36184
36185     /**
36186      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
36187      * rows measured to get a columns size. Default is 0 (all rows).
36188      */
36189     maxRowsToMeasure : 0,
36190
36191     /**
36192      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
36193      */
36194     trackMouseOver : true,
36195
36196     /**
36197     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
36198     */
36199     
36200     /**
36201     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
36202     */
36203     enableDragDrop : false,
36204     
36205     /**
36206     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
36207     */
36208     enableColumnMove : true,
36209     
36210     /**
36211     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
36212     */
36213     enableColumnHide : true,
36214     
36215     /**
36216     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
36217     */
36218     enableRowHeightSync : false,
36219     
36220     /**
36221     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
36222     */
36223     stripeRows : true,
36224     
36225     /**
36226     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
36227     */
36228     autoHeight : false,
36229
36230     /**
36231      * @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.
36232      */
36233     autoExpandColumn : false,
36234
36235     /**
36236     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
36237     * Default is 50.
36238     */
36239     autoExpandMin : 50,
36240
36241     /**
36242     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
36243     */
36244     autoExpandMax : 1000,
36245
36246     /**
36247     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
36248     */
36249     view : null,
36250
36251     /**
36252     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
36253     */
36254     loadMask : false,
36255     /**
36256     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
36257     */
36258     dropTarget: false,
36259     
36260    
36261     
36262     // private
36263     rendered : false,
36264
36265     /**
36266     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
36267     * of a fixed width. Default is false.
36268     */
36269     /**
36270     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
36271     */
36272     /**
36273      * Called once after all setup has been completed and the grid is ready to be rendered.
36274      * @return {Roo.grid.Grid} this
36275      */
36276     render : function()
36277     {
36278         var c = this.container;
36279         // try to detect autoHeight/width mode
36280         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
36281             this.autoHeight = true;
36282         }
36283         var view = this.getView();
36284         view.init(this);
36285
36286         c.on("click", this.onClick, this);
36287         c.on("dblclick", this.onDblClick, this);
36288         c.on("contextmenu", this.onContextMenu, this);
36289         c.on("keydown", this.onKeyDown, this);
36290         if (Roo.isTouch) {
36291             c.on("touchstart", this.onTouchStart, this);
36292         }
36293
36294         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
36295
36296         this.getSelectionModel().init(this);
36297
36298         view.render();
36299
36300         if(this.loadMask){
36301             this.loadMask = new Roo.LoadMask(this.container,
36302                     Roo.apply({store:this.dataSource}, this.loadMask));
36303         }
36304         
36305         
36306         if (this.toolbar && this.toolbar.xtype) {
36307             this.toolbar.container = this.getView().getHeaderPanel(true);
36308             this.toolbar = new Roo.Toolbar(this.toolbar);
36309         }
36310         if (this.footer && this.footer.xtype) {
36311             this.footer.dataSource = this.getDataSource();
36312             this.footer.container = this.getView().getFooterPanel(true);
36313             this.footer = Roo.factory(this.footer, Roo);
36314         }
36315         if (this.dropTarget && this.dropTarget.xtype) {
36316             delete this.dropTarget.xtype;
36317             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
36318         }
36319         
36320         
36321         this.rendered = true;
36322         this.fireEvent('render', this);
36323         return this;
36324     },
36325
36326         /**
36327          * Reconfigures the grid to use a different Store and Column Model.
36328          * The View will be bound to the new objects and refreshed.
36329          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
36330          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
36331          */
36332     reconfigure : function(dataSource, colModel){
36333         if(this.loadMask){
36334             this.loadMask.destroy();
36335             this.loadMask = new Roo.LoadMask(this.container,
36336                     Roo.apply({store:dataSource}, this.loadMask));
36337         }
36338         this.view.bind(dataSource, colModel);
36339         this.dataSource = dataSource;
36340         this.colModel = colModel;
36341         this.view.refresh(true);
36342     },
36343
36344     // private
36345     onKeyDown : function(e){
36346         this.fireEvent("keydown", e);
36347     },
36348
36349     /**
36350      * Destroy this grid.
36351      * @param {Boolean} removeEl True to remove the element
36352      */
36353     destroy : function(removeEl, keepListeners){
36354         if(this.loadMask){
36355             this.loadMask.destroy();
36356         }
36357         var c = this.container;
36358         c.removeAllListeners();
36359         this.view.destroy();
36360         this.colModel.purgeListeners();
36361         if(!keepListeners){
36362             this.purgeListeners();
36363         }
36364         c.update("");
36365         if(removeEl === true){
36366             c.remove();
36367         }
36368     },
36369
36370     // private
36371     processEvent : function(name, e){
36372         // does this fire select???
36373         //Roo.log('grid:processEvent '  + name);
36374         
36375         if (name != 'touchstart' ) {
36376             this.fireEvent(name, e);    
36377         }
36378         
36379         var t = e.getTarget();
36380         var v = this.view;
36381         var header = v.findHeaderIndex(t);
36382         if(header !== false){
36383             var ename = name == 'touchstart' ? 'click' : name;
36384              
36385             this.fireEvent("header" + ename, this, header, e);
36386         }else{
36387             var row = v.findRowIndex(t);
36388             var cell = v.findCellIndex(t);
36389             if (name == 'touchstart') {
36390                 // first touch is always a click.
36391                 // hopefull this happens after selection is updated.?
36392                 name = false;
36393                 
36394                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
36395                     var cs = this.selModel.getSelectedCell();
36396                     if (row == cs[0] && cell == cs[1]){
36397                         name = 'dblclick';
36398                     }
36399                 }
36400                 if (typeof(this.selModel.getSelections) != 'undefined') {
36401                     var cs = this.selModel.getSelections();
36402                     var ds = this.dataSource;
36403                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
36404                         name = 'dblclick';
36405                     }
36406                 }
36407                 if (!name) {
36408                     return;
36409                 }
36410             }
36411             
36412             
36413             if(row !== false){
36414                 this.fireEvent("row" + name, this, row, e);
36415                 if(cell !== false){
36416                     this.fireEvent("cell" + name, this, row, cell, e);
36417                 }
36418             }
36419         }
36420     },
36421
36422     // private
36423     onClick : function(e){
36424         this.processEvent("click", e);
36425     },
36426    // private
36427     onTouchStart : function(e){
36428         this.processEvent("touchstart", e);
36429     },
36430
36431     // private
36432     onContextMenu : function(e, t){
36433         this.processEvent("contextmenu", e);
36434     },
36435
36436     // private
36437     onDblClick : function(e){
36438         this.processEvent("dblclick", e);
36439     },
36440
36441     // private
36442     walkCells : function(row, col, step, fn, scope){
36443         var cm = this.colModel, clen = cm.getColumnCount();
36444         var ds = this.dataSource, rlen = ds.getCount(), first = true;
36445         if(step < 0){
36446             if(col < 0){
36447                 row--;
36448                 first = false;
36449             }
36450             while(row >= 0){
36451                 if(!first){
36452                     col = clen-1;
36453                 }
36454                 first = false;
36455                 while(col >= 0){
36456                     if(fn.call(scope || this, row, col, cm) === true){
36457                         return [row, col];
36458                     }
36459                     col--;
36460                 }
36461                 row--;
36462             }
36463         } else {
36464             if(col >= clen){
36465                 row++;
36466                 first = false;
36467             }
36468             while(row < rlen){
36469                 if(!first){
36470                     col = 0;
36471                 }
36472                 first = false;
36473                 while(col < clen){
36474                     if(fn.call(scope || this, row, col, cm) === true){
36475                         return [row, col];
36476                     }
36477                     col++;
36478                 }
36479                 row++;
36480             }
36481         }
36482         return null;
36483     },
36484
36485     // private
36486     getSelections : function(){
36487         return this.selModel.getSelections();
36488     },
36489
36490     /**
36491      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
36492      * but if manual update is required this method will initiate it.
36493      */
36494     autoSize : function(){
36495         if(this.rendered){
36496             this.view.layout();
36497             if(this.view.adjustForScroll){
36498                 this.view.adjustForScroll();
36499             }
36500         }
36501     },
36502
36503     /**
36504      * Returns the grid's underlying element.
36505      * @return {Element} The element
36506      */
36507     getGridEl : function(){
36508         return this.container;
36509     },
36510
36511     // private for compatibility, overridden by editor grid
36512     stopEditing : function(){},
36513
36514     /**
36515      * Returns the grid's SelectionModel.
36516      * @return {SelectionModel}
36517      */
36518     getSelectionModel : function(){
36519         if(!this.selModel){
36520             this.selModel = new Roo.grid.RowSelectionModel();
36521         }
36522         return this.selModel;
36523     },
36524
36525     /**
36526      * Returns the grid's DataSource.
36527      * @return {DataSource}
36528      */
36529     getDataSource : function(){
36530         return this.dataSource;
36531     },
36532
36533     /**
36534      * Returns the grid's ColumnModel.
36535      * @return {ColumnModel}
36536      */
36537     getColumnModel : function(){
36538         return this.colModel;
36539     },
36540
36541     /**
36542      * Returns the grid's GridView object.
36543      * @return {GridView}
36544      */
36545     getView : function(){
36546         if(!this.view){
36547             this.view = new Roo.grid.GridView(this.viewConfig);
36548         }
36549         return this.view;
36550     },
36551     /**
36552      * Called to get grid's drag proxy text, by default returns this.ddText.
36553      * @return {String}
36554      */
36555     getDragDropText : function(){
36556         var count = this.selModel.getCount();
36557         return String.format(this.ddText, count, count == 1 ? '' : 's');
36558     }
36559 });
36560 /**
36561  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
36562  * %0 is replaced with the number of selected rows.
36563  * @type String
36564  */
36565 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
36566  * Based on:
36567  * Ext JS Library 1.1.1
36568  * Copyright(c) 2006-2007, Ext JS, LLC.
36569  *
36570  * Originally Released Under LGPL - original licence link has changed is not relivant.
36571  *
36572  * Fork - LGPL
36573  * <script type="text/javascript">
36574  */
36575  
36576 Roo.grid.AbstractGridView = function(){
36577         this.grid = null;
36578         
36579         this.events = {
36580             "beforerowremoved" : true,
36581             "beforerowsinserted" : true,
36582             "beforerefresh" : true,
36583             "rowremoved" : true,
36584             "rowsinserted" : true,
36585             "rowupdated" : true,
36586             "refresh" : true
36587         };
36588     Roo.grid.AbstractGridView.superclass.constructor.call(this);
36589 };
36590
36591 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
36592     rowClass : "x-grid-row",
36593     cellClass : "x-grid-cell",
36594     tdClass : "x-grid-td",
36595     hdClass : "x-grid-hd",
36596     splitClass : "x-grid-hd-split",
36597     
36598     init: function(grid){
36599         this.grid = grid;
36600                 var cid = this.grid.getGridEl().id;
36601         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
36602         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
36603         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
36604         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
36605         },
36606         
36607     getColumnRenderers : function(){
36608         var renderers = [];
36609         var cm = this.grid.colModel;
36610         var colCount = cm.getColumnCount();
36611         for(var i = 0; i < colCount; i++){
36612             renderers[i] = cm.getRenderer(i);
36613         }
36614         return renderers;
36615     },
36616     
36617     getColumnIds : function(){
36618         var ids = [];
36619         var cm = this.grid.colModel;
36620         var colCount = cm.getColumnCount();
36621         for(var i = 0; i < colCount; i++){
36622             ids[i] = cm.getColumnId(i);
36623         }
36624         return ids;
36625     },
36626     
36627     getDataIndexes : function(){
36628         if(!this.indexMap){
36629             this.indexMap = this.buildIndexMap();
36630         }
36631         return this.indexMap.colToData;
36632     },
36633     
36634     getColumnIndexByDataIndex : function(dataIndex){
36635         if(!this.indexMap){
36636             this.indexMap = this.buildIndexMap();
36637         }
36638         return this.indexMap.dataToCol[dataIndex];
36639     },
36640     
36641     /**
36642      * Set a css style for a column dynamically. 
36643      * @param {Number} colIndex The index of the column
36644      * @param {String} name The css property name
36645      * @param {String} value The css value
36646      */
36647     setCSSStyle : function(colIndex, name, value){
36648         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
36649         Roo.util.CSS.updateRule(selector, name, value);
36650     },
36651     
36652     generateRules : function(cm){
36653         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
36654         Roo.util.CSS.removeStyleSheet(rulesId);
36655         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36656             var cid = cm.getColumnId(i);
36657             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
36658                          this.tdSelector, cid, " {\n}\n",
36659                          this.hdSelector, cid, " {\n}\n",
36660                          this.splitSelector, cid, " {\n}\n");
36661         }
36662         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36663     }
36664 });/*
36665  * Based on:
36666  * Ext JS Library 1.1.1
36667  * Copyright(c) 2006-2007, Ext JS, LLC.
36668  *
36669  * Originally Released Under LGPL - original licence link has changed is not relivant.
36670  *
36671  * Fork - LGPL
36672  * <script type="text/javascript">
36673  */
36674
36675 // private
36676 // This is a support class used internally by the Grid components
36677 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
36678     this.grid = grid;
36679     this.view = grid.getView();
36680     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36681     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
36682     if(hd2){
36683         this.setHandleElId(Roo.id(hd));
36684         this.setOuterHandleElId(Roo.id(hd2));
36685     }
36686     this.scroll = false;
36687 };
36688 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
36689     maxDragWidth: 120,
36690     getDragData : function(e){
36691         var t = Roo.lib.Event.getTarget(e);
36692         var h = this.view.findHeaderCell(t);
36693         if(h){
36694             return {ddel: h.firstChild, header:h};
36695         }
36696         return false;
36697     },
36698
36699     onInitDrag : function(e){
36700         this.view.headersDisabled = true;
36701         var clone = this.dragData.ddel.cloneNode(true);
36702         clone.id = Roo.id();
36703         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
36704         this.proxy.update(clone);
36705         return true;
36706     },
36707
36708     afterValidDrop : function(){
36709         var v = this.view;
36710         setTimeout(function(){
36711             v.headersDisabled = false;
36712         }, 50);
36713     },
36714
36715     afterInvalidDrop : function(){
36716         var v = this.view;
36717         setTimeout(function(){
36718             v.headersDisabled = false;
36719         }, 50);
36720     }
36721 });
36722 /*
36723  * Based on:
36724  * Ext JS Library 1.1.1
36725  * Copyright(c) 2006-2007, Ext JS, LLC.
36726  *
36727  * Originally Released Under LGPL - original licence link has changed is not relivant.
36728  *
36729  * Fork - LGPL
36730  * <script type="text/javascript">
36731  */
36732 // private
36733 // This is a support class used internally by the Grid components
36734 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
36735     this.grid = grid;
36736     this.view = grid.getView();
36737     // split the proxies so they don't interfere with mouse events
36738     this.proxyTop = Roo.DomHelper.append(document.body, {
36739         cls:"col-move-top", html:"&#160;"
36740     }, true);
36741     this.proxyBottom = Roo.DomHelper.append(document.body, {
36742         cls:"col-move-bottom", html:"&#160;"
36743     }, true);
36744     this.proxyTop.hide = this.proxyBottom.hide = function(){
36745         this.setLeftTop(-100,-100);
36746         this.setStyle("visibility", "hidden");
36747     };
36748     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
36749     // temporarily disabled
36750     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
36751     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
36752 };
36753 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
36754     proxyOffsets : [-4, -9],
36755     fly: Roo.Element.fly,
36756
36757     getTargetFromEvent : function(e){
36758         var t = Roo.lib.Event.getTarget(e);
36759         var cindex = this.view.findCellIndex(t);
36760         if(cindex !== false){
36761             return this.view.getHeaderCell(cindex);
36762         }
36763         return null;
36764     },
36765
36766     nextVisible : function(h){
36767         var v = this.view, cm = this.grid.colModel;
36768         h = h.nextSibling;
36769         while(h){
36770             if(!cm.isHidden(v.getCellIndex(h))){
36771                 return h;
36772             }
36773             h = h.nextSibling;
36774         }
36775         return null;
36776     },
36777
36778     prevVisible : function(h){
36779         var v = this.view, cm = this.grid.colModel;
36780         h = h.prevSibling;
36781         while(h){
36782             if(!cm.isHidden(v.getCellIndex(h))){
36783                 return h;
36784             }
36785             h = h.prevSibling;
36786         }
36787         return null;
36788     },
36789
36790     positionIndicator : function(h, n, e){
36791         var x = Roo.lib.Event.getPageX(e);
36792         var r = Roo.lib.Dom.getRegion(n.firstChild);
36793         var px, pt, py = r.top + this.proxyOffsets[1];
36794         if((r.right - x) <= (r.right-r.left)/2){
36795             px = r.right+this.view.borderWidth;
36796             pt = "after";
36797         }else{
36798             px = r.left;
36799             pt = "before";
36800         }
36801         var oldIndex = this.view.getCellIndex(h);
36802         var newIndex = this.view.getCellIndex(n);
36803
36804         if(this.grid.colModel.isFixed(newIndex)){
36805             return false;
36806         }
36807
36808         var locked = this.grid.colModel.isLocked(newIndex);
36809
36810         if(pt == "after"){
36811             newIndex++;
36812         }
36813         if(oldIndex < newIndex){
36814             newIndex--;
36815         }
36816         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
36817             return false;
36818         }
36819         px +=  this.proxyOffsets[0];
36820         this.proxyTop.setLeftTop(px, py);
36821         this.proxyTop.show();
36822         if(!this.bottomOffset){
36823             this.bottomOffset = this.view.mainHd.getHeight();
36824         }
36825         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
36826         this.proxyBottom.show();
36827         return pt;
36828     },
36829
36830     onNodeEnter : function(n, dd, e, data){
36831         if(data.header != n){
36832             this.positionIndicator(data.header, n, e);
36833         }
36834     },
36835
36836     onNodeOver : function(n, dd, e, data){
36837         var result = false;
36838         if(data.header != n){
36839             result = this.positionIndicator(data.header, n, e);
36840         }
36841         if(!result){
36842             this.proxyTop.hide();
36843             this.proxyBottom.hide();
36844         }
36845         return result ? this.dropAllowed : this.dropNotAllowed;
36846     },
36847
36848     onNodeOut : function(n, dd, e, data){
36849         this.proxyTop.hide();
36850         this.proxyBottom.hide();
36851     },
36852
36853     onNodeDrop : function(n, dd, e, data){
36854         var h = data.header;
36855         if(h != n){
36856             var cm = this.grid.colModel;
36857             var x = Roo.lib.Event.getPageX(e);
36858             var r = Roo.lib.Dom.getRegion(n.firstChild);
36859             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
36860             var oldIndex = this.view.getCellIndex(h);
36861             var newIndex = this.view.getCellIndex(n);
36862             var locked = cm.isLocked(newIndex);
36863             if(pt == "after"){
36864                 newIndex++;
36865             }
36866             if(oldIndex < newIndex){
36867                 newIndex--;
36868             }
36869             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
36870                 return false;
36871             }
36872             cm.setLocked(oldIndex, locked, true);
36873             cm.moveColumn(oldIndex, newIndex);
36874             this.grid.fireEvent("columnmove", oldIndex, newIndex);
36875             return true;
36876         }
36877         return false;
36878     }
36879 });
36880 /*
36881  * Based on:
36882  * Ext JS Library 1.1.1
36883  * Copyright(c) 2006-2007, Ext JS, LLC.
36884  *
36885  * Originally Released Under LGPL - original licence link has changed is not relivant.
36886  *
36887  * Fork - LGPL
36888  * <script type="text/javascript">
36889  */
36890   
36891 /**
36892  * @class Roo.grid.GridView
36893  * @extends Roo.util.Observable
36894  *
36895  * @constructor
36896  * @param {Object} config
36897  */
36898 Roo.grid.GridView = function(config){
36899     Roo.grid.GridView.superclass.constructor.call(this);
36900     this.el = null;
36901
36902     Roo.apply(this, config);
36903 };
36904
36905 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
36906
36907     unselectable :  'unselectable="on"',
36908     unselectableCls :  'x-unselectable',
36909     
36910     
36911     rowClass : "x-grid-row",
36912
36913     cellClass : "x-grid-col",
36914
36915     tdClass : "x-grid-td",
36916
36917     hdClass : "x-grid-hd",
36918
36919     splitClass : "x-grid-split",
36920
36921     sortClasses : ["sort-asc", "sort-desc"],
36922
36923     enableMoveAnim : false,
36924
36925     hlColor: "C3DAF9",
36926
36927     dh : Roo.DomHelper,
36928
36929     fly : Roo.Element.fly,
36930
36931     css : Roo.util.CSS,
36932
36933     borderWidth: 1,
36934
36935     splitOffset: 3,
36936
36937     scrollIncrement : 22,
36938
36939     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
36940
36941     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
36942
36943     bind : function(ds, cm){
36944         if(this.ds){
36945             this.ds.un("load", this.onLoad, this);
36946             this.ds.un("datachanged", this.onDataChange, this);
36947             this.ds.un("add", this.onAdd, this);
36948             this.ds.un("remove", this.onRemove, this);
36949             this.ds.un("update", this.onUpdate, this);
36950             this.ds.un("clear", this.onClear, this);
36951         }
36952         if(ds){
36953             ds.on("load", this.onLoad, this);
36954             ds.on("datachanged", this.onDataChange, this);
36955             ds.on("add", this.onAdd, this);
36956             ds.on("remove", this.onRemove, this);
36957             ds.on("update", this.onUpdate, this);
36958             ds.on("clear", this.onClear, this);
36959         }
36960         this.ds = ds;
36961
36962         if(this.cm){
36963             this.cm.un("widthchange", this.onColWidthChange, this);
36964             this.cm.un("headerchange", this.onHeaderChange, this);
36965             this.cm.un("hiddenchange", this.onHiddenChange, this);
36966             this.cm.un("columnmoved", this.onColumnMove, this);
36967             this.cm.un("columnlockchange", this.onColumnLock, this);
36968         }
36969         if(cm){
36970             this.generateRules(cm);
36971             cm.on("widthchange", this.onColWidthChange, this);
36972             cm.on("headerchange", this.onHeaderChange, this);
36973             cm.on("hiddenchange", this.onHiddenChange, this);
36974             cm.on("columnmoved", this.onColumnMove, this);
36975             cm.on("columnlockchange", this.onColumnLock, this);
36976         }
36977         this.cm = cm;
36978     },
36979
36980     init: function(grid){
36981         Roo.grid.GridView.superclass.init.call(this, grid);
36982
36983         this.bind(grid.dataSource, grid.colModel);
36984
36985         grid.on("headerclick", this.handleHeaderClick, this);
36986
36987         if(grid.trackMouseOver){
36988             grid.on("mouseover", this.onRowOver, this);
36989             grid.on("mouseout", this.onRowOut, this);
36990         }
36991         grid.cancelTextSelection = function(){};
36992         this.gridId = grid.id;
36993
36994         var tpls = this.templates || {};
36995
36996         if(!tpls.master){
36997             tpls.master = new Roo.Template(
36998                '<div class="x-grid" hidefocus="true">',
36999                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
37000                   '<div class="x-grid-topbar"></div>',
37001                   '<div class="x-grid-scroller"><div></div></div>',
37002                   '<div class="x-grid-locked">',
37003                       '<div class="x-grid-header">{lockedHeader}</div>',
37004                       '<div class="x-grid-body">{lockedBody}</div>',
37005                   "</div>",
37006                   '<div class="x-grid-viewport">',
37007                       '<div class="x-grid-header">{header}</div>',
37008                       '<div class="x-grid-body">{body}</div>',
37009                   "</div>",
37010                   '<div class="x-grid-bottombar"></div>',
37011                  
37012                   '<div class="x-grid-resize-proxy">&#160;</div>',
37013                "</div>"
37014             );
37015             tpls.master.disableformats = true;
37016         }
37017
37018         if(!tpls.header){
37019             tpls.header = new Roo.Template(
37020                '<table border="0" cellspacing="0" cellpadding="0">',
37021                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
37022                "</table>{splits}"
37023             );
37024             tpls.header.disableformats = true;
37025         }
37026         tpls.header.compile();
37027
37028         if(!tpls.hcell){
37029             tpls.hcell = new Roo.Template(
37030                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
37031                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
37032                 "</div></td>"
37033              );
37034              tpls.hcell.disableFormats = true;
37035         }
37036         tpls.hcell.compile();
37037
37038         if(!tpls.hsplit){
37039             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
37040                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
37041             tpls.hsplit.disableFormats = true;
37042         }
37043         tpls.hsplit.compile();
37044
37045         if(!tpls.body){
37046             tpls.body = new Roo.Template(
37047                '<table border="0" cellspacing="0" cellpadding="0">',
37048                "<tbody>{rows}</tbody>",
37049                "</table>"
37050             );
37051             tpls.body.disableFormats = true;
37052         }
37053         tpls.body.compile();
37054
37055         if(!tpls.row){
37056             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
37057             tpls.row.disableFormats = true;
37058         }
37059         tpls.row.compile();
37060
37061         if(!tpls.cell){
37062             tpls.cell = new Roo.Template(
37063                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
37064                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
37065                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
37066                 "</td>"
37067             );
37068             tpls.cell.disableFormats = true;
37069         }
37070         tpls.cell.compile();
37071
37072         this.templates = tpls;
37073     },
37074
37075     // remap these for backwards compat
37076     onColWidthChange : function(){
37077         this.updateColumns.apply(this, arguments);
37078     },
37079     onHeaderChange : function(){
37080         this.updateHeaders.apply(this, arguments);
37081     }, 
37082     onHiddenChange : function(){
37083         this.handleHiddenChange.apply(this, arguments);
37084     },
37085     onColumnMove : function(){
37086         this.handleColumnMove.apply(this, arguments);
37087     },
37088     onColumnLock : function(){
37089         this.handleLockChange.apply(this, arguments);
37090     },
37091
37092     onDataChange : function(){
37093         this.refresh();
37094         this.updateHeaderSortState();
37095     },
37096
37097     onClear : function(){
37098         this.refresh();
37099     },
37100
37101     onUpdate : function(ds, record){
37102         this.refreshRow(record);
37103     },
37104
37105     refreshRow : function(record){
37106         var ds = this.ds, index;
37107         if(typeof record == 'number'){
37108             index = record;
37109             record = ds.getAt(index);
37110         }else{
37111             index = ds.indexOf(record);
37112         }
37113         this.insertRows(ds, index, index, true);
37114         this.onRemove(ds, record, index+1, true);
37115         this.syncRowHeights(index, index);
37116         this.layout();
37117         this.fireEvent("rowupdated", this, index, record);
37118     },
37119
37120     onAdd : function(ds, records, index){
37121         this.insertRows(ds, index, index + (records.length-1));
37122     },
37123
37124     onRemove : function(ds, record, index, isUpdate){
37125         if(isUpdate !== true){
37126             this.fireEvent("beforerowremoved", this, index, record);
37127         }
37128         var bt = this.getBodyTable(), lt = this.getLockedTable();
37129         if(bt.rows[index]){
37130             bt.firstChild.removeChild(bt.rows[index]);
37131         }
37132         if(lt.rows[index]){
37133             lt.firstChild.removeChild(lt.rows[index]);
37134         }
37135         if(isUpdate !== true){
37136             this.stripeRows(index);
37137             this.syncRowHeights(index, index);
37138             this.layout();
37139             this.fireEvent("rowremoved", this, index, record);
37140         }
37141     },
37142
37143     onLoad : function(){
37144         this.scrollToTop();
37145     },
37146
37147     /**
37148      * Scrolls the grid to the top
37149      */
37150     scrollToTop : function(){
37151         if(this.scroller){
37152             this.scroller.dom.scrollTop = 0;
37153             this.syncScroll();
37154         }
37155     },
37156
37157     /**
37158      * Gets a panel in the header of the grid that can be used for toolbars etc.
37159      * After modifying the contents of this panel a call to grid.autoSize() may be
37160      * required to register any changes in size.
37161      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
37162      * @return Roo.Element
37163      */
37164     getHeaderPanel : function(doShow){
37165         if(doShow){
37166             this.headerPanel.show();
37167         }
37168         return this.headerPanel;
37169     },
37170
37171     /**
37172      * Gets a panel in the footer of the grid that can be used for toolbars etc.
37173      * After modifying the contents of this panel a call to grid.autoSize() may be
37174      * required to register any changes in size.
37175      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
37176      * @return Roo.Element
37177      */
37178     getFooterPanel : function(doShow){
37179         if(doShow){
37180             this.footerPanel.show();
37181         }
37182         return this.footerPanel;
37183     },
37184
37185     initElements : function(){
37186         var E = Roo.Element;
37187         var el = this.grid.getGridEl().dom.firstChild;
37188         var cs = el.childNodes;
37189
37190         this.el = new E(el);
37191         
37192          this.focusEl = new E(el.firstChild);
37193         this.focusEl.swallowEvent("click", true);
37194         
37195         this.headerPanel = new E(cs[1]);
37196         this.headerPanel.enableDisplayMode("block");
37197
37198         this.scroller = new E(cs[2]);
37199         this.scrollSizer = new E(this.scroller.dom.firstChild);
37200
37201         this.lockedWrap = new E(cs[3]);
37202         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
37203         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
37204
37205         this.mainWrap = new E(cs[4]);
37206         this.mainHd = new E(this.mainWrap.dom.firstChild);
37207         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
37208
37209         this.footerPanel = new E(cs[5]);
37210         this.footerPanel.enableDisplayMode("block");
37211
37212         this.resizeProxy = new E(cs[6]);
37213
37214         this.headerSelector = String.format(
37215            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
37216            this.lockedHd.id, this.mainHd.id
37217         );
37218
37219         this.splitterSelector = String.format(
37220            '#{0} div.x-grid-split, #{1} div.x-grid-split',
37221            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
37222         );
37223     },
37224     idToCssName : function(s)
37225     {
37226         return s.replace(/[^a-z0-9]+/ig, '-');
37227     },
37228
37229     getHeaderCell : function(index){
37230         return Roo.DomQuery.select(this.headerSelector)[index];
37231     },
37232
37233     getHeaderCellMeasure : function(index){
37234         return this.getHeaderCell(index).firstChild;
37235     },
37236
37237     getHeaderCellText : function(index){
37238         return this.getHeaderCell(index).firstChild.firstChild;
37239     },
37240
37241     getLockedTable : function(){
37242         return this.lockedBody.dom.firstChild;
37243     },
37244
37245     getBodyTable : function(){
37246         return this.mainBody.dom.firstChild;
37247     },
37248
37249     getLockedRow : function(index){
37250         return this.getLockedTable().rows[index];
37251     },
37252
37253     getRow : function(index){
37254         return this.getBodyTable().rows[index];
37255     },
37256
37257     getRowComposite : function(index){
37258         if(!this.rowEl){
37259             this.rowEl = new Roo.CompositeElementLite();
37260         }
37261         var els = [], lrow, mrow;
37262         if(lrow = this.getLockedRow(index)){
37263             els.push(lrow);
37264         }
37265         if(mrow = this.getRow(index)){
37266             els.push(mrow);
37267         }
37268         this.rowEl.elements = els;
37269         return this.rowEl;
37270     },
37271     /**
37272      * Gets the 'td' of the cell
37273      * 
37274      * @param {Integer} rowIndex row to select
37275      * @param {Integer} colIndex column to select
37276      * 
37277      * @return {Object} 
37278      */
37279     getCell : function(rowIndex, colIndex){
37280         var locked = this.cm.getLockedCount();
37281         var source;
37282         if(colIndex < locked){
37283             source = this.lockedBody.dom.firstChild;
37284         }else{
37285             source = this.mainBody.dom.firstChild;
37286             colIndex -= locked;
37287         }
37288         return source.rows[rowIndex].childNodes[colIndex];
37289     },
37290
37291     getCellText : function(rowIndex, colIndex){
37292         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
37293     },
37294
37295     getCellBox : function(cell){
37296         var b = this.fly(cell).getBox();
37297         if(Roo.isOpera){ // opera fails to report the Y
37298             b.y = cell.offsetTop + this.mainBody.getY();
37299         }
37300         return b;
37301     },
37302
37303     getCellIndex : function(cell){
37304         var id = String(cell.className).match(this.cellRE);
37305         if(id){
37306             return parseInt(id[1], 10);
37307         }
37308         return 0;
37309     },
37310
37311     findHeaderIndex : function(n){
37312         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37313         return r ? this.getCellIndex(r) : false;
37314     },
37315
37316     findHeaderCell : function(n){
37317         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
37318         return r ? r : false;
37319     },
37320
37321     findRowIndex : function(n){
37322         if(!n){
37323             return false;
37324         }
37325         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
37326         return r ? r.rowIndex : false;
37327     },
37328
37329     findCellIndex : function(node){
37330         var stop = this.el.dom;
37331         while(node && node != stop){
37332             if(this.findRE.test(node.className)){
37333                 return this.getCellIndex(node);
37334             }
37335             node = node.parentNode;
37336         }
37337         return false;
37338     },
37339
37340     getColumnId : function(index){
37341         return this.cm.getColumnId(index);
37342     },
37343
37344     getSplitters : function()
37345     {
37346         if(this.splitterSelector){
37347            return Roo.DomQuery.select(this.splitterSelector);
37348         }else{
37349             return null;
37350       }
37351     },
37352
37353     getSplitter : function(index){
37354         return this.getSplitters()[index];
37355     },
37356
37357     onRowOver : function(e, t){
37358         var row;
37359         if((row = this.findRowIndex(t)) !== false){
37360             this.getRowComposite(row).addClass("x-grid-row-over");
37361         }
37362     },
37363
37364     onRowOut : function(e, t){
37365         var row;
37366         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
37367             this.getRowComposite(row).removeClass("x-grid-row-over");
37368         }
37369     },
37370
37371     renderHeaders : function(){
37372         var cm = this.cm;
37373         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
37374         var cb = [], lb = [], sb = [], lsb = [], p = {};
37375         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37376             p.cellId = "x-grid-hd-0-" + i;
37377             p.splitId = "x-grid-csplit-0-" + i;
37378             p.id = cm.getColumnId(i);
37379             p.title = cm.getColumnTooltip(i) || "";
37380             p.value = cm.getColumnHeader(i) || "";
37381             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
37382             if(!cm.isLocked(i)){
37383                 cb[cb.length] = ct.apply(p);
37384                 sb[sb.length] = st.apply(p);
37385             }else{
37386                 lb[lb.length] = ct.apply(p);
37387                 lsb[lsb.length] = st.apply(p);
37388             }
37389         }
37390         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
37391                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
37392     },
37393
37394     updateHeaders : function(){
37395         var html = this.renderHeaders();
37396         this.lockedHd.update(html[0]);
37397         this.mainHd.update(html[1]);
37398     },
37399
37400     /**
37401      * Focuses the specified row.
37402      * @param {Number} row The row index
37403      */
37404     focusRow : function(row)
37405     {
37406         //Roo.log('GridView.focusRow');
37407         var x = this.scroller.dom.scrollLeft;
37408         this.focusCell(row, 0, false);
37409         this.scroller.dom.scrollLeft = x;
37410     },
37411
37412     /**
37413      * Focuses the specified cell.
37414      * @param {Number} row The row index
37415      * @param {Number} col The column index
37416      * @param {Boolean} hscroll false to disable horizontal scrolling
37417      */
37418     focusCell : function(row, col, hscroll)
37419     {
37420         //Roo.log('GridView.focusCell');
37421         var el = this.ensureVisible(row, col, hscroll);
37422         this.focusEl.alignTo(el, "tl-tl");
37423         if(Roo.isGecko){
37424             this.focusEl.focus();
37425         }else{
37426             this.focusEl.focus.defer(1, this.focusEl);
37427         }
37428     },
37429
37430     /**
37431      * Scrolls the specified cell into view
37432      * @param {Number} row The row index
37433      * @param {Number} col The column index
37434      * @param {Boolean} hscroll false to disable horizontal scrolling
37435      */
37436     ensureVisible : function(row, col, hscroll)
37437     {
37438         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
37439         //return null; //disable for testing.
37440         if(typeof row != "number"){
37441             row = row.rowIndex;
37442         }
37443         if(row < 0 && row >= this.ds.getCount()){
37444             return  null;
37445         }
37446         col = (col !== undefined ? col : 0);
37447         var cm = this.grid.colModel;
37448         while(cm.isHidden(col)){
37449             col++;
37450         }
37451
37452         var el = this.getCell(row, col);
37453         if(!el){
37454             return null;
37455         }
37456         var c = this.scroller.dom;
37457
37458         var ctop = parseInt(el.offsetTop, 10);
37459         var cleft = parseInt(el.offsetLeft, 10);
37460         var cbot = ctop + el.offsetHeight;
37461         var cright = cleft + el.offsetWidth;
37462         
37463         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
37464         var stop = parseInt(c.scrollTop, 10);
37465         var sleft = parseInt(c.scrollLeft, 10);
37466         var sbot = stop + ch;
37467         var sright = sleft + c.clientWidth;
37468         /*
37469         Roo.log('GridView.ensureVisible:' +
37470                 ' ctop:' + ctop +
37471                 ' c.clientHeight:' + c.clientHeight +
37472                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
37473                 ' stop:' + stop +
37474                 ' cbot:' + cbot +
37475                 ' sbot:' + sbot +
37476                 ' ch:' + ch  
37477                 );
37478         */
37479         if(ctop < stop){
37480              c.scrollTop = ctop;
37481             //Roo.log("set scrolltop to ctop DISABLE?");
37482         }else if(cbot > sbot){
37483             //Roo.log("set scrolltop to cbot-ch");
37484             c.scrollTop = cbot-ch;
37485         }
37486         
37487         if(hscroll !== false){
37488             if(cleft < sleft){
37489                 c.scrollLeft = cleft;
37490             }else if(cright > sright){
37491                 c.scrollLeft = cright-c.clientWidth;
37492             }
37493         }
37494          
37495         return el;
37496     },
37497
37498     updateColumns : function(){
37499         this.grid.stopEditing();
37500         var cm = this.grid.colModel, colIds = this.getColumnIds();
37501         //var totalWidth = cm.getTotalWidth();
37502         var pos = 0;
37503         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37504             //if(cm.isHidden(i)) continue;
37505             var w = cm.getColumnWidth(i);
37506             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37507             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
37508         }
37509         this.updateSplitters();
37510     },
37511
37512     generateRules : function(cm){
37513         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
37514         Roo.util.CSS.removeStyleSheet(rulesId);
37515         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37516             var cid = cm.getColumnId(i);
37517             var align = '';
37518             if(cm.config[i].align){
37519                 align = 'text-align:'+cm.config[i].align+';';
37520             }
37521             var hidden = '';
37522             if(cm.isHidden(i)){
37523                 hidden = 'display:none;';
37524             }
37525             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
37526             ruleBuf.push(
37527                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
37528                     this.hdSelector, cid, " {\n", align, width, "}\n",
37529                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
37530                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
37531         }
37532         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37533     },
37534
37535     updateSplitters : function(){
37536         var cm = this.cm, s = this.getSplitters();
37537         if(s){ // splitters not created yet
37538             var pos = 0, locked = true;
37539             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37540                 if(cm.isHidden(i)) continue;
37541                 var w = cm.getColumnWidth(i); // make sure it's a number
37542                 if(!cm.isLocked(i) && locked){
37543                     pos = 0;
37544                     locked = false;
37545                 }
37546                 pos += w;
37547                 s[i].style.left = (pos-this.splitOffset) + "px";
37548             }
37549         }
37550     },
37551
37552     handleHiddenChange : function(colModel, colIndex, hidden){
37553         if(hidden){
37554             this.hideColumn(colIndex);
37555         }else{
37556             this.unhideColumn(colIndex);
37557         }
37558     },
37559
37560     hideColumn : function(colIndex){
37561         var cid = this.getColumnId(colIndex);
37562         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
37563         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
37564         if(Roo.isSafari){
37565             this.updateHeaders();
37566         }
37567         this.updateSplitters();
37568         this.layout();
37569     },
37570
37571     unhideColumn : function(colIndex){
37572         var cid = this.getColumnId(colIndex);
37573         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
37574         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
37575
37576         if(Roo.isSafari){
37577             this.updateHeaders();
37578         }
37579         this.updateSplitters();
37580         this.layout();
37581     },
37582
37583     insertRows : function(dm, firstRow, lastRow, isUpdate){
37584         if(firstRow == 0 && lastRow == dm.getCount()-1){
37585             this.refresh();
37586         }else{
37587             if(!isUpdate){
37588                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
37589             }
37590             var s = this.getScrollState();
37591             var markup = this.renderRows(firstRow, lastRow);
37592             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
37593             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
37594             this.restoreScroll(s);
37595             if(!isUpdate){
37596                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
37597                 this.syncRowHeights(firstRow, lastRow);
37598                 this.stripeRows(firstRow);
37599                 this.layout();
37600             }
37601         }
37602     },
37603
37604     bufferRows : function(markup, target, index){
37605         var before = null, trows = target.rows, tbody = target.tBodies[0];
37606         if(index < trows.length){
37607             before = trows[index];
37608         }
37609         var b = document.createElement("div");
37610         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
37611         var rows = b.firstChild.rows;
37612         for(var i = 0, len = rows.length; i < len; i++){
37613             if(before){
37614                 tbody.insertBefore(rows[0], before);
37615             }else{
37616                 tbody.appendChild(rows[0]);
37617             }
37618         }
37619         b.innerHTML = "";
37620         b = null;
37621     },
37622
37623     deleteRows : function(dm, firstRow, lastRow){
37624         if(dm.getRowCount()<1){
37625             this.fireEvent("beforerefresh", this);
37626             this.mainBody.update("");
37627             this.lockedBody.update("");
37628             this.fireEvent("refresh", this);
37629         }else{
37630             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
37631             var bt = this.getBodyTable();
37632             var tbody = bt.firstChild;
37633             var rows = bt.rows;
37634             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
37635                 tbody.removeChild(rows[firstRow]);
37636             }
37637             this.stripeRows(firstRow);
37638             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
37639         }
37640     },
37641
37642     updateRows : function(dataSource, firstRow, lastRow){
37643         var s = this.getScrollState();
37644         this.refresh();
37645         this.restoreScroll(s);
37646     },
37647
37648     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
37649         if(!noRefresh){
37650            this.refresh();
37651         }
37652         this.updateHeaderSortState();
37653     },
37654
37655     getScrollState : function(){
37656         
37657         var sb = this.scroller.dom;
37658         return {left: sb.scrollLeft, top: sb.scrollTop};
37659     },
37660
37661     stripeRows : function(startRow){
37662         if(!this.grid.stripeRows || this.ds.getCount() < 1){
37663             return;
37664         }
37665         startRow = startRow || 0;
37666         var rows = this.getBodyTable().rows;
37667         var lrows = this.getLockedTable().rows;
37668         var cls = ' x-grid-row-alt ';
37669         for(var i = startRow, len = rows.length; i < len; i++){
37670             var row = rows[i], lrow = lrows[i];
37671             var isAlt = ((i+1) % 2 == 0);
37672             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
37673             if(isAlt == hasAlt){
37674                 continue;
37675             }
37676             if(isAlt){
37677                 row.className += " x-grid-row-alt";
37678             }else{
37679                 row.className = row.className.replace("x-grid-row-alt", "");
37680             }
37681             if(lrow){
37682                 lrow.className = row.className;
37683             }
37684         }
37685     },
37686
37687     restoreScroll : function(state){
37688         //Roo.log('GridView.restoreScroll');
37689         var sb = this.scroller.dom;
37690         sb.scrollLeft = state.left;
37691         sb.scrollTop = state.top;
37692         this.syncScroll();
37693     },
37694
37695     syncScroll : function(){
37696         //Roo.log('GridView.syncScroll');
37697         var sb = this.scroller.dom;
37698         var sh = this.mainHd.dom;
37699         var bs = this.mainBody.dom;
37700         var lv = this.lockedBody.dom;
37701         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
37702         lv.scrollTop = bs.scrollTop = sb.scrollTop;
37703     },
37704
37705     handleScroll : function(e){
37706         this.syncScroll();
37707         var sb = this.scroller.dom;
37708         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
37709         e.stopEvent();
37710     },
37711
37712     handleWheel : function(e){
37713         var d = e.getWheelDelta();
37714         this.scroller.dom.scrollTop -= d*22;
37715         // set this here to prevent jumpy scrolling on large tables
37716         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
37717         e.stopEvent();
37718     },
37719
37720     renderRows : function(startRow, endRow){
37721         // pull in all the crap needed to render rows
37722         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
37723         var colCount = cm.getColumnCount();
37724
37725         if(ds.getCount() < 1){
37726             return ["", ""];
37727         }
37728
37729         // build a map for all the columns
37730         var cs = [];
37731         for(var i = 0; i < colCount; i++){
37732             var name = cm.getDataIndex(i);
37733             cs[i] = {
37734                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
37735                 renderer : cm.getRenderer(i),
37736                 id : cm.getColumnId(i),
37737                 locked : cm.isLocked(i)
37738             };
37739         }
37740
37741         startRow = startRow || 0;
37742         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
37743
37744         // records to render
37745         var rs = ds.getRange(startRow, endRow);
37746
37747         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
37748     },
37749
37750     // As much as I hate to duplicate code, this was branched because FireFox really hates
37751     // [].join("") on strings. The performance difference was substantial enough to
37752     // branch this function
37753     doRender : Roo.isGecko ?
37754             function(cs, rs, ds, startRow, colCount, stripe){
37755                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37756                 // buffers
37757                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37758                 
37759                 var hasListener = this.grid.hasListener('rowclass');
37760                 var rowcfg = {};
37761                 for(var j = 0, len = rs.length; j < len; j++){
37762                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
37763                     for(var i = 0; i < colCount; i++){
37764                         c = cs[i];
37765                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37766                         p.id = c.id;
37767                         p.css = p.attr = "";
37768                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37769                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37770                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37771                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37772                         }
37773                         var markup = ct.apply(p);
37774                         if(!c.locked){
37775                             cb+= markup;
37776                         }else{
37777                             lcb+= markup;
37778                         }
37779                     }
37780                     var alt = [];
37781                     if(stripe && ((rowIndex+1) % 2 == 0)){
37782                         alt.push("x-grid-row-alt")
37783                     }
37784                     if(r.dirty){
37785                         alt.push(  " x-grid-dirty-row");
37786                     }
37787                     rp.cells = lcb;
37788                     if(this.getRowClass){
37789                         alt.push(this.getRowClass(r, rowIndex));
37790                     }
37791                     if (hasListener) {
37792                         rowcfg = {
37793                              
37794                             record: r,
37795                             rowIndex : rowIndex,
37796                             rowClass : ''
37797                         }
37798                         this.grid.fireEvent('rowclass', this, rowcfg);
37799                         alt.push(rowcfg.rowClass);
37800                     }
37801                     rp.alt = alt.join(" ");
37802                     lbuf+= rt.apply(rp);
37803                     rp.cells = cb;
37804                     buf+=  rt.apply(rp);
37805                 }
37806                 return [lbuf, buf];
37807             } :
37808             function(cs, rs, ds, startRow, colCount, stripe){
37809                 var ts = this.templates, ct = ts.cell, rt = ts.row;
37810                 // buffers
37811                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
37812                 var hasListener = this.grid.hasListener('rowclass');
37813  
37814                 var rowcfg = {};
37815                 for(var j = 0, len = rs.length; j < len; j++){
37816                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
37817                     for(var i = 0; i < colCount; i++){
37818                         c = cs[i];
37819                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
37820                         p.id = c.id;
37821                         p.css = p.attr = "";
37822                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
37823                         if(p.value == undefined || p.value === "") p.value = "&#160;";
37824                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
37825                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
37826                         }
37827                         
37828                         var markup = ct.apply(p);
37829                         if(!c.locked){
37830                             cb[cb.length] = markup;
37831                         }else{
37832                             lcb[lcb.length] = markup;
37833                         }
37834                     }
37835                     var alt = [];
37836                     if(stripe && ((rowIndex+1) % 2 == 0)){
37837                         alt.push( "x-grid-row-alt");
37838                     }
37839                     if(r.dirty){
37840                         alt.push(" x-grid-dirty-row");
37841                     }
37842                     rp.cells = lcb;
37843                     if(this.getRowClass){
37844                         alt.push( this.getRowClass(r, rowIndex));
37845                     }
37846                     if (hasListener) {
37847                         rowcfg = {
37848                              
37849                             record: r,
37850                             rowIndex : rowIndex,
37851                             rowClass : ''
37852                         }
37853                         this.grid.fireEvent('rowclass', this, rowcfg);
37854                         alt.push(rowcfg.rowClass);
37855                     }
37856                     rp.alt = alt.join(" ");
37857                     rp.cells = lcb.join("");
37858                     lbuf[lbuf.length] = rt.apply(rp);
37859                     rp.cells = cb.join("");
37860                     buf[buf.length] =  rt.apply(rp);
37861                 }
37862                 return [lbuf.join(""), buf.join("")];
37863             },
37864
37865     renderBody : function(){
37866         var markup = this.renderRows();
37867         var bt = this.templates.body;
37868         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
37869     },
37870
37871     /**
37872      * Refreshes the grid
37873      * @param {Boolean} headersToo
37874      */
37875     refresh : function(headersToo){
37876         this.fireEvent("beforerefresh", this);
37877         this.grid.stopEditing();
37878         var result = this.renderBody();
37879         this.lockedBody.update(result[0]);
37880         this.mainBody.update(result[1]);
37881         if(headersToo === true){
37882             this.updateHeaders();
37883             this.updateColumns();
37884             this.updateSplitters();
37885             this.updateHeaderSortState();
37886         }
37887         this.syncRowHeights();
37888         this.layout();
37889         this.fireEvent("refresh", this);
37890     },
37891
37892     handleColumnMove : function(cm, oldIndex, newIndex){
37893         this.indexMap = null;
37894         var s = this.getScrollState();
37895         this.refresh(true);
37896         this.restoreScroll(s);
37897         this.afterMove(newIndex);
37898     },
37899
37900     afterMove : function(colIndex){
37901         if(this.enableMoveAnim && Roo.enableFx){
37902             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
37903         }
37904         // if multisort - fix sortOrder, and reload..
37905         if (this.grid.dataSource.multiSort) {
37906             // the we can call sort again..
37907             var dm = this.grid.dataSource;
37908             var cm = this.grid.colModel;
37909             var so = [];
37910             for(var i = 0; i < cm.config.length; i++ ) {
37911                 
37912                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
37913                     continue; // dont' bother, it's not in sort list or being set.
37914                 }
37915                 
37916                 so.push(cm.config[i].dataIndex);
37917             };
37918             dm.sortOrder = so;
37919             dm.load(dm.lastOptions);
37920             
37921             
37922         }
37923         
37924     },
37925
37926     updateCell : function(dm, rowIndex, dataIndex){
37927         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
37928         if(typeof colIndex == "undefined"){ // not present in grid
37929             return;
37930         }
37931         var cm = this.grid.colModel;
37932         var cell = this.getCell(rowIndex, colIndex);
37933         var cellText = this.getCellText(rowIndex, colIndex);
37934
37935         var p = {
37936             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
37937             id : cm.getColumnId(colIndex),
37938             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
37939         };
37940         var renderer = cm.getRenderer(colIndex);
37941         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
37942         if(typeof val == "undefined" || val === "") val = "&#160;";
37943         cellText.innerHTML = val;
37944         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
37945         this.syncRowHeights(rowIndex, rowIndex);
37946     },
37947
37948     calcColumnWidth : function(colIndex, maxRowsToMeasure){
37949         var maxWidth = 0;
37950         if(this.grid.autoSizeHeaders){
37951             var h = this.getHeaderCellMeasure(colIndex);
37952             maxWidth = Math.max(maxWidth, h.scrollWidth);
37953         }
37954         var tb, index;
37955         if(this.cm.isLocked(colIndex)){
37956             tb = this.getLockedTable();
37957             index = colIndex;
37958         }else{
37959             tb = this.getBodyTable();
37960             index = colIndex - this.cm.getLockedCount();
37961         }
37962         if(tb && tb.rows){
37963             var rows = tb.rows;
37964             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
37965             for(var i = 0; i < stopIndex; i++){
37966                 var cell = rows[i].childNodes[index].firstChild;
37967                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
37968             }
37969         }
37970         return maxWidth + /*margin for error in IE*/ 5;
37971     },
37972     /**
37973      * Autofit a column to its content.
37974      * @param {Number} colIndex
37975      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
37976      */
37977      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
37978          if(this.cm.isHidden(colIndex)){
37979              return; // can't calc a hidden column
37980          }
37981         if(forceMinSize){
37982             var cid = this.cm.getColumnId(colIndex);
37983             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
37984            if(this.grid.autoSizeHeaders){
37985                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
37986            }
37987         }
37988         var newWidth = this.calcColumnWidth(colIndex);
37989         this.cm.setColumnWidth(colIndex,
37990             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
37991         if(!suppressEvent){
37992             this.grid.fireEvent("columnresize", colIndex, newWidth);
37993         }
37994     },
37995
37996     /**
37997      * Autofits all columns to their content and then expands to fit any extra space in the grid
37998      */
37999      autoSizeColumns : function(){
38000         var cm = this.grid.colModel;
38001         var colCount = cm.getColumnCount();
38002         for(var i = 0; i < colCount; i++){
38003             this.autoSizeColumn(i, true, true);
38004         }
38005         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
38006             this.fitColumns();
38007         }else{
38008             this.updateColumns();
38009             this.layout();
38010         }
38011     },
38012
38013     /**
38014      * Autofits all columns to the grid's width proportionate with their current size
38015      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
38016      */
38017     fitColumns : function(reserveScrollSpace){
38018         var cm = this.grid.colModel;
38019         var colCount = cm.getColumnCount();
38020         var cols = [];
38021         var width = 0;
38022         var i, w;
38023         for (i = 0; i < colCount; i++){
38024             if(!cm.isHidden(i) && !cm.isFixed(i)){
38025                 w = cm.getColumnWidth(i);
38026                 cols.push(i);
38027                 cols.push(w);
38028                 width += w;
38029             }
38030         }
38031         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
38032         if(reserveScrollSpace){
38033             avail -= 17;
38034         }
38035         var frac = (avail - cm.getTotalWidth())/width;
38036         while (cols.length){
38037             w = cols.pop();
38038             i = cols.pop();
38039             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
38040         }
38041         this.updateColumns();
38042         this.layout();
38043     },
38044
38045     onRowSelect : function(rowIndex){
38046         var row = this.getRowComposite(rowIndex);
38047         row.addClass("x-grid-row-selected");
38048     },
38049
38050     onRowDeselect : function(rowIndex){
38051         var row = this.getRowComposite(rowIndex);
38052         row.removeClass("x-grid-row-selected");
38053     },
38054
38055     onCellSelect : function(row, col){
38056         var cell = this.getCell(row, col);
38057         if(cell){
38058             Roo.fly(cell).addClass("x-grid-cell-selected");
38059         }
38060     },
38061
38062     onCellDeselect : function(row, col){
38063         var cell = this.getCell(row, col);
38064         if(cell){
38065             Roo.fly(cell).removeClass("x-grid-cell-selected");
38066         }
38067     },
38068
38069     updateHeaderSortState : function(){
38070         
38071         // sort state can be single { field: xxx, direction : yyy}
38072         // or   { xxx=>ASC , yyy : DESC ..... }
38073         
38074         var mstate = {};
38075         if (!this.ds.multiSort) { 
38076             var state = this.ds.getSortState();
38077             if(!state){
38078                 return;
38079             }
38080             mstate[state.field] = state.direction;
38081             // FIXME... - this is not used here.. but might be elsewhere..
38082             this.sortState = state;
38083             
38084         } else {
38085             mstate = this.ds.sortToggle;
38086         }
38087         //remove existing sort classes..
38088         
38089         var sc = this.sortClasses;
38090         var hds = this.el.select(this.headerSelector).removeClass(sc);
38091         
38092         for(var f in mstate) {
38093         
38094             var sortColumn = this.cm.findColumnIndex(f);
38095             
38096             if(sortColumn != -1){
38097                 var sortDir = mstate[f];        
38098                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
38099             }
38100         }
38101         
38102          
38103         
38104     },
38105
38106
38107     handleHeaderClick : function(g, index,e){
38108         
38109         Roo.log("header click");
38110         
38111         if (Roo.isTouch) {
38112             // touch events on header are handled by context
38113             this.handleHdCtx(g,index,e);
38114             return;
38115         }
38116         
38117         
38118         if(this.headersDisabled){
38119             return;
38120         }
38121         var dm = g.dataSource, cm = g.colModel;
38122         if(!cm.isSortable(index)){
38123             return;
38124         }
38125         g.stopEditing();
38126         
38127         if (dm.multiSort) {
38128             // update the sortOrder
38129             var so = [];
38130             for(var i = 0; i < cm.config.length; i++ ) {
38131                 
38132                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
38133                     continue; // dont' bother, it's not in sort list or being set.
38134                 }
38135                 
38136                 so.push(cm.config[i].dataIndex);
38137             };
38138             dm.sortOrder = so;
38139         }
38140         
38141         
38142         dm.sort(cm.getDataIndex(index));
38143     },
38144
38145
38146     destroy : function(){
38147         if(this.colMenu){
38148             this.colMenu.removeAll();
38149             Roo.menu.MenuMgr.unregister(this.colMenu);
38150             this.colMenu.getEl().remove();
38151             delete this.colMenu;
38152         }
38153         if(this.hmenu){
38154             this.hmenu.removeAll();
38155             Roo.menu.MenuMgr.unregister(this.hmenu);
38156             this.hmenu.getEl().remove();
38157             delete this.hmenu;
38158         }
38159         if(this.grid.enableColumnMove){
38160             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38161             if(dds){
38162                 for(var dd in dds){
38163                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
38164                         var elid = dds[dd].dragElId;
38165                         dds[dd].unreg();
38166                         Roo.get(elid).remove();
38167                     } else if(dds[dd].config.isTarget){
38168                         dds[dd].proxyTop.remove();
38169                         dds[dd].proxyBottom.remove();
38170                         dds[dd].unreg();
38171                     }
38172                     if(Roo.dd.DDM.locationCache[dd]){
38173                         delete Roo.dd.DDM.locationCache[dd];
38174                     }
38175                 }
38176                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
38177             }
38178         }
38179         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
38180         this.bind(null, null);
38181         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
38182     },
38183
38184     handleLockChange : function(){
38185         this.refresh(true);
38186     },
38187
38188     onDenyColumnLock : function(){
38189
38190     },
38191
38192     onDenyColumnHide : function(){
38193
38194     },
38195
38196     handleHdMenuClick : function(item){
38197         var index = this.hdCtxIndex;
38198         var cm = this.cm, ds = this.ds;
38199         switch(item.id){
38200             case "asc":
38201                 ds.sort(cm.getDataIndex(index), "ASC");
38202                 break;
38203             case "desc":
38204                 ds.sort(cm.getDataIndex(index), "DESC");
38205                 break;
38206             case "lock":
38207                 var lc = cm.getLockedCount();
38208                 if(cm.getColumnCount(true) <= lc+1){
38209                     this.onDenyColumnLock();
38210                     return;
38211                 }
38212                 if(lc != index){
38213                     cm.setLocked(index, true, true);
38214                     cm.moveColumn(index, lc);
38215                     this.grid.fireEvent("columnmove", index, lc);
38216                 }else{
38217                     cm.setLocked(index, true);
38218                 }
38219             break;
38220             case "unlock":
38221                 var lc = cm.getLockedCount();
38222                 if((lc-1) != index){
38223                     cm.setLocked(index, false, true);
38224                     cm.moveColumn(index, lc-1);
38225                     this.grid.fireEvent("columnmove", index, lc-1);
38226                 }else{
38227                     cm.setLocked(index, false);
38228                 }
38229             break;
38230             case 'wider': // used to expand cols on touch..
38231             case 'narrow':
38232                 var cw = cm.getColumnWidth(index);
38233                 cw += (item.id == 'wider' ? 1 : -1) * 50;
38234                 cw = Math.max(0, cw);
38235                 cw = Math.min(cw,4000);
38236                 cm.setColumnWidth(index, cw);
38237                 break;
38238                 
38239             default:
38240                 index = cm.getIndexById(item.id.substr(4));
38241                 if(index != -1){
38242                     if(item.checked && cm.getColumnCount(true) <= 1){
38243                         this.onDenyColumnHide();
38244                         return false;
38245                     }
38246                     cm.setHidden(index, item.checked);
38247                 }
38248         }
38249         return true;
38250     },
38251
38252     beforeColMenuShow : function(){
38253         var cm = this.cm,  colCount = cm.getColumnCount();
38254         this.colMenu.removeAll();
38255         for(var i = 0; i < colCount; i++){
38256             this.colMenu.add(new Roo.menu.CheckItem({
38257                 id: "col-"+cm.getColumnId(i),
38258                 text: cm.getColumnHeader(i),
38259                 checked: !cm.isHidden(i),
38260                 hideOnClick:false
38261             }));
38262         }
38263     },
38264
38265     handleHdCtx : function(g, index, e){
38266         e.stopEvent();
38267         var hd = this.getHeaderCell(index);
38268         this.hdCtxIndex = index;
38269         var ms = this.hmenu.items, cm = this.cm;
38270         ms.get("asc").setDisabled(!cm.isSortable(index));
38271         ms.get("desc").setDisabled(!cm.isSortable(index));
38272         if(this.grid.enableColLock !== false){
38273             ms.get("lock").setDisabled(cm.isLocked(index));
38274             ms.get("unlock").setDisabled(!cm.isLocked(index));
38275         }
38276         this.hmenu.show(hd, "tl-bl");
38277     },
38278
38279     handleHdOver : function(e){
38280         var hd = this.findHeaderCell(e.getTarget());
38281         if(hd && !this.headersDisabled){
38282             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
38283                this.fly(hd).addClass("x-grid-hd-over");
38284             }
38285         }
38286     },
38287
38288     handleHdOut : function(e){
38289         var hd = this.findHeaderCell(e.getTarget());
38290         if(hd){
38291             this.fly(hd).removeClass("x-grid-hd-over");
38292         }
38293     },
38294
38295     handleSplitDblClick : function(e, t){
38296         var i = this.getCellIndex(t);
38297         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
38298             this.autoSizeColumn(i, true);
38299             this.layout();
38300         }
38301     },
38302
38303     render : function(){
38304
38305         var cm = this.cm;
38306         var colCount = cm.getColumnCount();
38307
38308         if(this.grid.monitorWindowResize === true){
38309             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38310         }
38311         var header = this.renderHeaders();
38312         var body = this.templates.body.apply({rows:""});
38313         var html = this.templates.master.apply({
38314             lockedBody: body,
38315             body: body,
38316             lockedHeader: header[0],
38317             header: header[1]
38318         });
38319
38320         //this.updateColumns();
38321
38322         this.grid.getGridEl().dom.innerHTML = html;
38323
38324         this.initElements();
38325         
38326         // a kludge to fix the random scolling effect in webkit
38327         this.el.on("scroll", function() {
38328             this.el.dom.scrollTop=0; // hopefully not recursive..
38329         },this);
38330
38331         this.scroller.on("scroll", this.handleScroll, this);
38332         this.lockedBody.on("mousewheel", this.handleWheel, this);
38333         this.mainBody.on("mousewheel", this.handleWheel, this);
38334
38335         this.mainHd.on("mouseover", this.handleHdOver, this);
38336         this.mainHd.on("mouseout", this.handleHdOut, this);
38337         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
38338                 {delegate: "."+this.splitClass});
38339
38340         this.lockedHd.on("mouseover", this.handleHdOver, this);
38341         this.lockedHd.on("mouseout", this.handleHdOut, this);
38342         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
38343                 {delegate: "."+this.splitClass});
38344
38345         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
38346             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38347         }
38348
38349         this.updateSplitters();
38350
38351         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
38352             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38353             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
38354         }
38355
38356         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
38357             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
38358             this.hmenu.add(
38359                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
38360                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
38361             );
38362             if(this.grid.enableColLock !== false){
38363                 this.hmenu.add('-',
38364                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
38365                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
38366                 );
38367             }
38368             if (Roo.isTouch) {
38369                  this.hmenu.add('-',
38370                     {id:"wider", text: this.columnsWiderText},
38371                     {id:"narrow", text: this.columnsNarrowText }
38372                 );
38373                 
38374                  
38375             }
38376             
38377             if(this.grid.enableColumnHide !== false){
38378
38379                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
38380                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
38381                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
38382
38383                 this.hmenu.add('-',
38384                     {id:"columns", text: this.columnsText, menu: this.colMenu}
38385                 );
38386             }
38387             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
38388
38389             this.grid.on("headercontextmenu", this.handleHdCtx, this);
38390         }
38391
38392         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
38393             this.dd = new Roo.grid.GridDragZone(this.grid, {
38394                 ddGroup : this.grid.ddGroup || 'GridDD'
38395             });
38396             
38397         }
38398
38399         /*
38400         for(var i = 0; i < colCount; i++){
38401             if(cm.isHidden(i)){
38402                 this.hideColumn(i);
38403             }
38404             if(cm.config[i].align){
38405                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
38406                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
38407             }
38408         }*/
38409         
38410         this.updateHeaderSortState();
38411
38412         this.beforeInitialResize();
38413         this.layout(true);
38414
38415         // two part rendering gives faster view to the user
38416         this.renderPhase2.defer(1, this);
38417     },
38418
38419     renderPhase2 : function(){
38420         // render the rows now
38421         this.refresh();
38422         if(this.grid.autoSizeColumns){
38423             this.autoSizeColumns();
38424         }
38425     },
38426
38427     beforeInitialResize : function(){
38428
38429     },
38430
38431     onColumnSplitterMoved : function(i, w){
38432         this.userResized = true;
38433         var cm = this.grid.colModel;
38434         cm.setColumnWidth(i, w, true);
38435         var cid = cm.getColumnId(i);
38436         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38437         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
38438         this.updateSplitters();
38439         this.layout();
38440         this.grid.fireEvent("columnresize", i, w);
38441     },
38442
38443     syncRowHeights : function(startIndex, endIndex){
38444         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
38445             startIndex = startIndex || 0;
38446             var mrows = this.getBodyTable().rows;
38447             var lrows = this.getLockedTable().rows;
38448             var len = mrows.length-1;
38449             endIndex = Math.min(endIndex || len, len);
38450             for(var i = startIndex; i <= endIndex; i++){
38451                 var m = mrows[i], l = lrows[i];
38452                 var h = Math.max(m.offsetHeight, l.offsetHeight);
38453                 m.style.height = l.style.height = h + "px";
38454             }
38455         }
38456     },
38457
38458     layout : function(initialRender, is2ndPass){
38459         var g = this.grid;
38460         var auto = g.autoHeight;
38461         var scrollOffset = 16;
38462         var c = g.getGridEl(), cm = this.cm,
38463                 expandCol = g.autoExpandColumn,
38464                 gv = this;
38465         //c.beginMeasure();
38466
38467         if(!c.dom.offsetWidth){ // display:none?
38468             if(initialRender){
38469                 this.lockedWrap.show();
38470                 this.mainWrap.show();
38471             }
38472             return;
38473         }
38474
38475         var hasLock = this.cm.isLocked(0);
38476
38477         var tbh = this.headerPanel.getHeight();
38478         var bbh = this.footerPanel.getHeight();
38479
38480         if(auto){
38481             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
38482             var newHeight = ch + c.getBorderWidth("tb");
38483             if(g.maxHeight){
38484                 newHeight = Math.min(g.maxHeight, newHeight);
38485             }
38486             c.setHeight(newHeight);
38487         }
38488
38489         if(g.autoWidth){
38490             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
38491         }
38492
38493         var s = this.scroller;
38494
38495         var csize = c.getSize(true);
38496
38497         this.el.setSize(csize.width, csize.height);
38498
38499         this.headerPanel.setWidth(csize.width);
38500         this.footerPanel.setWidth(csize.width);
38501
38502         var hdHeight = this.mainHd.getHeight();
38503         var vw = csize.width;
38504         var vh = csize.height - (tbh + bbh);
38505
38506         s.setSize(vw, vh);
38507
38508         var bt = this.getBodyTable();
38509         var ltWidth = hasLock ?
38510                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
38511
38512         var scrollHeight = bt.offsetHeight;
38513         var scrollWidth = ltWidth + bt.offsetWidth;
38514         var vscroll = false, hscroll = false;
38515
38516         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
38517
38518         var lw = this.lockedWrap, mw = this.mainWrap;
38519         var lb = this.lockedBody, mb = this.mainBody;
38520
38521         setTimeout(function(){
38522             var t = s.dom.offsetTop;
38523             var w = s.dom.clientWidth,
38524                 h = s.dom.clientHeight;
38525
38526             lw.setTop(t);
38527             lw.setSize(ltWidth, h);
38528
38529             mw.setLeftTop(ltWidth, t);
38530             mw.setSize(w-ltWidth, h);
38531
38532             lb.setHeight(h-hdHeight);
38533             mb.setHeight(h-hdHeight);
38534
38535             if(is2ndPass !== true && !gv.userResized && expandCol){
38536                 // high speed resize without full column calculation
38537                 
38538                 var ci = cm.getIndexById(expandCol);
38539                 if (ci < 0) {
38540                     ci = cm.findColumnIndex(expandCol);
38541                 }
38542                 ci = Math.max(0, ci); // make sure it's got at least the first col.
38543                 var expandId = cm.getColumnId(ci);
38544                 var  tw = cm.getTotalWidth(false);
38545                 var currentWidth = cm.getColumnWidth(ci);
38546                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
38547                 if(currentWidth != cw){
38548                     cm.setColumnWidth(ci, cw, true);
38549                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38550                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
38551                     gv.updateSplitters();
38552                     gv.layout(false, true);
38553                 }
38554             }
38555
38556             if(initialRender){
38557                 lw.show();
38558                 mw.show();
38559             }
38560             //c.endMeasure();
38561         }, 10);
38562     },
38563
38564     onWindowResize : function(){
38565         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
38566             return;
38567         }
38568         this.layout();
38569     },
38570
38571     appendFooter : function(parentEl){
38572         return null;
38573     },
38574
38575     sortAscText : "Sort Ascending",
38576     sortDescText : "Sort Descending",
38577     lockText : "Lock Column",
38578     unlockText : "Unlock Column",
38579     columnsText : "Columns",
38580  
38581     columnsWiderText : "Wider",
38582     columnsNarrowText : "Thinner"
38583 });
38584
38585
38586 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
38587     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
38588     this.proxy.el.addClass('x-grid3-col-dd');
38589 };
38590
38591 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
38592     handleMouseDown : function(e){
38593
38594     },
38595
38596     callHandleMouseDown : function(e){
38597         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
38598     }
38599 });
38600 /*
38601  * Based on:
38602  * Ext JS Library 1.1.1
38603  * Copyright(c) 2006-2007, Ext JS, LLC.
38604  *
38605  * Originally Released Under LGPL - original licence link has changed is not relivant.
38606  *
38607  * Fork - LGPL
38608  * <script type="text/javascript">
38609  */
38610  
38611 // private
38612 // This is a support class used internally by the Grid components
38613 Roo.grid.SplitDragZone = function(grid, hd, hd2){
38614     this.grid = grid;
38615     this.view = grid.getView();
38616     this.proxy = this.view.resizeProxy;
38617     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
38618         "gridSplitters" + this.grid.getGridEl().id, {
38619         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
38620     });
38621     this.setHandleElId(Roo.id(hd));
38622     this.setOuterHandleElId(Roo.id(hd2));
38623     this.scroll = false;
38624 };
38625 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
38626     fly: Roo.Element.fly,
38627
38628     b4StartDrag : function(x, y){
38629         this.view.headersDisabled = true;
38630         this.proxy.setHeight(this.view.mainWrap.getHeight());
38631         var w = this.cm.getColumnWidth(this.cellIndex);
38632         var minw = Math.max(w-this.grid.minColumnWidth, 0);
38633         this.resetConstraints();
38634         this.setXConstraint(minw, 1000);
38635         this.setYConstraint(0, 0);
38636         this.minX = x - minw;
38637         this.maxX = x + 1000;
38638         this.startPos = x;
38639         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
38640     },
38641
38642
38643     handleMouseDown : function(e){
38644         ev = Roo.EventObject.setEvent(e);
38645         var t = this.fly(ev.getTarget());
38646         if(t.hasClass("x-grid-split")){
38647             this.cellIndex = this.view.getCellIndex(t.dom);
38648             this.split = t.dom;
38649             this.cm = this.grid.colModel;
38650             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
38651                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
38652             }
38653         }
38654     },
38655
38656     endDrag : function(e){
38657         this.view.headersDisabled = false;
38658         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
38659         var diff = endX - this.startPos;
38660         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
38661     },
38662
38663     autoOffset : function(){
38664         this.setDelta(0,0);
38665     }
38666 });/*
38667  * Based on:
38668  * Ext JS Library 1.1.1
38669  * Copyright(c) 2006-2007, Ext JS, LLC.
38670  *
38671  * Originally Released Under LGPL - original licence link has changed is not relivant.
38672  *
38673  * Fork - LGPL
38674  * <script type="text/javascript">
38675  */
38676  
38677 // private
38678 // This is a support class used internally by the Grid components
38679 Roo.grid.GridDragZone = function(grid, config){
38680     this.view = grid.getView();
38681     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
38682     if(this.view.lockedBody){
38683         this.setHandleElId(Roo.id(this.view.mainBody.dom));
38684         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
38685     }
38686     this.scroll = false;
38687     this.grid = grid;
38688     this.ddel = document.createElement('div');
38689     this.ddel.className = 'x-grid-dd-wrap';
38690 };
38691
38692 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
38693     ddGroup : "GridDD",
38694
38695     getDragData : function(e){
38696         var t = Roo.lib.Event.getTarget(e);
38697         var rowIndex = this.view.findRowIndex(t);
38698         var sm = this.grid.selModel;
38699             
38700         //Roo.log(rowIndex);
38701         
38702         if (sm.getSelectedCell) {
38703             // cell selection..
38704             if (!sm.getSelectedCell()) {
38705                 return false;
38706             }
38707             if (rowIndex != sm.getSelectedCell()[0]) {
38708                 return false;
38709             }
38710         
38711         }
38712         
38713         if(rowIndex !== false){
38714             
38715             // if editorgrid.. 
38716             
38717             
38718             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
38719                
38720             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
38721               //  
38722             //}
38723             if (e.hasModifier()){
38724                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
38725             }
38726             
38727             Roo.log("getDragData");
38728             
38729             return {
38730                 grid: this.grid,
38731                 ddel: this.ddel,
38732                 rowIndex: rowIndex,
38733                 selections:sm.getSelections ? sm.getSelections() : (
38734                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
38735                 )
38736             };
38737         }
38738         return false;
38739     },
38740
38741     onInitDrag : function(e){
38742         var data = this.dragData;
38743         this.ddel.innerHTML = this.grid.getDragDropText();
38744         this.proxy.update(this.ddel);
38745         // fire start drag?
38746     },
38747
38748     afterRepair : function(){
38749         this.dragging = false;
38750     },
38751
38752     getRepairXY : function(e, data){
38753         return false;
38754     },
38755
38756     onEndDrag : function(data, e){
38757         // fire end drag?
38758     },
38759
38760     onValidDrop : function(dd, e, id){
38761         // fire drag drop?
38762         this.hideProxy();
38763     },
38764
38765     beforeInvalidDrop : function(e, id){
38766
38767     }
38768 });/*
38769  * Based on:
38770  * Ext JS Library 1.1.1
38771  * Copyright(c) 2006-2007, Ext JS, LLC.
38772  *
38773  * Originally Released Under LGPL - original licence link has changed is not relivant.
38774  *
38775  * Fork - LGPL
38776  * <script type="text/javascript">
38777  */
38778  
38779
38780 /**
38781  * @class Roo.grid.ColumnModel
38782  * @extends Roo.util.Observable
38783  * This is the default implementation of a ColumnModel used by the Grid. It defines
38784  * the columns in the grid.
38785  * <br>Usage:<br>
38786  <pre><code>
38787  var colModel = new Roo.grid.ColumnModel([
38788         {header: "Ticker", width: 60, sortable: true, locked: true},
38789         {header: "Company Name", width: 150, sortable: true},
38790         {header: "Market Cap.", width: 100, sortable: true},
38791         {header: "$ Sales", width: 100, sortable: true, renderer: money},
38792         {header: "Employees", width: 100, sortable: true, resizable: false}
38793  ]);
38794  </code></pre>
38795  * <p>
38796  
38797  * The config options listed for this class are options which may appear in each
38798  * individual column definition.
38799  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
38800  * @constructor
38801  * @param {Object} config An Array of column config objects. See this class's
38802  * config objects for details.
38803 */
38804 Roo.grid.ColumnModel = function(config){
38805         /**
38806      * The config passed into the constructor
38807      */
38808     this.config = config;
38809     this.lookup = {};
38810
38811     // if no id, create one
38812     // if the column does not have a dataIndex mapping,
38813     // map it to the order it is in the config
38814     for(var i = 0, len = config.length; i < len; i++){
38815         var c = config[i];
38816         if(typeof c.dataIndex == "undefined"){
38817             c.dataIndex = i;
38818         }
38819         if(typeof c.renderer == "string"){
38820             c.renderer = Roo.util.Format[c.renderer];
38821         }
38822         if(typeof c.id == "undefined"){
38823             c.id = Roo.id();
38824         }
38825         if(c.editor && c.editor.xtype){
38826             c.editor  = Roo.factory(c.editor, Roo.grid);
38827         }
38828         if(c.editor && c.editor.isFormField){
38829             c.editor = new Roo.grid.GridEditor(c.editor);
38830         }
38831         this.lookup[c.id] = c;
38832     }
38833
38834     /**
38835      * The width of columns which have no width specified (defaults to 100)
38836      * @type Number
38837      */
38838     this.defaultWidth = 100;
38839
38840     /**
38841      * Default sortable of columns which have no sortable specified (defaults to false)
38842      * @type Boolean
38843      */
38844     this.defaultSortable = false;
38845
38846     this.addEvents({
38847         /**
38848              * @event widthchange
38849              * Fires when the width of a column changes.
38850              * @param {ColumnModel} this
38851              * @param {Number} columnIndex The column index
38852              * @param {Number} newWidth The new width
38853              */
38854             "widthchange": true,
38855         /**
38856              * @event headerchange
38857              * Fires when the text of a header changes.
38858              * @param {ColumnModel} this
38859              * @param {Number} columnIndex The column index
38860              * @param {Number} newText The new header text
38861              */
38862             "headerchange": true,
38863         /**
38864              * @event hiddenchange
38865              * Fires when a column is hidden or "unhidden".
38866              * @param {ColumnModel} this
38867              * @param {Number} columnIndex The column index
38868              * @param {Boolean} hidden true if hidden, false otherwise
38869              */
38870             "hiddenchange": true,
38871             /**
38872          * @event columnmoved
38873          * Fires when a column is moved.
38874          * @param {ColumnModel} this
38875          * @param {Number} oldIndex
38876          * @param {Number} newIndex
38877          */
38878         "columnmoved" : true,
38879         /**
38880          * @event columlockchange
38881          * Fires when a column's locked state is changed
38882          * @param {ColumnModel} this
38883          * @param {Number} colIndex
38884          * @param {Boolean} locked true if locked
38885          */
38886         "columnlockchange" : true
38887     });
38888     Roo.grid.ColumnModel.superclass.constructor.call(this);
38889 };
38890 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
38891     /**
38892      * @cfg {String} header The header text to display in the Grid view.
38893      */
38894     /**
38895      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
38896      * {@link Roo.data.Record} definition from which to draw the column's value. If not
38897      * specified, the column's index is used as an index into the Record's data Array.
38898      */
38899     /**
38900      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
38901      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
38902      */
38903     /**
38904      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
38905      * Defaults to the value of the {@link #defaultSortable} property.
38906      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
38907      */
38908     /**
38909      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
38910      */
38911     /**
38912      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
38913      */
38914     /**
38915      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
38916      */
38917     /**
38918      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
38919      */
38920     /**
38921      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
38922      * given the cell's data value. See {@link #setRenderer}. If not specified, the
38923      * default renderer uses the raw data value. If an object is returned (bootstrap only)
38924      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
38925      */
38926        /**
38927      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
38928      */
38929     /**
38930      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
38931      */
38932     /**
38933      * @cfg {String} cursor (Optional)
38934      */
38935     /**
38936      * @cfg {String} tooltip (Optional)
38937      */
38938     /**
38939      * Returns the id of the column at the specified index.
38940      * @param {Number} index The column index
38941      * @return {String} the id
38942      */
38943     getColumnId : function(index){
38944         return this.config[index].id;
38945     },
38946
38947     /**
38948      * Returns the column for a specified id.
38949      * @param {String} id The column id
38950      * @return {Object} the column
38951      */
38952     getColumnById : function(id){
38953         return this.lookup[id];
38954     },
38955
38956     
38957     /**
38958      * Returns the column for a specified dataIndex.
38959      * @param {String} dataIndex The column dataIndex
38960      * @return {Object|Boolean} the column or false if not found
38961      */
38962     getColumnByDataIndex: function(dataIndex){
38963         var index = this.findColumnIndex(dataIndex);
38964         return index > -1 ? this.config[index] : false;
38965     },
38966     
38967     /**
38968      * Returns the index for a specified column id.
38969      * @param {String} id The column id
38970      * @return {Number} the index, or -1 if not found
38971      */
38972     getIndexById : function(id){
38973         for(var i = 0, len = this.config.length; i < len; i++){
38974             if(this.config[i].id == id){
38975                 return i;
38976             }
38977         }
38978         return -1;
38979     },
38980     
38981     /**
38982      * Returns the index for a specified column dataIndex.
38983      * @param {String} dataIndex The column dataIndex
38984      * @return {Number} the index, or -1 if not found
38985      */
38986     
38987     findColumnIndex : function(dataIndex){
38988         for(var i = 0, len = this.config.length; i < len; i++){
38989             if(this.config[i].dataIndex == dataIndex){
38990                 return i;
38991             }
38992         }
38993         return -1;
38994     },
38995     
38996     
38997     moveColumn : function(oldIndex, newIndex){
38998         var c = this.config[oldIndex];
38999         this.config.splice(oldIndex, 1);
39000         this.config.splice(newIndex, 0, c);
39001         this.dataMap = null;
39002         this.fireEvent("columnmoved", this, oldIndex, newIndex);
39003     },
39004
39005     isLocked : function(colIndex){
39006         return this.config[colIndex].locked === true;
39007     },
39008
39009     setLocked : function(colIndex, value, suppressEvent){
39010         if(this.isLocked(colIndex) == value){
39011             return;
39012         }
39013         this.config[colIndex].locked = value;
39014         if(!suppressEvent){
39015             this.fireEvent("columnlockchange", this, colIndex, value);
39016         }
39017     },
39018
39019     getTotalLockedWidth : function(){
39020         var totalWidth = 0;
39021         for(var i = 0; i < this.config.length; i++){
39022             if(this.isLocked(i) && !this.isHidden(i)){
39023                 this.totalWidth += this.getColumnWidth(i);
39024             }
39025         }
39026         return totalWidth;
39027     },
39028
39029     getLockedCount : function(){
39030         for(var i = 0, len = this.config.length; i < len; i++){
39031             if(!this.isLocked(i)){
39032                 return i;
39033             }
39034         }
39035     },
39036
39037     /**
39038      * Returns the number of columns.
39039      * @return {Number}
39040      */
39041     getColumnCount : function(visibleOnly){
39042         if(visibleOnly === true){
39043             var c = 0;
39044             for(var i = 0, len = this.config.length; i < len; i++){
39045                 if(!this.isHidden(i)){
39046                     c++;
39047                 }
39048             }
39049             return c;
39050         }
39051         return this.config.length;
39052     },
39053
39054     /**
39055      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
39056      * @param {Function} fn
39057      * @param {Object} scope (optional)
39058      * @return {Array} result
39059      */
39060     getColumnsBy : function(fn, scope){
39061         var r = [];
39062         for(var i = 0, len = this.config.length; i < len; i++){
39063             var c = this.config[i];
39064             if(fn.call(scope||this, c, i) === true){
39065                 r[r.length] = c;
39066             }
39067         }
39068         return r;
39069     },
39070
39071     /**
39072      * Returns true if the specified column is sortable.
39073      * @param {Number} col The column index
39074      * @return {Boolean}
39075      */
39076     isSortable : function(col){
39077         if(typeof this.config[col].sortable == "undefined"){
39078             return this.defaultSortable;
39079         }
39080         return this.config[col].sortable;
39081     },
39082
39083     /**
39084      * Returns the rendering (formatting) function defined for the column.
39085      * @param {Number} col The column index.
39086      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
39087      */
39088     getRenderer : function(col){
39089         if(!this.config[col].renderer){
39090             return Roo.grid.ColumnModel.defaultRenderer;
39091         }
39092         return this.config[col].renderer;
39093     },
39094
39095     /**
39096      * Sets the rendering (formatting) function for a column.
39097      * @param {Number} col The column index
39098      * @param {Function} fn The function to use to process the cell's raw data
39099      * to return HTML markup for the grid view. The render function is called with
39100      * the following parameters:<ul>
39101      * <li>Data value.</li>
39102      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
39103      * <li>css A CSS style string to apply to the table cell.</li>
39104      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
39105      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
39106      * <li>Row index</li>
39107      * <li>Column index</li>
39108      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
39109      */
39110     setRenderer : function(col, fn){
39111         this.config[col].renderer = fn;
39112     },
39113
39114     /**
39115      * Returns the width for the specified column.
39116      * @param {Number} col The column index
39117      * @return {Number}
39118      */
39119     getColumnWidth : function(col){
39120         return this.config[col].width * 1 || this.defaultWidth;
39121     },
39122
39123     /**
39124      * Sets the width for a column.
39125      * @param {Number} col The column index
39126      * @param {Number} width The new width
39127      */
39128     setColumnWidth : function(col, width, suppressEvent){
39129         this.config[col].width = width;
39130         this.totalWidth = null;
39131         if(!suppressEvent){
39132              this.fireEvent("widthchange", this, col, width);
39133         }
39134     },
39135
39136     /**
39137      * Returns the total width of all columns.
39138      * @param {Boolean} includeHidden True to include hidden column widths
39139      * @return {Number}
39140      */
39141     getTotalWidth : function(includeHidden){
39142         if(!this.totalWidth){
39143             this.totalWidth = 0;
39144             for(var i = 0, len = this.config.length; i < len; i++){
39145                 if(includeHidden || !this.isHidden(i)){
39146                     this.totalWidth += this.getColumnWidth(i);
39147                 }
39148             }
39149         }
39150         return this.totalWidth;
39151     },
39152
39153     /**
39154      * Returns the header for the specified column.
39155      * @param {Number} col The column index
39156      * @return {String}
39157      */
39158     getColumnHeader : function(col){
39159         return this.config[col].header;
39160     },
39161
39162     /**
39163      * Sets the header for a column.
39164      * @param {Number} col The column index
39165      * @param {String} header The new header
39166      */
39167     setColumnHeader : function(col, header){
39168         this.config[col].header = header;
39169         this.fireEvent("headerchange", this, col, header);
39170     },
39171
39172     /**
39173      * Returns the tooltip for the specified column.
39174      * @param {Number} col The column index
39175      * @return {String}
39176      */
39177     getColumnTooltip : function(col){
39178             return this.config[col].tooltip;
39179     },
39180     /**
39181      * Sets the tooltip for a column.
39182      * @param {Number} col The column index
39183      * @param {String} tooltip The new tooltip
39184      */
39185     setColumnTooltip : function(col, tooltip){
39186             this.config[col].tooltip = tooltip;
39187     },
39188
39189     /**
39190      * Returns the dataIndex for the specified column.
39191      * @param {Number} col The column index
39192      * @return {Number}
39193      */
39194     getDataIndex : function(col){
39195         return this.config[col].dataIndex;
39196     },
39197
39198     /**
39199      * Sets the dataIndex for a column.
39200      * @param {Number} col The column index
39201      * @param {Number} dataIndex The new dataIndex
39202      */
39203     setDataIndex : function(col, dataIndex){
39204         this.config[col].dataIndex = dataIndex;
39205     },
39206
39207     
39208     
39209     /**
39210      * Returns true if the cell is editable.
39211      * @param {Number} colIndex The column index
39212      * @param {Number} rowIndex The row index
39213      * @return {Boolean}
39214      */
39215     isCellEditable : function(colIndex, rowIndex){
39216         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
39217     },
39218
39219     /**
39220      * Returns the editor defined for the cell/column.
39221      * return false or null to disable editing.
39222      * @param {Number} colIndex The column index
39223      * @param {Number} rowIndex The row index
39224      * @return {Object}
39225      */
39226     getCellEditor : function(colIndex, rowIndex){
39227         return this.config[colIndex].editor;
39228     },
39229
39230     /**
39231      * Sets if a column is editable.
39232      * @param {Number} col The column index
39233      * @param {Boolean} editable True if the column is editable
39234      */
39235     setEditable : function(col, editable){
39236         this.config[col].editable = editable;
39237     },
39238
39239
39240     /**
39241      * Returns true if the column is hidden.
39242      * @param {Number} colIndex The column index
39243      * @return {Boolean}
39244      */
39245     isHidden : function(colIndex){
39246         return this.config[colIndex].hidden;
39247     },
39248
39249
39250     /**
39251      * Returns true if the column width cannot be changed
39252      */
39253     isFixed : function(colIndex){
39254         return this.config[colIndex].fixed;
39255     },
39256
39257     /**
39258      * Returns true if the column can be resized
39259      * @return {Boolean}
39260      */
39261     isResizable : function(colIndex){
39262         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
39263     },
39264     /**
39265      * Sets if a column is hidden.
39266      * @param {Number} colIndex The column index
39267      * @param {Boolean} hidden True if the column is hidden
39268      */
39269     setHidden : function(colIndex, hidden){
39270         this.config[colIndex].hidden = hidden;
39271         this.totalWidth = null;
39272         this.fireEvent("hiddenchange", this, colIndex, hidden);
39273     },
39274
39275     /**
39276      * Sets the editor for a column.
39277      * @param {Number} col The column index
39278      * @param {Object} editor The editor object
39279      */
39280     setEditor : function(col, editor){
39281         this.config[col].editor = editor;
39282     }
39283 });
39284
39285 Roo.grid.ColumnModel.defaultRenderer = function(value){
39286         if(typeof value == "string" && value.length < 1){
39287             return "&#160;";
39288         }
39289         return value;
39290 };
39291
39292 // Alias for backwards compatibility
39293 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
39294 /*
39295  * Based on:
39296  * Ext JS Library 1.1.1
39297  * Copyright(c) 2006-2007, Ext JS, LLC.
39298  *
39299  * Originally Released Under LGPL - original licence link has changed is not relivant.
39300  *
39301  * Fork - LGPL
39302  * <script type="text/javascript">
39303  */
39304
39305 /**
39306  * @class Roo.grid.AbstractSelectionModel
39307  * @extends Roo.util.Observable
39308  * Abstract base class for grid SelectionModels.  It provides the interface that should be
39309  * implemented by descendant classes.  This class should not be directly instantiated.
39310  * @constructor
39311  */
39312 Roo.grid.AbstractSelectionModel = function(){
39313     this.locked = false;
39314     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
39315 };
39316
39317 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
39318     /** @ignore Called by the grid automatically. Do not call directly. */
39319     init : function(grid){
39320         this.grid = grid;
39321         this.initEvents();
39322     },
39323
39324     /**
39325      * Locks the selections.
39326      */
39327     lock : function(){
39328         this.locked = true;
39329     },
39330
39331     /**
39332      * Unlocks the selections.
39333      */
39334     unlock : function(){
39335         this.locked = false;
39336     },
39337
39338     /**
39339      * Returns true if the selections are locked.
39340      * @return {Boolean}
39341      */
39342     isLocked : function(){
39343         return this.locked;
39344     }
39345 });/*
39346  * Based on:
39347  * Ext JS Library 1.1.1
39348  * Copyright(c) 2006-2007, Ext JS, LLC.
39349  *
39350  * Originally Released Under LGPL - original licence link has changed is not relivant.
39351  *
39352  * Fork - LGPL
39353  * <script type="text/javascript">
39354  */
39355 /**
39356  * @extends Roo.grid.AbstractSelectionModel
39357  * @class Roo.grid.RowSelectionModel
39358  * The default SelectionModel used by {@link Roo.grid.Grid}.
39359  * It supports multiple selections and keyboard selection/navigation. 
39360  * @constructor
39361  * @param {Object} config
39362  */
39363 Roo.grid.RowSelectionModel = function(config){
39364     Roo.apply(this, config);
39365     this.selections = new Roo.util.MixedCollection(false, function(o){
39366         return o.id;
39367     });
39368
39369     this.last = false;
39370     this.lastActive = false;
39371
39372     this.addEvents({
39373         /**
39374              * @event selectionchange
39375              * Fires when the selection changes
39376              * @param {SelectionModel} this
39377              */
39378             "selectionchange" : true,
39379         /**
39380              * @event afterselectionchange
39381              * Fires after the selection changes (eg. by key press or clicking)
39382              * @param {SelectionModel} this
39383              */
39384             "afterselectionchange" : true,
39385         /**
39386              * @event beforerowselect
39387              * Fires when a row is selected being selected, return false to cancel.
39388              * @param {SelectionModel} this
39389              * @param {Number} rowIndex The selected index
39390              * @param {Boolean} keepExisting False if other selections will be cleared
39391              */
39392             "beforerowselect" : true,
39393         /**
39394              * @event rowselect
39395              * Fires when a row is selected.
39396              * @param {SelectionModel} this
39397              * @param {Number} rowIndex The selected index
39398              * @param {Roo.data.Record} r The record
39399              */
39400             "rowselect" : true,
39401         /**
39402              * @event rowdeselect
39403              * Fires when a row is deselected.
39404              * @param {SelectionModel} this
39405              * @param {Number} rowIndex The selected index
39406              */
39407         "rowdeselect" : true
39408     });
39409     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
39410     this.locked = false;
39411 };
39412
39413 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
39414     /**
39415      * @cfg {Boolean} singleSelect
39416      * True to allow selection of only one row at a time (defaults to false)
39417      */
39418     singleSelect : false,
39419
39420     // private
39421     initEvents : function(){
39422
39423         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
39424             this.grid.on("mousedown", this.handleMouseDown, this);
39425         }else{ // allow click to work like normal
39426             this.grid.on("rowclick", this.handleDragableRowClick, this);
39427         }
39428
39429         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
39430             "up" : function(e){
39431                 if(!e.shiftKey){
39432                     this.selectPrevious(e.shiftKey);
39433                 }else if(this.last !== false && this.lastActive !== false){
39434                     var last = this.last;
39435                     this.selectRange(this.last,  this.lastActive-1);
39436                     this.grid.getView().focusRow(this.lastActive);
39437                     if(last !== false){
39438                         this.last = last;
39439                     }
39440                 }else{
39441                     this.selectFirstRow();
39442                 }
39443                 this.fireEvent("afterselectionchange", this);
39444             },
39445             "down" : function(e){
39446                 if(!e.shiftKey){
39447                     this.selectNext(e.shiftKey);
39448                 }else if(this.last !== false && this.lastActive !== false){
39449                     var last = this.last;
39450                     this.selectRange(this.last,  this.lastActive+1);
39451                     this.grid.getView().focusRow(this.lastActive);
39452                     if(last !== false){
39453                         this.last = last;
39454                     }
39455                 }else{
39456                     this.selectFirstRow();
39457                 }
39458                 this.fireEvent("afterselectionchange", this);
39459             },
39460             scope: this
39461         });
39462
39463         var view = this.grid.view;
39464         view.on("refresh", this.onRefresh, this);
39465         view.on("rowupdated", this.onRowUpdated, this);
39466         view.on("rowremoved", this.onRemove, this);
39467     },
39468
39469     // private
39470     onRefresh : function(){
39471         var ds = this.grid.dataSource, i, v = this.grid.view;
39472         var s = this.selections;
39473         s.each(function(r){
39474             if((i = ds.indexOfId(r.id)) != -1){
39475                 v.onRowSelect(i);
39476             }else{
39477                 s.remove(r);
39478             }
39479         });
39480     },
39481
39482     // private
39483     onRemove : function(v, index, r){
39484         this.selections.remove(r);
39485     },
39486
39487     // private
39488     onRowUpdated : function(v, index, r){
39489         if(this.isSelected(r)){
39490             v.onRowSelect(index);
39491         }
39492     },
39493
39494     /**
39495      * Select records.
39496      * @param {Array} records The records to select
39497      * @param {Boolean} keepExisting (optional) True to keep existing selections
39498      */
39499     selectRecords : function(records, keepExisting){
39500         if(!keepExisting){
39501             this.clearSelections();
39502         }
39503         var ds = this.grid.dataSource;
39504         for(var i = 0, len = records.length; i < len; i++){
39505             this.selectRow(ds.indexOf(records[i]), true);
39506         }
39507     },
39508
39509     /**
39510      * Gets the number of selected rows.
39511      * @return {Number}
39512      */
39513     getCount : function(){
39514         return this.selections.length;
39515     },
39516
39517     /**
39518      * Selects the first row in the grid.
39519      */
39520     selectFirstRow : function(){
39521         this.selectRow(0);
39522     },
39523
39524     /**
39525      * Select the last row.
39526      * @param {Boolean} keepExisting (optional) True to keep existing selections
39527      */
39528     selectLastRow : function(keepExisting){
39529         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
39530     },
39531
39532     /**
39533      * Selects the row immediately following the last selected row.
39534      * @param {Boolean} keepExisting (optional) True to keep existing selections
39535      */
39536     selectNext : function(keepExisting){
39537         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
39538             this.selectRow(this.last+1, keepExisting);
39539             this.grid.getView().focusRow(this.last);
39540         }
39541     },
39542
39543     /**
39544      * Selects the row that precedes the last selected row.
39545      * @param {Boolean} keepExisting (optional) True to keep existing selections
39546      */
39547     selectPrevious : function(keepExisting){
39548         if(this.last){
39549             this.selectRow(this.last-1, keepExisting);
39550             this.grid.getView().focusRow(this.last);
39551         }
39552     },
39553
39554     /**
39555      * Returns the selected records
39556      * @return {Array} Array of selected records
39557      */
39558     getSelections : function(){
39559         return [].concat(this.selections.items);
39560     },
39561
39562     /**
39563      * Returns the first selected record.
39564      * @return {Record}
39565      */
39566     getSelected : function(){
39567         return this.selections.itemAt(0);
39568     },
39569
39570
39571     /**
39572      * Clears all selections.
39573      */
39574     clearSelections : function(fast){
39575         if(this.locked) return;
39576         if(fast !== true){
39577             var ds = this.grid.dataSource;
39578             var s = this.selections;
39579             s.each(function(r){
39580                 this.deselectRow(ds.indexOfId(r.id));
39581             }, this);
39582             s.clear();
39583         }else{
39584             this.selections.clear();
39585         }
39586         this.last = false;
39587     },
39588
39589
39590     /**
39591      * Selects all rows.
39592      */
39593     selectAll : function(){
39594         if(this.locked) return;
39595         this.selections.clear();
39596         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
39597             this.selectRow(i, true);
39598         }
39599     },
39600
39601     /**
39602      * Returns True if there is a selection.
39603      * @return {Boolean}
39604      */
39605     hasSelection : function(){
39606         return this.selections.length > 0;
39607     },
39608
39609     /**
39610      * Returns True if the specified row is selected.
39611      * @param {Number/Record} record The record or index of the record to check
39612      * @return {Boolean}
39613      */
39614     isSelected : function(index){
39615         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
39616         return (r && this.selections.key(r.id) ? true : false);
39617     },
39618
39619     /**
39620      * Returns True if the specified record id is selected.
39621      * @param {String} id The id of record to check
39622      * @return {Boolean}
39623      */
39624     isIdSelected : function(id){
39625         return (this.selections.key(id) ? true : false);
39626     },
39627
39628     // private
39629     handleMouseDown : function(e, t){
39630         var view = this.grid.getView(), rowIndex;
39631         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
39632             return;
39633         };
39634         if(e.shiftKey && this.last !== false){
39635             var last = this.last;
39636             this.selectRange(last, rowIndex, e.ctrlKey);
39637             this.last = last; // reset the last
39638             view.focusRow(rowIndex);
39639         }else{
39640             var isSelected = this.isSelected(rowIndex);
39641             if(e.button !== 0 && isSelected){
39642                 view.focusRow(rowIndex);
39643             }else if(e.ctrlKey && isSelected){
39644                 this.deselectRow(rowIndex);
39645             }else if(!isSelected){
39646                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
39647                 view.focusRow(rowIndex);
39648             }
39649         }
39650         this.fireEvent("afterselectionchange", this);
39651     },
39652     // private
39653     handleDragableRowClick :  function(grid, rowIndex, e) 
39654     {
39655         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
39656             this.selectRow(rowIndex, false);
39657             grid.view.focusRow(rowIndex);
39658              this.fireEvent("afterselectionchange", this);
39659         }
39660     },
39661     
39662     /**
39663      * Selects multiple rows.
39664      * @param {Array} rows Array of the indexes of the row to select
39665      * @param {Boolean} keepExisting (optional) True to keep existing selections
39666      */
39667     selectRows : function(rows, keepExisting){
39668         if(!keepExisting){
39669             this.clearSelections();
39670         }
39671         for(var i = 0, len = rows.length; i < len; i++){
39672             this.selectRow(rows[i], true);
39673         }
39674     },
39675
39676     /**
39677      * Selects a range of rows. All rows in between startRow and endRow are also selected.
39678      * @param {Number} startRow The index of the first row in the range
39679      * @param {Number} endRow The index of the last row in the range
39680      * @param {Boolean} keepExisting (optional) True to retain existing selections
39681      */
39682     selectRange : function(startRow, endRow, keepExisting){
39683         if(this.locked) return;
39684         if(!keepExisting){
39685             this.clearSelections();
39686         }
39687         if(startRow <= endRow){
39688             for(var i = startRow; i <= endRow; i++){
39689                 this.selectRow(i, true);
39690             }
39691         }else{
39692             for(var i = startRow; i >= endRow; i--){
39693                 this.selectRow(i, true);
39694             }
39695         }
39696     },
39697
39698     /**
39699      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
39700      * @param {Number} startRow The index of the first row in the range
39701      * @param {Number} endRow The index of the last row in the range
39702      */
39703     deselectRange : function(startRow, endRow, preventViewNotify){
39704         if(this.locked) return;
39705         for(var i = startRow; i <= endRow; i++){
39706             this.deselectRow(i, preventViewNotify);
39707         }
39708     },
39709
39710     /**
39711      * Selects a row.
39712      * @param {Number} row The index of the row to select
39713      * @param {Boolean} keepExisting (optional) True to keep existing selections
39714      */
39715     selectRow : function(index, keepExisting, preventViewNotify){
39716         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
39717         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
39718             if(!keepExisting || this.singleSelect){
39719                 this.clearSelections();
39720             }
39721             var r = this.grid.dataSource.getAt(index);
39722             this.selections.add(r);
39723             this.last = this.lastActive = index;
39724             if(!preventViewNotify){
39725                 this.grid.getView().onRowSelect(index);
39726             }
39727             this.fireEvent("rowselect", this, index, r);
39728             this.fireEvent("selectionchange", this);
39729         }
39730     },
39731
39732     /**
39733      * Deselects a row.
39734      * @param {Number} row The index of the row to deselect
39735      */
39736     deselectRow : function(index, preventViewNotify){
39737         if(this.locked) return;
39738         if(this.last == index){
39739             this.last = false;
39740         }
39741         if(this.lastActive == index){
39742             this.lastActive = false;
39743         }
39744         var r = this.grid.dataSource.getAt(index);
39745         this.selections.remove(r);
39746         if(!preventViewNotify){
39747             this.grid.getView().onRowDeselect(index);
39748         }
39749         this.fireEvent("rowdeselect", this, index);
39750         this.fireEvent("selectionchange", this);
39751     },
39752
39753     // private
39754     restoreLast : function(){
39755         if(this._last){
39756             this.last = this._last;
39757         }
39758     },
39759
39760     // private
39761     acceptsNav : function(row, col, cm){
39762         return !cm.isHidden(col) && cm.isCellEditable(col, row);
39763     },
39764
39765     // private
39766     onEditorKey : function(field, e){
39767         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
39768         if(k == e.TAB){
39769             e.stopEvent();
39770             ed.completeEdit();
39771             if(e.shiftKey){
39772                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
39773             }else{
39774                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
39775             }
39776         }else if(k == e.ENTER && !e.ctrlKey){
39777             e.stopEvent();
39778             ed.completeEdit();
39779             if(e.shiftKey){
39780                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
39781             }else{
39782                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
39783             }
39784         }else if(k == e.ESC){
39785             ed.cancelEdit();
39786         }
39787         if(newCell){
39788             g.startEditing(newCell[0], newCell[1]);
39789         }
39790     }
39791 });/*
39792  * Based on:
39793  * Ext JS Library 1.1.1
39794  * Copyright(c) 2006-2007, Ext JS, LLC.
39795  *
39796  * Originally Released Under LGPL - original licence link has changed is not relivant.
39797  *
39798  * Fork - LGPL
39799  * <script type="text/javascript">
39800  */
39801 /**
39802  * @class Roo.grid.CellSelectionModel
39803  * @extends Roo.grid.AbstractSelectionModel
39804  * This class provides the basic implementation for cell selection in a grid.
39805  * @constructor
39806  * @param {Object} config The object containing the configuration of this model.
39807  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
39808  */
39809 Roo.grid.CellSelectionModel = function(config){
39810     Roo.apply(this, config);
39811
39812     this.selection = null;
39813
39814     this.addEvents({
39815         /**
39816              * @event beforerowselect
39817              * Fires before a cell is selected.
39818              * @param {SelectionModel} this
39819              * @param {Number} rowIndex The selected row index
39820              * @param {Number} colIndex The selected cell index
39821              */
39822             "beforecellselect" : true,
39823         /**
39824              * @event cellselect
39825              * Fires when a cell is selected.
39826              * @param {SelectionModel} this
39827              * @param {Number} rowIndex The selected row index
39828              * @param {Number} colIndex The selected cell index
39829              */
39830             "cellselect" : true,
39831         /**
39832              * @event selectionchange
39833              * Fires when the active selection changes.
39834              * @param {SelectionModel} this
39835              * @param {Object} selection null for no selection or an object (o) with two properties
39836                 <ul>
39837                 <li>o.record: the record object for the row the selection is in</li>
39838                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
39839                 </ul>
39840              */
39841             "selectionchange" : true,
39842         /**
39843              * @event tabend
39844              * Fires when the tab (or enter) was pressed on the last editable cell
39845              * You can use this to trigger add new row.
39846              * @param {SelectionModel} this
39847              */
39848             "tabend" : true,
39849          /**
39850              * @event beforeeditnext
39851              * Fires before the next editable sell is made active
39852              * You can use this to skip to another cell or fire the tabend
39853              *    if you set cell to false
39854              * @param {Object} eventdata object : { cell : [ row, col ] } 
39855              */
39856             "beforeeditnext" : true
39857     });
39858     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
39859 };
39860
39861 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
39862     
39863     enter_is_tab: false,
39864
39865     /** @ignore */
39866     initEvents : function(){
39867         this.grid.on("mousedown", this.handleMouseDown, this);
39868         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
39869         var view = this.grid.view;
39870         view.on("refresh", this.onViewChange, this);
39871         view.on("rowupdated", this.onRowUpdated, this);
39872         view.on("beforerowremoved", this.clearSelections, this);
39873         view.on("beforerowsinserted", this.clearSelections, this);
39874         if(this.grid.isEditor){
39875             this.grid.on("beforeedit", this.beforeEdit,  this);
39876         }
39877     },
39878
39879         //private
39880     beforeEdit : function(e){
39881         this.select(e.row, e.column, false, true, e.record);
39882     },
39883
39884         //private
39885     onRowUpdated : function(v, index, r){
39886         if(this.selection && this.selection.record == r){
39887             v.onCellSelect(index, this.selection.cell[1]);
39888         }
39889     },
39890
39891         //private
39892     onViewChange : function(){
39893         this.clearSelections(true);
39894     },
39895
39896         /**
39897          * Returns the currently selected cell,.
39898          * @return {Array} The selected cell (row, column) or null if none selected.
39899          */
39900     getSelectedCell : function(){
39901         return this.selection ? this.selection.cell : null;
39902     },
39903
39904     /**
39905      * Clears all selections.
39906      * @param {Boolean} true to prevent the gridview from being notified about the change.
39907      */
39908     clearSelections : function(preventNotify){
39909         var s = this.selection;
39910         if(s){
39911             if(preventNotify !== true){
39912                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
39913             }
39914             this.selection = null;
39915             this.fireEvent("selectionchange", this, null);
39916         }
39917     },
39918
39919     /**
39920      * Returns true if there is a selection.
39921      * @return {Boolean}
39922      */
39923     hasSelection : function(){
39924         return this.selection ? true : false;
39925     },
39926
39927     /** @ignore */
39928     handleMouseDown : function(e, t){
39929         var v = this.grid.getView();
39930         if(this.isLocked()){
39931             return;
39932         };
39933         var row = v.findRowIndex(t);
39934         var cell = v.findCellIndex(t);
39935         if(row !== false && cell !== false){
39936             this.select(row, cell);
39937         }
39938     },
39939
39940     /**
39941      * Selects a cell.
39942      * @param {Number} rowIndex
39943      * @param {Number} collIndex
39944      */
39945     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
39946         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
39947             this.clearSelections();
39948             r = r || this.grid.dataSource.getAt(rowIndex);
39949             this.selection = {
39950                 record : r,
39951                 cell : [rowIndex, colIndex]
39952             };
39953             if(!preventViewNotify){
39954                 var v = this.grid.getView();
39955                 v.onCellSelect(rowIndex, colIndex);
39956                 if(preventFocus !== true){
39957                     v.focusCell(rowIndex, colIndex);
39958                 }
39959             }
39960             this.fireEvent("cellselect", this, rowIndex, colIndex);
39961             this.fireEvent("selectionchange", this, this.selection);
39962         }
39963     },
39964
39965         //private
39966     isSelectable : function(rowIndex, colIndex, cm){
39967         return !cm.isHidden(colIndex);
39968     },
39969
39970     /** @ignore */
39971     handleKeyDown : function(e){
39972         //Roo.log('Cell Sel Model handleKeyDown');
39973         if(!e.isNavKeyPress()){
39974             return;
39975         }
39976         var g = this.grid, s = this.selection;
39977         if(!s){
39978             e.stopEvent();
39979             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
39980             if(cell){
39981                 this.select(cell[0], cell[1]);
39982             }
39983             return;
39984         }
39985         var sm = this;
39986         var walk = function(row, col, step){
39987             return g.walkCells(row, col, step, sm.isSelectable,  sm);
39988         };
39989         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
39990         var newCell;
39991
39992       
39993
39994         switch(k){
39995             case e.TAB:
39996                 // handled by onEditorKey
39997                 if (g.isEditor && g.editing) {
39998                     return;
39999                 }
40000                 if(e.shiftKey) {
40001                     newCell = walk(r, c-1, -1);
40002                 } else {
40003                     newCell = walk(r, c+1, 1);
40004                 }
40005                 break;
40006             
40007             case e.DOWN:
40008                newCell = walk(r+1, c, 1);
40009                 break;
40010             
40011             case e.UP:
40012                 newCell = walk(r-1, c, -1);
40013                 break;
40014             
40015             case e.RIGHT:
40016                 newCell = walk(r, c+1, 1);
40017                 break;
40018             
40019             case e.LEFT:
40020                 newCell = walk(r, c-1, -1);
40021                 break;
40022             
40023             case e.ENTER:
40024                 
40025                 if(g.isEditor && !g.editing){
40026                    g.startEditing(r, c);
40027                    e.stopEvent();
40028                    return;
40029                 }
40030                 
40031                 
40032              break;
40033         };
40034         if(newCell){
40035             this.select(newCell[0], newCell[1]);
40036             e.stopEvent();
40037             
40038         }
40039     },
40040
40041     acceptsNav : function(row, col, cm){
40042         return !cm.isHidden(col) && cm.isCellEditable(col, row);
40043     },
40044     /**
40045      * Selects a cell.
40046      * @param {Number} field (not used) - as it's normally used as a listener
40047      * @param {Number} e - event - fake it by using
40048      *
40049      * var e = Roo.EventObjectImpl.prototype;
40050      * e.keyCode = e.TAB
40051      *
40052      * 
40053      */
40054     onEditorKey : function(field, e){
40055         
40056         var k = e.getKey(),
40057             newCell,
40058             g = this.grid,
40059             ed = g.activeEditor,
40060             forward = false;
40061         ///Roo.log('onEditorKey' + k);
40062         
40063         
40064         if (this.enter_is_tab && k == e.ENTER) {
40065             k = e.TAB;
40066         }
40067         
40068         if(k == e.TAB){
40069             if(e.shiftKey){
40070                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40071             }else{
40072                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40073                 forward = true;
40074             }
40075             
40076             e.stopEvent();
40077             
40078         } else if(k == e.ENTER &&  !e.ctrlKey){
40079             ed.completeEdit();
40080             e.stopEvent();
40081             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40082         
40083                 } else if(k == e.ESC){
40084             ed.cancelEdit();
40085         }
40086                 
40087         if (newCell) {
40088             var ecall = { cell : newCell, forward : forward };
40089             this.fireEvent('beforeeditnext', ecall );
40090             newCell = ecall.cell;
40091                         forward = ecall.forward;
40092         }
40093                 
40094         if(newCell){
40095             //Roo.log('next cell after edit');
40096             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
40097         } else if (forward) {
40098             // tabbed past last
40099             this.fireEvent.defer(100, this, ['tabend',this]);
40100         }
40101     }
40102 });/*
40103  * Based on:
40104  * Ext JS Library 1.1.1
40105  * Copyright(c) 2006-2007, Ext JS, LLC.
40106  *
40107  * Originally Released Under LGPL - original licence link has changed is not relivant.
40108  *
40109  * Fork - LGPL
40110  * <script type="text/javascript">
40111  */
40112  
40113 /**
40114  * @class Roo.grid.EditorGrid
40115  * @extends Roo.grid.Grid
40116  * Class for creating and editable grid.
40117  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
40118  * The container MUST have some type of size defined for the grid to fill. The container will be 
40119  * automatically set to position relative if it isn't already.
40120  * @param {Object} dataSource The data model to bind to
40121  * @param {Object} colModel The column model with info about this grid's columns
40122  */
40123 Roo.grid.EditorGrid = function(container, config){
40124     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
40125     this.getGridEl().addClass("xedit-grid");
40126
40127     if(!this.selModel){
40128         this.selModel = new Roo.grid.CellSelectionModel();
40129     }
40130
40131     this.activeEditor = null;
40132
40133         this.addEvents({
40134             /**
40135              * @event beforeedit
40136              * Fires before cell editing is triggered. The edit event object has the following properties <br />
40137              * <ul style="padding:5px;padding-left:16px;">
40138              * <li>grid - This grid</li>
40139              * <li>record - The record being edited</li>
40140              * <li>field - The field name being edited</li>
40141              * <li>value - The value for the field being edited.</li>
40142              * <li>row - The grid row index</li>
40143              * <li>column - The grid column index</li>
40144              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40145              * </ul>
40146              * @param {Object} e An edit event (see above for description)
40147              */
40148             "beforeedit" : true,
40149             /**
40150              * @event afteredit
40151              * Fires after a cell is edited. <br />
40152              * <ul style="padding:5px;padding-left:16px;">
40153              * <li>grid - This grid</li>
40154              * <li>record - The record being edited</li>
40155              * <li>field - The field name being edited</li>
40156              * <li>value - The value being set</li>
40157              * <li>originalValue - The original value for the field, before the edit.</li>
40158              * <li>row - The grid row index</li>
40159              * <li>column - The grid column index</li>
40160              * </ul>
40161              * @param {Object} e An edit event (see above for description)
40162              */
40163             "afteredit" : true,
40164             /**
40165              * @event validateedit
40166              * Fires after a cell is edited, but before the value is set in the record. 
40167          * You can use this to modify the value being set in the field, Return false
40168              * to cancel the change. The edit event object has the following properties <br />
40169              * <ul style="padding:5px;padding-left:16px;">
40170          * <li>editor - This editor</li>
40171              * <li>grid - This grid</li>
40172              * <li>record - The record being edited</li>
40173              * <li>field - The field name being edited</li>
40174              * <li>value - The value being set</li>
40175              * <li>originalValue - The original value for the field, before the edit.</li>
40176              * <li>row - The grid row index</li>
40177              * <li>column - The grid column index</li>
40178              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
40179              * </ul>
40180              * @param {Object} e An edit event (see above for description)
40181              */
40182             "validateedit" : true
40183         });
40184     this.on("bodyscroll", this.stopEditing,  this);
40185     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
40186 };
40187
40188 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
40189     /**
40190      * @cfg {Number} clicksToEdit
40191      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
40192      */
40193     clicksToEdit: 2,
40194
40195     // private
40196     isEditor : true,
40197     // private
40198     trackMouseOver: false, // causes very odd FF errors
40199
40200     onCellDblClick : function(g, row, col){
40201         this.startEditing(row, col);
40202     },
40203
40204     onEditComplete : function(ed, value, startValue){
40205         this.editing = false;
40206         this.activeEditor = null;
40207         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
40208         var r = ed.record;
40209         var field = this.colModel.getDataIndex(ed.col);
40210         var e = {
40211             grid: this,
40212             record: r,
40213             field: field,
40214             originalValue: startValue,
40215             value: value,
40216             row: ed.row,
40217             column: ed.col,
40218             cancel:false,
40219             editor: ed
40220         };
40221         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
40222         cell.show();
40223           
40224         if(String(value) !== String(startValue)){
40225             
40226             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
40227                 r.set(field, e.value);
40228                 // if we are dealing with a combo box..
40229                 // then we also set the 'name' colum to be the displayField
40230                 if (ed.field.displayField && ed.field.name) {
40231                     r.set(ed.field.name, ed.field.el.dom.value);
40232                 }
40233                 
40234                 delete e.cancel; //?? why!!!
40235                 this.fireEvent("afteredit", e);
40236             }
40237         } else {
40238             this.fireEvent("afteredit", e); // always fire it!
40239         }
40240         this.view.focusCell(ed.row, ed.col);
40241     },
40242
40243     /**
40244      * Starts editing the specified for the specified row/column
40245      * @param {Number} rowIndex
40246      * @param {Number} colIndex
40247      */
40248     startEditing : function(row, col){
40249         this.stopEditing();
40250         if(this.colModel.isCellEditable(col, row)){
40251             this.view.ensureVisible(row, col, true);
40252           
40253             var r = this.dataSource.getAt(row);
40254             var field = this.colModel.getDataIndex(col);
40255             var cell = Roo.get(this.view.getCell(row,col));
40256             var e = {
40257                 grid: this,
40258                 record: r,
40259                 field: field,
40260                 value: r.data[field],
40261                 row: row,
40262                 column: col,
40263                 cancel:false 
40264             };
40265             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
40266                 this.editing = true;
40267                 var ed = this.colModel.getCellEditor(col, row);
40268                 
40269                 if (!ed) {
40270                     return;
40271                 }
40272                 if(!ed.rendered){
40273                     ed.render(ed.parentEl || document.body);
40274                 }
40275                 ed.field.reset();
40276                
40277                 cell.hide();
40278                 
40279                 (function(){ // complex but required for focus issues in safari, ie and opera
40280                     ed.row = row;
40281                     ed.col = col;
40282                     ed.record = r;
40283                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
40284                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
40285                     this.activeEditor = ed;
40286                     var v = r.data[field];
40287                     ed.startEdit(this.view.getCell(row, col), v);
40288                     // combo's with 'displayField and name set
40289                     if (ed.field.displayField && ed.field.name) {
40290                         ed.field.el.dom.value = r.data[ed.field.name];
40291                     }
40292                     
40293                     
40294                 }).defer(50, this);
40295             }
40296         }
40297     },
40298         
40299     /**
40300      * Stops any active editing
40301      */
40302     stopEditing : function(){
40303         if(this.activeEditor){
40304             this.activeEditor.completeEdit();
40305         }
40306         this.activeEditor = null;
40307     },
40308         
40309          /**
40310      * Called to get grid's drag proxy text, by default returns this.ddText.
40311      * @return {String}
40312      */
40313     getDragDropText : function(){
40314         var count = this.selModel.getSelectedCell() ? 1 : 0;
40315         return String.format(this.ddText, count, count == 1 ? '' : 's');
40316     }
40317         
40318 });/*
40319  * Based on:
40320  * Ext JS Library 1.1.1
40321  * Copyright(c) 2006-2007, Ext JS, LLC.
40322  *
40323  * Originally Released Under LGPL - original licence link has changed is not relivant.
40324  *
40325  * Fork - LGPL
40326  * <script type="text/javascript">
40327  */
40328
40329 // private - not really -- you end up using it !
40330 // This is a support class used internally by the Grid components
40331
40332 /**
40333  * @class Roo.grid.GridEditor
40334  * @extends Roo.Editor
40335  * Class for creating and editable grid elements.
40336  * @param {Object} config any settings (must include field)
40337  */
40338 Roo.grid.GridEditor = function(field, config){
40339     if (!config && field.field) {
40340         config = field;
40341         field = Roo.factory(config.field, Roo.form);
40342     }
40343     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
40344     field.monitorTab = false;
40345 };
40346
40347 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
40348     
40349     /**
40350      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
40351      */
40352     
40353     alignment: "tl-tl",
40354     autoSize: "width",
40355     hideEl : false,
40356     cls: "x-small-editor x-grid-editor",
40357     shim:false,
40358     shadow:"frame"
40359 });/*
40360  * Based on:
40361  * Ext JS Library 1.1.1
40362  * Copyright(c) 2006-2007, Ext JS, LLC.
40363  *
40364  * Originally Released Under LGPL - original licence link has changed is not relivant.
40365  *
40366  * Fork - LGPL
40367  * <script type="text/javascript">
40368  */
40369   
40370
40371   
40372 Roo.grid.PropertyRecord = Roo.data.Record.create([
40373     {name:'name',type:'string'},  'value'
40374 ]);
40375
40376
40377 Roo.grid.PropertyStore = function(grid, source){
40378     this.grid = grid;
40379     this.store = new Roo.data.Store({
40380         recordType : Roo.grid.PropertyRecord
40381     });
40382     this.store.on('update', this.onUpdate,  this);
40383     if(source){
40384         this.setSource(source);
40385     }
40386     Roo.grid.PropertyStore.superclass.constructor.call(this);
40387 };
40388
40389
40390
40391 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
40392     setSource : function(o){
40393         this.source = o;
40394         this.store.removeAll();
40395         var data = [];
40396         for(var k in o){
40397             if(this.isEditableValue(o[k])){
40398                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
40399             }
40400         }
40401         this.store.loadRecords({records: data}, {}, true);
40402     },
40403
40404     onUpdate : function(ds, record, type){
40405         if(type == Roo.data.Record.EDIT){
40406             var v = record.data['value'];
40407             var oldValue = record.modified['value'];
40408             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
40409                 this.source[record.id] = v;
40410                 record.commit();
40411                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
40412             }else{
40413                 record.reject();
40414             }
40415         }
40416     },
40417
40418     getProperty : function(row){
40419        return this.store.getAt(row);
40420     },
40421
40422     isEditableValue: function(val){
40423         if(val && val instanceof Date){
40424             return true;
40425         }else if(typeof val == 'object' || typeof val == 'function'){
40426             return false;
40427         }
40428         return true;
40429     },
40430
40431     setValue : function(prop, value){
40432         this.source[prop] = value;
40433         this.store.getById(prop).set('value', value);
40434     },
40435
40436     getSource : function(){
40437         return this.source;
40438     }
40439 });
40440
40441 Roo.grid.PropertyColumnModel = function(grid, store){
40442     this.grid = grid;
40443     var g = Roo.grid;
40444     g.PropertyColumnModel.superclass.constructor.call(this, [
40445         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
40446         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
40447     ]);
40448     this.store = store;
40449     this.bselect = Roo.DomHelper.append(document.body, {
40450         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
40451             {tag: 'option', value: 'true', html: 'true'},
40452             {tag: 'option', value: 'false', html: 'false'}
40453         ]
40454     });
40455     Roo.id(this.bselect);
40456     var f = Roo.form;
40457     this.editors = {
40458         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
40459         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
40460         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
40461         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
40462         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
40463     };
40464     this.renderCellDelegate = this.renderCell.createDelegate(this);
40465     this.renderPropDelegate = this.renderProp.createDelegate(this);
40466 };
40467
40468 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
40469     
40470     
40471     nameText : 'Name',
40472     valueText : 'Value',
40473     
40474     dateFormat : 'm/j/Y',
40475     
40476     
40477     renderDate : function(dateVal){
40478         return dateVal.dateFormat(this.dateFormat);
40479     },
40480
40481     renderBool : function(bVal){
40482         return bVal ? 'true' : 'false';
40483     },
40484
40485     isCellEditable : function(colIndex, rowIndex){
40486         return colIndex == 1;
40487     },
40488
40489     getRenderer : function(col){
40490         return col == 1 ?
40491             this.renderCellDelegate : this.renderPropDelegate;
40492     },
40493
40494     renderProp : function(v){
40495         return this.getPropertyName(v);
40496     },
40497
40498     renderCell : function(val){
40499         var rv = val;
40500         if(val instanceof Date){
40501             rv = this.renderDate(val);
40502         }else if(typeof val == 'boolean'){
40503             rv = this.renderBool(val);
40504         }
40505         return Roo.util.Format.htmlEncode(rv);
40506     },
40507
40508     getPropertyName : function(name){
40509         var pn = this.grid.propertyNames;
40510         return pn && pn[name] ? pn[name] : name;
40511     },
40512
40513     getCellEditor : function(colIndex, rowIndex){
40514         var p = this.store.getProperty(rowIndex);
40515         var n = p.data['name'], val = p.data['value'];
40516         
40517         if(typeof(this.grid.customEditors[n]) == 'string'){
40518             return this.editors[this.grid.customEditors[n]];
40519         }
40520         if(typeof(this.grid.customEditors[n]) != 'undefined'){
40521             return this.grid.customEditors[n];
40522         }
40523         if(val instanceof Date){
40524             return this.editors['date'];
40525         }else if(typeof val == 'number'){
40526             return this.editors['number'];
40527         }else if(typeof val == 'boolean'){
40528             return this.editors['boolean'];
40529         }else{
40530             return this.editors['string'];
40531         }
40532     }
40533 });
40534
40535 /**
40536  * @class Roo.grid.PropertyGrid
40537  * @extends Roo.grid.EditorGrid
40538  * This class represents the  interface of a component based property grid control.
40539  * <br><br>Usage:<pre><code>
40540  var grid = new Roo.grid.PropertyGrid("my-container-id", {
40541       
40542  });
40543  // set any options
40544  grid.render();
40545  * </code></pre>
40546   
40547  * @constructor
40548  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40549  * The container MUST have some type of size defined for the grid to fill. The container will be
40550  * automatically set to position relative if it isn't already.
40551  * @param {Object} config A config object that sets properties on this grid.
40552  */
40553 Roo.grid.PropertyGrid = function(container, config){
40554     config = config || {};
40555     var store = new Roo.grid.PropertyStore(this);
40556     this.store = store;
40557     var cm = new Roo.grid.PropertyColumnModel(this, store);
40558     store.store.sort('name', 'ASC');
40559     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
40560         ds: store.store,
40561         cm: cm,
40562         enableColLock:false,
40563         enableColumnMove:false,
40564         stripeRows:false,
40565         trackMouseOver: false,
40566         clicksToEdit:1
40567     }, config));
40568     this.getGridEl().addClass('x-props-grid');
40569     this.lastEditRow = null;
40570     this.on('columnresize', this.onColumnResize, this);
40571     this.addEvents({
40572          /**
40573              * @event beforepropertychange
40574              * Fires before a property changes (return false to stop?)
40575              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40576              * @param {String} id Record Id
40577              * @param {String} newval New Value
40578          * @param {String} oldval Old Value
40579              */
40580         "beforepropertychange": true,
40581         /**
40582              * @event propertychange
40583              * Fires after a property changes
40584              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
40585              * @param {String} id Record Id
40586              * @param {String} newval New Value
40587          * @param {String} oldval Old Value
40588              */
40589         "propertychange": true
40590     });
40591     this.customEditors = this.customEditors || {};
40592 };
40593 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
40594     
40595      /**
40596      * @cfg {Object} customEditors map of colnames=> custom editors.
40597      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
40598      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
40599      * false disables editing of the field.
40600          */
40601     
40602       /**
40603      * @cfg {Object} propertyNames map of property Names to their displayed value
40604          */
40605     
40606     render : function(){
40607         Roo.grid.PropertyGrid.superclass.render.call(this);
40608         this.autoSize.defer(100, this);
40609     },
40610
40611     autoSize : function(){
40612         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
40613         if(this.view){
40614             this.view.fitColumns();
40615         }
40616     },
40617
40618     onColumnResize : function(){
40619         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
40620         this.autoSize();
40621     },
40622     /**
40623      * Sets the data for the Grid
40624      * accepts a Key => Value object of all the elements avaiable.
40625      * @param {Object} data  to appear in grid.
40626      */
40627     setSource : function(source){
40628         this.store.setSource(source);
40629         //this.autoSize();
40630     },
40631     /**
40632      * Gets all the data from the grid.
40633      * @return {Object} data  data stored in grid
40634      */
40635     getSource : function(){
40636         return this.store.getSource();
40637     }
40638 });/*
40639   
40640  * Licence LGPL
40641  
40642  */
40643  
40644 /**
40645  * @class Roo.grid.Calendar
40646  * @extends Roo.util.Grid
40647  * This class extends the Grid to provide a calendar widget
40648  * <br><br>Usage:<pre><code>
40649  var grid = new Roo.grid.Calendar("my-container-id", {
40650      ds: myDataStore,
40651      cm: myColModel,
40652      selModel: mySelectionModel,
40653      autoSizeColumns: true,
40654      monitorWindowResize: false,
40655      trackMouseOver: true
40656      eventstore : real data store..
40657  });
40658  // set any options
40659  grid.render();
40660   
40661   * @constructor
40662  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
40663  * The container MUST have some type of size defined for the grid to fill. The container will be
40664  * automatically set to position relative if it isn't already.
40665  * @param {Object} config A config object that sets properties on this grid.
40666  */
40667 Roo.grid.Calendar = function(container, config){
40668         // initialize the container
40669         this.container = Roo.get(container);
40670         this.container.update("");
40671         this.container.setStyle("overflow", "hidden");
40672     this.container.addClass('x-grid-container');
40673
40674     this.id = this.container.id;
40675
40676     Roo.apply(this, config);
40677     // check and correct shorthanded configs
40678     
40679     var rows = [];
40680     var d =1;
40681     for (var r = 0;r < 6;r++) {
40682         
40683         rows[r]=[];
40684         for (var c =0;c < 7;c++) {
40685             rows[r][c]= '';
40686         }
40687     }
40688     if (this.eventStore) {
40689         this.eventStore= Roo.factory(this.eventStore, Roo.data);
40690         this.eventStore.on('load',this.onLoad, this);
40691         this.eventStore.on('beforeload',this.clearEvents, this);
40692          
40693     }
40694     
40695     this.dataSource = new Roo.data.Store({
40696             proxy: new Roo.data.MemoryProxy(rows),
40697             reader: new Roo.data.ArrayReader({}, [
40698                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
40699     });
40700
40701     this.dataSource.load();
40702     this.ds = this.dataSource;
40703     this.ds.xmodule = this.xmodule || false;
40704     
40705     
40706     var cellRender = function(v,x,r)
40707     {
40708         return String.format(
40709             '<div class="fc-day  fc-widget-content"><div>' +
40710                 '<div class="fc-event-container"></div>' +
40711                 '<div class="fc-day-number">{0}</div>'+
40712                 
40713                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
40714             '</div></div>', v);
40715     
40716     }
40717     
40718     
40719     this.colModel = new Roo.grid.ColumnModel( [
40720         {
40721             xtype: 'ColumnModel',
40722             xns: Roo.grid,
40723             dataIndex : 'weekday0',
40724             header : 'Sunday',
40725             renderer : cellRender
40726         },
40727         {
40728             xtype: 'ColumnModel',
40729             xns: Roo.grid,
40730             dataIndex : 'weekday1',
40731             header : 'Monday',
40732             renderer : cellRender
40733         },
40734         {
40735             xtype: 'ColumnModel',
40736             xns: Roo.grid,
40737             dataIndex : 'weekday2',
40738             header : 'Tuesday',
40739             renderer : cellRender
40740         },
40741         {
40742             xtype: 'ColumnModel',
40743             xns: Roo.grid,
40744             dataIndex : 'weekday3',
40745             header : 'Wednesday',
40746             renderer : cellRender
40747         },
40748         {
40749             xtype: 'ColumnModel',
40750             xns: Roo.grid,
40751             dataIndex : 'weekday4',
40752             header : 'Thursday',
40753             renderer : cellRender
40754         },
40755         {
40756             xtype: 'ColumnModel',
40757             xns: Roo.grid,
40758             dataIndex : 'weekday5',
40759             header : 'Friday',
40760             renderer : cellRender
40761         },
40762         {
40763             xtype: 'ColumnModel',
40764             xns: Roo.grid,
40765             dataIndex : 'weekday6',
40766             header : 'Saturday',
40767             renderer : cellRender
40768         }
40769     ]);
40770     this.cm = this.colModel;
40771     this.cm.xmodule = this.xmodule || false;
40772  
40773         
40774           
40775     //this.selModel = new Roo.grid.CellSelectionModel();
40776     //this.sm = this.selModel;
40777     //this.selModel.init(this);
40778     
40779     
40780     if(this.width){
40781         this.container.setWidth(this.width);
40782     }
40783
40784     if(this.height){
40785         this.container.setHeight(this.height);
40786     }
40787     /** @private */
40788         this.addEvents({
40789         // raw events
40790         /**
40791          * @event click
40792          * The raw click event for the entire grid.
40793          * @param {Roo.EventObject} e
40794          */
40795         "click" : true,
40796         /**
40797          * @event dblclick
40798          * The raw dblclick event for the entire grid.
40799          * @param {Roo.EventObject} e
40800          */
40801         "dblclick" : true,
40802         /**
40803          * @event contextmenu
40804          * The raw contextmenu event for the entire grid.
40805          * @param {Roo.EventObject} e
40806          */
40807         "contextmenu" : true,
40808         /**
40809          * @event mousedown
40810          * The raw mousedown event for the entire grid.
40811          * @param {Roo.EventObject} e
40812          */
40813         "mousedown" : true,
40814         /**
40815          * @event mouseup
40816          * The raw mouseup event for the entire grid.
40817          * @param {Roo.EventObject} e
40818          */
40819         "mouseup" : true,
40820         /**
40821          * @event mouseover
40822          * The raw mouseover event for the entire grid.
40823          * @param {Roo.EventObject} e
40824          */
40825         "mouseover" : true,
40826         /**
40827          * @event mouseout
40828          * The raw mouseout event for the entire grid.
40829          * @param {Roo.EventObject} e
40830          */
40831         "mouseout" : true,
40832         /**
40833          * @event keypress
40834          * The raw keypress event for the entire grid.
40835          * @param {Roo.EventObject} e
40836          */
40837         "keypress" : true,
40838         /**
40839          * @event keydown
40840          * The raw keydown event for the entire grid.
40841          * @param {Roo.EventObject} e
40842          */
40843         "keydown" : true,
40844
40845         // custom events
40846
40847         /**
40848          * @event cellclick
40849          * Fires when a cell is clicked
40850          * @param {Grid} this
40851          * @param {Number} rowIndex
40852          * @param {Number} columnIndex
40853          * @param {Roo.EventObject} e
40854          */
40855         "cellclick" : true,
40856         /**
40857          * @event celldblclick
40858          * Fires when a cell is double clicked
40859          * @param {Grid} this
40860          * @param {Number} rowIndex
40861          * @param {Number} columnIndex
40862          * @param {Roo.EventObject} e
40863          */
40864         "celldblclick" : true,
40865         /**
40866          * @event rowclick
40867          * Fires when a row is clicked
40868          * @param {Grid} this
40869          * @param {Number} rowIndex
40870          * @param {Roo.EventObject} e
40871          */
40872         "rowclick" : true,
40873         /**
40874          * @event rowdblclick
40875          * Fires when a row is double clicked
40876          * @param {Grid} this
40877          * @param {Number} rowIndex
40878          * @param {Roo.EventObject} e
40879          */
40880         "rowdblclick" : true,
40881         /**
40882          * @event headerclick
40883          * Fires when a header is clicked
40884          * @param {Grid} this
40885          * @param {Number} columnIndex
40886          * @param {Roo.EventObject} e
40887          */
40888         "headerclick" : true,
40889         /**
40890          * @event headerdblclick
40891          * Fires when a header cell is double clicked
40892          * @param {Grid} this
40893          * @param {Number} columnIndex
40894          * @param {Roo.EventObject} e
40895          */
40896         "headerdblclick" : true,
40897         /**
40898          * @event rowcontextmenu
40899          * Fires when a row is right clicked
40900          * @param {Grid} this
40901          * @param {Number} rowIndex
40902          * @param {Roo.EventObject} e
40903          */
40904         "rowcontextmenu" : true,
40905         /**
40906          * @event cellcontextmenu
40907          * Fires when a cell is right clicked
40908          * @param {Grid} this
40909          * @param {Number} rowIndex
40910          * @param {Number} cellIndex
40911          * @param {Roo.EventObject} e
40912          */
40913          "cellcontextmenu" : true,
40914         /**
40915          * @event headercontextmenu
40916          * Fires when a header is right clicked
40917          * @param {Grid} this
40918          * @param {Number} columnIndex
40919          * @param {Roo.EventObject} e
40920          */
40921         "headercontextmenu" : true,
40922         /**
40923          * @event bodyscroll
40924          * Fires when the body element is scrolled
40925          * @param {Number} scrollLeft
40926          * @param {Number} scrollTop
40927          */
40928         "bodyscroll" : true,
40929         /**
40930          * @event columnresize
40931          * Fires when the user resizes a column
40932          * @param {Number} columnIndex
40933          * @param {Number} newSize
40934          */
40935         "columnresize" : true,
40936         /**
40937          * @event columnmove
40938          * Fires when the user moves a column
40939          * @param {Number} oldIndex
40940          * @param {Number} newIndex
40941          */
40942         "columnmove" : true,
40943         /**
40944          * @event startdrag
40945          * Fires when row(s) start being dragged
40946          * @param {Grid} this
40947          * @param {Roo.GridDD} dd The drag drop object
40948          * @param {event} e The raw browser event
40949          */
40950         "startdrag" : true,
40951         /**
40952          * @event enddrag
40953          * Fires when a drag operation is complete
40954          * @param {Grid} this
40955          * @param {Roo.GridDD} dd The drag drop object
40956          * @param {event} e The raw browser event
40957          */
40958         "enddrag" : true,
40959         /**
40960          * @event dragdrop
40961          * Fires when dragged row(s) are dropped on a valid DD target
40962          * @param {Grid} this
40963          * @param {Roo.GridDD} dd The drag drop object
40964          * @param {String} targetId The target drag drop object
40965          * @param {event} e The raw browser event
40966          */
40967         "dragdrop" : true,
40968         /**
40969          * @event dragover
40970          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
40971          * @param {Grid} this
40972          * @param {Roo.GridDD} dd The drag drop object
40973          * @param {String} targetId The target drag drop object
40974          * @param {event} e The raw browser event
40975          */
40976         "dragover" : true,
40977         /**
40978          * @event dragenter
40979          *  Fires when the dragged row(s) first cross another DD target while being dragged
40980          * @param {Grid} this
40981          * @param {Roo.GridDD} dd The drag drop object
40982          * @param {String} targetId The target drag drop object
40983          * @param {event} e The raw browser event
40984          */
40985         "dragenter" : true,
40986         /**
40987          * @event dragout
40988          * Fires when the dragged row(s) leave another DD target while being dragged
40989          * @param {Grid} this
40990          * @param {Roo.GridDD} dd The drag drop object
40991          * @param {String} targetId The target drag drop object
40992          * @param {event} e The raw browser event
40993          */
40994         "dragout" : true,
40995         /**
40996          * @event rowclass
40997          * Fires when a row is rendered, so you can change add a style to it.
40998          * @param {GridView} gridview   The grid view
40999          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
41000          */
41001         'rowclass' : true,
41002
41003         /**
41004          * @event render
41005          * Fires when the grid is rendered
41006          * @param {Grid} grid
41007          */
41008         'render' : true,
41009             /**
41010              * @event select
41011              * Fires when a date is selected
41012              * @param {DatePicker} this
41013              * @param {Date} date The selected date
41014              */
41015         'select': true,
41016         /**
41017              * @event monthchange
41018              * Fires when the displayed month changes 
41019              * @param {DatePicker} this
41020              * @param {Date} date The selected month
41021              */
41022         'monthchange': true,
41023         /**
41024              * @event evententer
41025              * Fires when mouse over an event
41026              * @param {Calendar} this
41027              * @param {event} Event
41028              */
41029         'evententer': true,
41030         /**
41031              * @event eventleave
41032              * Fires when the mouse leaves an
41033              * @param {Calendar} this
41034              * @param {event}
41035              */
41036         'eventleave': true,
41037         /**
41038              * @event eventclick
41039              * Fires when the mouse click an
41040              * @param {Calendar} this
41041              * @param {event}
41042              */
41043         'eventclick': true,
41044         /**
41045              * @event eventrender
41046              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
41047              * @param {Calendar} this
41048              * @param {data} data to be modified
41049              */
41050         'eventrender': true
41051         
41052     });
41053
41054     Roo.grid.Grid.superclass.constructor.call(this);
41055     this.on('render', function() {
41056         this.view.el.addClass('x-grid-cal'); 
41057         
41058         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
41059
41060     },this);
41061     
41062     if (!Roo.grid.Calendar.style) {
41063         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
41064             
41065             
41066             '.x-grid-cal .x-grid-col' :  {
41067                 height: 'auto !important',
41068                 'vertical-align': 'top'
41069             },
41070             '.x-grid-cal  .fc-event-hori' : {
41071                 height: '14px'
41072             }
41073              
41074             
41075         }, Roo.id());
41076     }
41077
41078     
41079     
41080 };
41081 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
41082     /**
41083      * @cfg {Store} eventStore The store that loads events.
41084      */
41085     eventStore : 25,
41086
41087      
41088     activeDate : false,
41089     startDay : 0,
41090     autoWidth : true,
41091     monitorWindowResize : false,
41092
41093     
41094     resizeColumns : function() {
41095         var col = (this.view.el.getWidth() / 7) - 3;
41096         // loop through cols, and setWidth
41097         for(var i =0 ; i < 7 ; i++){
41098             this.cm.setColumnWidth(i, col);
41099         }
41100     },
41101      setDate :function(date) {
41102         
41103         Roo.log('setDate?');
41104         
41105         this.resizeColumns();
41106         var vd = this.activeDate;
41107         this.activeDate = date;
41108 //        if(vd && this.el){
41109 //            var t = date.getTime();
41110 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
41111 //                Roo.log('using add remove');
41112 //                
41113 //                this.fireEvent('monthchange', this, date);
41114 //                
41115 //                this.cells.removeClass("fc-state-highlight");
41116 //                this.cells.each(function(c){
41117 //                   if(c.dateValue == t){
41118 //                       c.addClass("fc-state-highlight");
41119 //                       setTimeout(function(){
41120 //                            try{c.dom.firstChild.focus();}catch(e){}
41121 //                       }, 50);
41122 //                       return false;
41123 //                   }
41124 //                   return true;
41125 //                });
41126 //                return;
41127 //            }
41128 //        }
41129         
41130         var days = date.getDaysInMonth();
41131         
41132         var firstOfMonth = date.getFirstDateOfMonth();
41133         var startingPos = firstOfMonth.getDay()-this.startDay;
41134         
41135         if(startingPos < this.startDay){
41136             startingPos += 7;
41137         }
41138         
41139         var pm = date.add(Date.MONTH, -1);
41140         var prevStart = pm.getDaysInMonth()-startingPos;
41141 //        
41142         
41143         
41144         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41145         
41146         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
41147         //this.cells.addClassOnOver('fc-state-hover');
41148         
41149         var cells = this.cells.elements;
41150         var textEls = this.textNodes;
41151         
41152         //Roo.each(cells, function(cell){
41153         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
41154         //});
41155         
41156         days += startingPos;
41157
41158         // convert everything to numbers so it's fast
41159         var day = 86400000;
41160         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
41161         //Roo.log(d);
41162         //Roo.log(pm);
41163         //Roo.log(prevStart);
41164         
41165         var today = new Date().clearTime().getTime();
41166         var sel = date.clearTime().getTime();
41167         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
41168         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
41169         var ddMatch = this.disabledDatesRE;
41170         var ddText = this.disabledDatesText;
41171         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
41172         var ddaysText = this.disabledDaysText;
41173         var format = this.format;
41174         
41175         var setCellClass = function(cal, cell){
41176             
41177             //Roo.log('set Cell Class');
41178             cell.title = "";
41179             var t = d.getTime();
41180             
41181             //Roo.log(d);
41182             
41183             
41184             cell.dateValue = t;
41185             if(t == today){
41186                 cell.className += " fc-today";
41187                 cell.className += " fc-state-highlight";
41188                 cell.title = cal.todayText;
41189             }
41190             if(t == sel){
41191                 // disable highlight in other month..
41192                 cell.className += " fc-state-highlight";
41193                 
41194             }
41195             // disabling
41196             if(t < min) {
41197                 //cell.className = " fc-state-disabled";
41198                 cell.title = cal.minText;
41199                 return;
41200             }
41201             if(t > max) {
41202                 //cell.className = " fc-state-disabled";
41203                 cell.title = cal.maxText;
41204                 return;
41205             }
41206             if(ddays){
41207                 if(ddays.indexOf(d.getDay()) != -1){
41208                     // cell.title = ddaysText;
41209                    // cell.className = " fc-state-disabled";
41210                 }
41211             }
41212             if(ddMatch && format){
41213                 var fvalue = d.dateFormat(format);
41214                 if(ddMatch.test(fvalue)){
41215                     cell.title = ddText.replace("%0", fvalue);
41216                    cell.className = " fc-state-disabled";
41217                 }
41218             }
41219             
41220             if (!cell.initialClassName) {
41221                 cell.initialClassName = cell.dom.className;
41222             }
41223             
41224             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
41225         };
41226
41227         var i = 0;
41228         
41229         for(; i < startingPos; i++) {
41230             cells[i].dayName =  (++prevStart);
41231             Roo.log(textEls[i]);
41232             d.setDate(d.getDate()+1);
41233             
41234             //cells[i].className = "fc-past fc-other-month";
41235             setCellClass(this, cells[i]);
41236         }
41237         
41238         var intDay = 0;
41239         
41240         for(; i < days; i++){
41241             intDay = i - startingPos + 1;
41242             cells[i].dayName =  (intDay);
41243             d.setDate(d.getDate()+1);
41244             
41245             cells[i].className = ''; // "x-date-active";
41246             setCellClass(this, cells[i]);
41247         }
41248         var extraDays = 0;
41249         
41250         for(; i < 42; i++) {
41251             //textEls[i].innerHTML = (++extraDays);
41252             
41253             d.setDate(d.getDate()+1);
41254             cells[i].dayName = (++extraDays);
41255             cells[i].className = "fc-future fc-other-month";
41256             setCellClass(this, cells[i]);
41257         }
41258         
41259         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
41260         
41261         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
41262         
41263         // this will cause all the cells to mis
41264         var rows= [];
41265         var i =0;
41266         for (var r = 0;r < 6;r++) {
41267             for (var c =0;c < 7;c++) {
41268                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
41269             }    
41270         }
41271         
41272         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
41273         for(i=0;i<cells.length;i++) {
41274             
41275             this.cells.elements[i].dayName = cells[i].dayName ;
41276             this.cells.elements[i].className = cells[i].className;
41277             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
41278             this.cells.elements[i].title = cells[i].title ;
41279             this.cells.elements[i].dateValue = cells[i].dateValue ;
41280         }
41281         
41282         
41283         
41284         
41285         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
41286         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
41287         
41288         ////if(totalRows != 6){
41289             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
41290            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
41291        // }
41292         
41293         this.fireEvent('monthchange', this, date);
41294         
41295         
41296     },
41297  /**
41298      * Returns the grid's SelectionModel.
41299      * @return {SelectionModel}
41300      */
41301     getSelectionModel : function(){
41302         if(!this.selModel){
41303             this.selModel = new Roo.grid.CellSelectionModel();
41304         }
41305         return this.selModel;
41306     },
41307
41308     load: function() {
41309         this.eventStore.load()
41310         
41311         
41312         
41313     },
41314     
41315     findCell : function(dt) {
41316         dt = dt.clearTime().getTime();
41317         var ret = false;
41318         this.cells.each(function(c){
41319             //Roo.log("check " +c.dateValue + '?=' + dt);
41320             if(c.dateValue == dt){
41321                 ret = c;
41322                 return false;
41323             }
41324             return true;
41325         });
41326         
41327         return ret;
41328     },
41329     
41330     findCells : function(rec) {
41331         var s = rec.data.start_dt.clone().clearTime().getTime();
41332        // Roo.log(s);
41333         var e= rec.data.end_dt.clone().clearTime().getTime();
41334        // Roo.log(e);
41335         var ret = [];
41336         this.cells.each(function(c){
41337              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
41338             
41339             if(c.dateValue > e){
41340                 return ;
41341             }
41342             if(c.dateValue < s){
41343                 return ;
41344             }
41345             ret.push(c);
41346         });
41347         
41348         return ret;    
41349     },
41350     
41351     findBestRow: function(cells)
41352     {
41353         var ret = 0;
41354         
41355         for (var i =0 ; i < cells.length;i++) {
41356             ret  = Math.max(cells[i].rows || 0,ret);
41357         }
41358         return ret;
41359         
41360     },
41361     
41362     
41363     addItem : function(rec)
41364     {
41365         // look for vertical location slot in
41366         var cells = this.findCells(rec);
41367         
41368         rec.row = this.findBestRow(cells);
41369         
41370         // work out the location.
41371         
41372         var crow = false;
41373         var rows = [];
41374         for(var i =0; i < cells.length; i++) {
41375             if (!crow) {
41376                 crow = {
41377                     start : cells[i],
41378                     end :  cells[i]
41379                 };
41380                 continue;
41381             }
41382             if (crow.start.getY() == cells[i].getY()) {
41383                 // on same row.
41384                 crow.end = cells[i];
41385                 continue;
41386             }
41387             // different row.
41388             rows.push(crow);
41389             crow = {
41390                 start: cells[i],
41391                 end : cells[i]
41392             };
41393             
41394         }
41395         
41396         rows.push(crow);
41397         rec.els = [];
41398         rec.rows = rows;
41399         rec.cells = cells;
41400         for (var i = 0; i < cells.length;i++) {
41401             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
41402             
41403         }
41404         
41405         
41406     },
41407     
41408     clearEvents: function() {
41409         
41410         if (!this.eventStore.getCount()) {
41411             return;
41412         }
41413         // reset number of rows in cells.
41414         Roo.each(this.cells.elements, function(c){
41415             c.rows = 0;
41416         });
41417         
41418         this.eventStore.each(function(e) {
41419             this.clearEvent(e);
41420         },this);
41421         
41422     },
41423     
41424     clearEvent : function(ev)
41425     {
41426         if (ev.els) {
41427             Roo.each(ev.els, function(el) {
41428                 el.un('mouseenter' ,this.onEventEnter, this);
41429                 el.un('mouseleave' ,this.onEventLeave, this);
41430                 el.remove();
41431             },this);
41432             ev.els = [];
41433         }
41434     },
41435     
41436     
41437     renderEvent : function(ev,ctr) {
41438         if (!ctr) {
41439              ctr = this.view.el.select('.fc-event-container',true).first();
41440         }
41441         
41442          
41443         this.clearEvent(ev);
41444             //code
41445        
41446         
41447         
41448         ev.els = [];
41449         var cells = ev.cells;
41450         var rows = ev.rows;
41451         this.fireEvent('eventrender', this, ev);
41452         
41453         for(var i =0; i < rows.length; i++) {
41454             
41455             cls = '';
41456             if (i == 0) {
41457                 cls += ' fc-event-start';
41458             }
41459             if ((i+1) == rows.length) {
41460                 cls += ' fc-event-end';
41461             }
41462             
41463             //Roo.log(ev.data);
41464             // how many rows should it span..
41465             var cg = this.eventTmpl.append(ctr,Roo.apply({
41466                 fccls : cls
41467                 
41468             }, ev.data) , true);
41469             
41470             
41471             cg.on('mouseenter' ,this.onEventEnter, this, ev);
41472             cg.on('mouseleave' ,this.onEventLeave, this, ev);
41473             cg.on('click', this.onEventClick, this, ev);
41474             
41475             ev.els.push(cg);
41476             
41477             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
41478             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
41479             //Roo.log(cg);
41480              
41481             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
41482             cg.setWidth(ebox.right - sbox.x -2);
41483         }
41484     },
41485     
41486     renderEvents: function()
41487     {   
41488         // first make sure there is enough space..
41489         
41490         if (!this.eventTmpl) {
41491             this.eventTmpl = new Roo.Template(
41492                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
41493                     '<div class="fc-event-inner">' +
41494                         '<span class="fc-event-time">{time}</span>' +
41495                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
41496                     '</div>' +
41497                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
41498                 '</div>'
41499             );
41500                 
41501         }
41502                
41503         
41504         
41505         this.cells.each(function(c) {
41506             //Roo.log(c.select('.fc-day-content div',true).first());
41507             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
41508         });
41509         
41510         var ctr = this.view.el.select('.fc-event-container',true).first();
41511         
41512         var cls;
41513         this.eventStore.each(function(ev){
41514             
41515             this.renderEvent(ev);
41516              
41517              
41518         }, this);
41519         this.view.layout();
41520         
41521     },
41522     
41523     onEventEnter: function (e, el,event,d) {
41524         this.fireEvent('evententer', this, el, event);
41525     },
41526     
41527     onEventLeave: function (e, el,event,d) {
41528         this.fireEvent('eventleave', this, el, event);
41529     },
41530     
41531     onEventClick: function (e, el,event,d) {
41532         this.fireEvent('eventclick', this, el, event);
41533     },
41534     
41535     onMonthChange: function () {
41536         this.store.load();
41537     },
41538     
41539     onLoad: function () {
41540         
41541         //Roo.log('calendar onload');
41542 //         
41543         if(this.eventStore.getCount() > 0){
41544             
41545            
41546             
41547             this.eventStore.each(function(d){
41548                 
41549                 
41550                 // FIXME..
41551                 var add =   d.data;
41552                 if (typeof(add.end_dt) == 'undefined')  {
41553                     Roo.log("Missing End time in calendar data: ");
41554                     Roo.log(d);
41555                     return;
41556                 }
41557                 if (typeof(add.start_dt) == 'undefined')  {
41558                     Roo.log("Missing Start time in calendar data: ");
41559                     Roo.log(d);
41560                     return;
41561                 }
41562                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
41563                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
41564                 add.id = add.id || d.id;
41565                 add.title = add.title || '??';
41566                 
41567                 this.addItem(d);
41568                 
41569              
41570             },this);
41571         }
41572         
41573         this.renderEvents();
41574     }
41575     
41576
41577 });
41578 /*
41579  grid : {
41580                 xtype: 'Grid',
41581                 xns: Roo.grid,
41582                 listeners : {
41583                     render : function ()
41584                     {
41585                         _this.grid = this;
41586                         
41587                         if (!this.view.el.hasClass('course-timesheet')) {
41588                             this.view.el.addClass('course-timesheet');
41589                         }
41590                         if (this.tsStyle) {
41591                             this.ds.load({});
41592                             return; 
41593                         }
41594                         Roo.log('width');
41595                         Roo.log(_this.grid.view.el.getWidth());
41596                         
41597                         
41598                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
41599                             '.course-timesheet .x-grid-row' : {
41600                                 height: '80px'
41601                             },
41602                             '.x-grid-row td' : {
41603                                 'vertical-align' : 0
41604                             },
41605                             '.course-edit-link' : {
41606                                 'color' : 'blue',
41607                                 'text-overflow' : 'ellipsis',
41608                                 'overflow' : 'hidden',
41609                                 'white-space' : 'nowrap',
41610                                 'cursor' : 'pointer'
41611                             },
41612                             '.sub-link' : {
41613                                 'color' : 'green'
41614                             },
41615                             '.de-act-sup-link' : {
41616                                 'color' : 'purple',
41617                                 'text-decoration' : 'line-through'
41618                             },
41619                             '.de-act-link' : {
41620                                 'color' : 'red',
41621                                 'text-decoration' : 'line-through'
41622                             },
41623                             '.course-timesheet .course-highlight' : {
41624                                 'border-top-style': 'dashed !important',
41625                                 'border-bottom-bottom': 'dashed !important'
41626                             },
41627                             '.course-timesheet .course-item' : {
41628                                 'font-family'   : 'tahoma, arial, helvetica',
41629                                 'font-size'     : '11px',
41630                                 'overflow'      : 'hidden',
41631                                 'padding-left'  : '10px',
41632                                 'padding-right' : '10px',
41633                                 'padding-top' : '10px' 
41634                             }
41635                             
41636                         }, Roo.id());
41637                                 this.ds.load({});
41638                     }
41639                 },
41640                 autoWidth : true,
41641                 monitorWindowResize : false,
41642                 cellrenderer : function(v,x,r)
41643                 {
41644                     return v;
41645                 },
41646                 sm : {
41647                     xtype: 'CellSelectionModel',
41648                     xns: Roo.grid
41649                 },
41650                 dataSource : {
41651                     xtype: 'Store',
41652                     xns: Roo.data,
41653                     listeners : {
41654                         beforeload : function (_self, options)
41655                         {
41656                             options.params = options.params || {};
41657                             options.params._month = _this.monthField.getValue();
41658                             options.params.limit = 9999;
41659                             options.params['sort'] = 'when_dt';    
41660                             options.params['dir'] = 'ASC';    
41661                             this.proxy.loadResponse = this.loadResponse;
41662                             Roo.log("load?");
41663                             //this.addColumns();
41664                         },
41665                         load : function (_self, records, options)
41666                         {
41667                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
41668                                 // if you click on the translation.. you can edit it...
41669                                 var el = Roo.get(this);
41670                                 var id = el.dom.getAttribute('data-id');
41671                                 var d = el.dom.getAttribute('data-date');
41672                                 var t = el.dom.getAttribute('data-time');
41673                                 //var id = this.child('span').dom.textContent;
41674                                 
41675                                 //Roo.log(this);
41676                                 Pman.Dialog.CourseCalendar.show({
41677                                     id : id,
41678                                     when_d : d,
41679                                     when_t : t,
41680                                     productitem_active : id ? 1 : 0
41681                                 }, function() {
41682                                     _this.grid.ds.load({});
41683                                 });
41684                            
41685                            });
41686                            
41687                            _this.panel.fireEvent('resize', [ '', '' ]);
41688                         }
41689                     },
41690                     loadResponse : function(o, success, response){
41691                             // this is overridden on before load..
41692                             
41693                             Roo.log("our code?");       
41694                             //Roo.log(success);
41695                             //Roo.log(response)
41696                             delete this.activeRequest;
41697                             if(!success){
41698                                 this.fireEvent("loadexception", this, o, response);
41699                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41700                                 return;
41701                             }
41702                             var result;
41703                             try {
41704                                 result = o.reader.read(response);
41705                             }catch(e){
41706                                 Roo.log("load exception?");
41707                                 this.fireEvent("loadexception", this, o, response, e);
41708                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
41709                                 return;
41710                             }
41711                             Roo.log("ready...");        
41712                             // loop through result.records;
41713                             // and set this.tdate[date] = [] << array of records..
41714                             _this.tdata  = {};
41715                             Roo.each(result.records, function(r){
41716                                 //Roo.log(r.data);
41717                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
41718                                     _this.tdata[r.data.when_dt.format('j')] = [];
41719                                 }
41720                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
41721                             });
41722                             
41723                             //Roo.log(_this.tdata);
41724                             
41725                             result.records = [];
41726                             result.totalRecords = 6;
41727                     
41728                             // let's generate some duumy records for the rows.
41729                             //var st = _this.dateField.getValue();
41730                             
41731                             // work out monday..
41732                             //st = st.add(Date.DAY, -1 * st.format('w'));
41733                             
41734                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41735                             
41736                             var firstOfMonth = date.getFirstDayOfMonth();
41737                             var days = date.getDaysInMonth();
41738                             var d = 1;
41739                             var firstAdded = false;
41740                             for (var i = 0; i < result.totalRecords ; i++) {
41741                                 //var d= st.add(Date.DAY, i);
41742                                 var row = {};
41743                                 var added = 0;
41744                                 for(var w = 0 ; w < 7 ; w++){
41745                                     if(!firstAdded && firstOfMonth != w){
41746                                         continue;
41747                                     }
41748                                     if(d > days){
41749                                         continue;
41750                                     }
41751                                     firstAdded = true;
41752                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
41753                                     row['weekday'+w] = String.format(
41754                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
41755                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
41756                                                     d,
41757                                                     date.format('Y-m-')+dd
41758                                                 );
41759                                     added++;
41760                                     if(typeof(_this.tdata[d]) != 'undefined'){
41761                                         Roo.each(_this.tdata[d], function(r){
41762                                             var is_sub = '';
41763                                             var deactive = '';
41764                                             var id = r.id;
41765                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
41766                                             if(r.parent_id*1>0){
41767                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
41768                                                 id = r.parent_id;
41769                                             }
41770                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
41771                                                 deactive = 'de-act-link';
41772                                             }
41773                                             
41774                                             row['weekday'+w] += String.format(
41775                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
41776                                                     id, //0
41777                                                     r.product_id_name, //1
41778                                                     r.when_dt.format('h:ia'), //2
41779                                                     is_sub, //3
41780                                                     deactive, //4
41781                                                     desc // 5
41782                                             );
41783                                         });
41784                                     }
41785                                     d++;
41786                                 }
41787                                 
41788                                 // only do this if something added..
41789                                 if(added > 0){ 
41790                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
41791                                 }
41792                                 
41793                                 
41794                                 // push it twice. (second one with an hour..
41795                                 
41796                             }
41797                             //Roo.log(result);
41798                             this.fireEvent("load", this, o, o.request.arg);
41799                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
41800                         },
41801                     sortInfo : {field: 'when_dt', direction : 'ASC' },
41802                     proxy : {
41803                         xtype: 'HttpProxy',
41804                         xns: Roo.data,
41805                         method : 'GET',
41806                         url : baseURL + '/Roo/Shop_course.php'
41807                     },
41808                     reader : {
41809                         xtype: 'JsonReader',
41810                         xns: Roo.data,
41811                         id : 'id',
41812                         fields : [
41813                             {
41814                                 'name': 'id',
41815                                 'type': 'int'
41816                             },
41817                             {
41818                                 'name': 'when_dt',
41819                                 'type': 'string'
41820                             },
41821                             {
41822                                 'name': 'end_dt',
41823                                 'type': 'string'
41824                             },
41825                             {
41826                                 'name': 'parent_id',
41827                                 'type': 'int'
41828                             },
41829                             {
41830                                 'name': 'product_id',
41831                                 'type': 'int'
41832                             },
41833                             {
41834                                 'name': 'productitem_id',
41835                                 'type': 'int'
41836                             },
41837                             {
41838                                 'name': 'guid',
41839                                 'type': 'int'
41840                             }
41841                         ]
41842                     }
41843                 },
41844                 toolbar : {
41845                     xtype: 'Toolbar',
41846                     xns: Roo,
41847                     items : [
41848                         {
41849                             xtype: 'Button',
41850                             xns: Roo.Toolbar,
41851                             listeners : {
41852                                 click : function (_self, e)
41853                                 {
41854                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41855                                     sd.setMonth(sd.getMonth()-1);
41856                                     _this.monthField.setValue(sd.format('Y-m-d'));
41857                                     _this.grid.ds.load({});
41858                                 }
41859                             },
41860                             text : "Back"
41861                         },
41862                         {
41863                             xtype: 'Separator',
41864                             xns: Roo.Toolbar
41865                         },
41866                         {
41867                             xtype: 'MonthField',
41868                             xns: Roo.form,
41869                             listeners : {
41870                                 render : function (_self)
41871                                 {
41872                                     _this.monthField = _self;
41873                                    // _this.monthField.set  today
41874                                 },
41875                                 select : function (combo, date)
41876                                 {
41877                                     _this.grid.ds.load({});
41878                                 }
41879                             },
41880                             value : (function() { return new Date(); })()
41881                         },
41882                         {
41883                             xtype: 'Separator',
41884                             xns: Roo.Toolbar
41885                         },
41886                         {
41887                             xtype: 'TextItem',
41888                             xns: Roo.Toolbar,
41889                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
41890                         },
41891                         {
41892                             xtype: 'Fill',
41893                             xns: Roo.Toolbar
41894                         },
41895                         {
41896                             xtype: 'Button',
41897                             xns: Roo.Toolbar,
41898                             listeners : {
41899                                 click : function (_self, e)
41900                                 {
41901                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
41902                                     sd.setMonth(sd.getMonth()+1);
41903                                     _this.monthField.setValue(sd.format('Y-m-d'));
41904                                     _this.grid.ds.load({});
41905                                 }
41906                             },
41907                             text : "Next"
41908                         }
41909                     ]
41910                 },
41911                  
41912             }
41913         };
41914         
41915         *//*
41916  * Based on:
41917  * Ext JS Library 1.1.1
41918  * Copyright(c) 2006-2007, Ext JS, LLC.
41919  *
41920  * Originally Released Under LGPL - original licence link has changed is not relivant.
41921  *
41922  * Fork - LGPL
41923  * <script type="text/javascript">
41924  */
41925  
41926 /**
41927  * @class Roo.LoadMask
41928  * A simple utility class for generically masking elements while loading data.  If the element being masked has
41929  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
41930  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
41931  * element's UpdateManager load indicator and will be destroyed after the initial load.
41932  * @constructor
41933  * Create a new LoadMask
41934  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
41935  * @param {Object} config The config object
41936  */
41937 Roo.LoadMask = function(el, config){
41938     this.el = Roo.get(el);
41939     Roo.apply(this, config);
41940     if(this.store){
41941         this.store.on('beforeload', this.onBeforeLoad, this);
41942         this.store.on('load', this.onLoad, this);
41943         this.store.on('loadexception', this.onLoadException, this);
41944         this.removeMask = false;
41945     }else{
41946         var um = this.el.getUpdateManager();
41947         um.showLoadIndicator = false; // disable the default indicator
41948         um.on('beforeupdate', this.onBeforeLoad, this);
41949         um.on('update', this.onLoad, this);
41950         um.on('failure', this.onLoad, this);
41951         this.removeMask = true;
41952     }
41953 };
41954
41955 Roo.LoadMask.prototype = {
41956     /**
41957      * @cfg {Boolean} removeMask
41958      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
41959      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
41960      */
41961     /**
41962      * @cfg {String} msg
41963      * The text to display in a centered loading message box (defaults to 'Loading...')
41964      */
41965     msg : 'Loading...',
41966     /**
41967      * @cfg {String} msgCls
41968      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
41969      */
41970     msgCls : 'x-mask-loading',
41971
41972     /**
41973      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
41974      * @type Boolean
41975      */
41976     disabled: false,
41977
41978     /**
41979      * Disables the mask to prevent it from being displayed
41980      */
41981     disable : function(){
41982        this.disabled = true;
41983     },
41984
41985     /**
41986      * Enables the mask so that it can be displayed
41987      */
41988     enable : function(){
41989         this.disabled = false;
41990     },
41991     
41992     onLoadException : function()
41993     {
41994         Roo.log(arguments);
41995         
41996         if (typeof(arguments[3]) != 'undefined') {
41997             Roo.MessageBox.alert("Error loading",arguments[3]);
41998         } 
41999         /*
42000         try {
42001             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42002                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42003             }   
42004         } catch(e) {
42005             
42006         }
42007         */
42008     
42009         
42010         
42011         this.el.unmask(this.removeMask);
42012     },
42013     // private
42014     onLoad : function()
42015     {
42016         this.el.unmask(this.removeMask);
42017     },
42018
42019     // private
42020     onBeforeLoad : function(){
42021         if(!this.disabled){
42022             this.el.mask(this.msg, this.msgCls);
42023         }
42024     },
42025
42026     // private
42027     destroy : function(){
42028         if(this.store){
42029             this.store.un('beforeload', this.onBeforeLoad, this);
42030             this.store.un('load', this.onLoad, this);
42031             this.store.un('loadexception', this.onLoadException, this);
42032         }else{
42033             var um = this.el.getUpdateManager();
42034             um.un('beforeupdate', this.onBeforeLoad, this);
42035             um.un('update', this.onLoad, this);
42036             um.un('failure', this.onLoad, this);
42037         }
42038     }
42039 };/*
42040  * Based on:
42041  * Ext JS Library 1.1.1
42042  * Copyright(c) 2006-2007, Ext JS, LLC.
42043  *
42044  * Originally Released Under LGPL - original licence link has changed is not relivant.
42045  *
42046  * Fork - LGPL
42047  * <script type="text/javascript">
42048  */
42049
42050
42051 /**
42052  * @class Roo.XTemplate
42053  * @extends Roo.Template
42054  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
42055 <pre><code>
42056 var t = new Roo.XTemplate(
42057         '&lt;select name="{name}"&gt;',
42058                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
42059         '&lt;/select&gt;'
42060 );
42061  
42062 // then append, applying the master template values
42063  </code></pre>
42064  *
42065  * Supported features:
42066  *
42067  *  Tags:
42068
42069 <pre><code>
42070       {a_variable} - output encoded.
42071       {a_variable.format:("Y-m-d")} - call a method on the variable
42072       {a_variable:raw} - unencoded output
42073       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
42074       {a_variable:this.method_on_template(...)} - call a method on the template object.
42075  
42076 </code></pre>
42077  *  The tpl tag:
42078 <pre><code>
42079         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
42080         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
42081         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
42082         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
42083   
42084         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
42085         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
42086 </code></pre>
42087  *      
42088  */
42089 Roo.XTemplate = function()
42090 {
42091     Roo.XTemplate.superclass.constructor.apply(this, arguments);
42092     if (this.html) {
42093         this.compile();
42094     }
42095 };
42096
42097
42098 Roo.extend(Roo.XTemplate, Roo.Template, {
42099
42100     /**
42101      * The various sub templates
42102      */
42103     tpls : false,
42104     /**
42105      *
42106      * basic tag replacing syntax
42107      * WORD:WORD()
42108      *
42109      * // you can fake an object call by doing this
42110      *  x.t:(test,tesT) 
42111      * 
42112      */
42113     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
42114
42115     /**
42116      * compile the template
42117      *
42118      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
42119      *
42120      */
42121     compile: function()
42122     {
42123         var s = this.html;
42124      
42125         s = ['<tpl>', s, '</tpl>'].join('');
42126     
42127         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
42128             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
42129             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
42130             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
42131             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
42132             m,
42133             id     = 0,
42134             tpls   = [];
42135     
42136         while(true == !!(m = s.match(re))){
42137             var forMatch   = m[0].match(nameRe),
42138                 ifMatch   = m[0].match(ifRe),
42139                 execMatch   = m[0].match(execRe),
42140                 namedMatch   = m[0].match(namedRe),
42141                 
42142                 exp  = null, 
42143                 fn   = null,
42144                 exec = null,
42145                 name = forMatch && forMatch[1] ? forMatch[1] : '';
42146                 
42147             if (ifMatch) {
42148                 // if - puts fn into test..
42149                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
42150                 if(exp){
42151                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
42152                 }
42153             }
42154             
42155             if (execMatch) {
42156                 // exec - calls a function... returns empty if true is  returned.
42157                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
42158                 if(exp){
42159                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
42160                 }
42161             }
42162             
42163             
42164             if (name) {
42165                 // for = 
42166                 switch(name){
42167                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
42168                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
42169                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
42170                 }
42171             }
42172             var uid = namedMatch ? namedMatch[1] : id;
42173             
42174             
42175             tpls.push({
42176                 id:     namedMatch ? namedMatch[1] : id,
42177                 target: name,
42178                 exec:   exec,
42179                 test:   fn,
42180                 body:   m[1] || ''
42181             });
42182             if (namedMatch) {
42183                 s = s.replace(m[0], '');
42184             } else { 
42185                 s = s.replace(m[0], '{xtpl'+ id + '}');
42186             }
42187             ++id;
42188         }
42189         this.tpls = [];
42190         for(var i = tpls.length-1; i >= 0; --i){
42191             this.compileTpl(tpls[i]);
42192             this.tpls[tpls[i].id] = tpls[i];
42193         }
42194         this.master = tpls[tpls.length-1];
42195         return this;
42196     },
42197     /**
42198      * same as applyTemplate, except it's done to one of the subTemplates
42199      * when using named templates, you can do:
42200      *
42201      * var str = pl.applySubTemplate('your-name', values);
42202      *
42203      * 
42204      * @param {Number} id of the template
42205      * @param {Object} values to apply to template
42206      * @param {Object} parent (normaly the instance of this object)
42207      */
42208     applySubTemplate : function(id, values, parent)
42209     {
42210         
42211         
42212         var t = this.tpls[id];
42213         
42214         
42215         try { 
42216             if(t.test && !t.test.call(this, values, parent)){
42217                 return '';
42218             }
42219         } catch(e) {
42220             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
42221             Roo.log(e.toString());
42222             Roo.log(t.test);
42223             return ''
42224         }
42225         try { 
42226             
42227             if(t.exec && t.exec.call(this, values, parent)){
42228                 return '';
42229             }
42230         } catch(e) {
42231             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
42232             Roo.log(e.toString());
42233             Roo.log(t.exec);
42234             return ''
42235         }
42236         try {
42237             var vs = t.target ? t.target.call(this, values, parent) : values;
42238             parent = t.target ? values : parent;
42239             if(t.target && vs instanceof Array){
42240                 var buf = [];
42241                 for(var i = 0, len = vs.length; i < len; i++){
42242                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
42243                 }
42244                 return buf.join('');
42245             }
42246             return t.compiled.call(this, vs, parent);
42247         } catch (e) {
42248             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
42249             Roo.log(e.toString());
42250             Roo.log(t.compiled);
42251             return '';
42252         }
42253     },
42254
42255     compileTpl : function(tpl)
42256     {
42257         var fm = Roo.util.Format;
42258         var useF = this.disableFormats !== true;
42259         var sep = Roo.isGecko ? "+" : ",";
42260         var undef = function(str) {
42261             Roo.log("Property not found :"  + str);
42262             return '';
42263         };
42264         
42265         var fn = function(m, name, format, args)
42266         {
42267             //Roo.log(arguments);
42268             args = args ? args.replace(/\\'/g,"'") : args;
42269             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
42270             if (typeof(format) == 'undefined') {
42271                 format= 'htmlEncode';
42272             }
42273             if (format == 'raw' ) {
42274                 format = false;
42275             }
42276             
42277             if(name.substr(0, 4) == 'xtpl'){
42278                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
42279             }
42280             
42281             // build an array of options to determine if value is undefined..
42282             
42283             // basically get 'xxxx.yyyy' then do
42284             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
42285             //    (function () { Roo.log("Property not found"); return ''; })() :
42286             //    ......
42287             
42288             var udef_ar = [];
42289             var lookfor = '';
42290             Roo.each(name.split('.'), function(st) {
42291                 lookfor += (lookfor.length ? '.': '') + st;
42292                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
42293             });
42294             
42295             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
42296             
42297             
42298             if(format && useF){
42299                 
42300                 args = args ? ',' + args : "";
42301                  
42302                 if(format.substr(0, 5) != "this."){
42303                     format = "fm." + format + '(';
42304                 }else{
42305                     format = 'this.call("'+ format.substr(5) + '", ';
42306                     args = ", values";
42307                 }
42308                 
42309                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
42310             }
42311              
42312             if (args.length) {
42313                 // called with xxyx.yuu:(test,test)
42314                 // change to ()
42315                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
42316             }
42317             // raw.. - :raw modifier..
42318             return "'"+ sep + udef_st  + name + ")"+sep+"'";
42319             
42320         };
42321         var body;
42322         // branched to use + in gecko and [].join() in others
42323         if(Roo.isGecko){
42324             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
42325                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
42326                     "';};};";
42327         }else{
42328             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
42329             body.push(tpl.body.replace(/(\r\n|\n)/g,
42330                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
42331             body.push("'].join('');};};");
42332             body = body.join('');
42333         }
42334         
42335         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
42336        
42337         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
42338         eval(body);
42339         
42340         return this;
42341     },
42342
42343     applyTemplate : function(values){
42344         return this.master.compiled.call(this, values, {});
42345         //var s = this.subs;
42346     },
42347
42348     apply : function(){
42349         return this.applyTemplate.apply(this, arguments);
42350     }
42351
42352  });
42353
42354 Roo.XTemplate.from = function(el){
42355     el = Roo.getDom(el);
42356     return new Roo.XTemplate(el.value || el.innerHTML);
42357 };